changeset 1653:a6e6d3500c13 trunk

[svn] - revert back to r2216
author nenolod
date Thu, 07 Sep 2006 22:26:54 -0700
parents 62c5bff8a05b
children e1667bdf1272
files ChangeLog audacious/Makefile audacious/controlsocket.c audacious/equalizer.c audacious/equalizer.h audacious/genevent.c audacious/genevent.h audacious/input.c audacious/interface.h audacious/main.c audacious/main.h audacious/mainwin.c audacious/mainwin.h audacious/playback.c audacious/playlist.c audacious/pluginenum.c audacious/ui_playlist.c audacious/ui_playlist.h audacious/util.c audacious/util.h audacious/visualization.c audacious/widgets/Makefile audacious/widgets/eq_graph.c audacious/widgets/eq_graph.h audacious/widgets/eq_slider.c audacious/widgets/eq_slider.h audacious/widgets/hslider.c audacious/widgets/hslider.h audacious/widgets/menurow.c audacious/widgets/menurow.h audacious/widgets/monostereo.c audacious/widgets/monostereo.h audacious/widgets/number.c audacious/widgets/number.h audacious/widgets/pbutton.c audacious/widgets/pbutton.h audacious/widgets/playlist_list.c audacious/widgets/playlist_list.h audacious/widgets/playlist_slider.c audacious/widgets/playlist_slider.h audacious/widgets/playstatus.c audacious/widgets/playstatus.h audacious/widgets/sbutton.c audacious/widgets/sbutton.h audacious/widgets/skin.c audacious/widgets/skin.h audacious/widgets/svis.c audacious/widgets/svis.h audacious/widgets/tbutton.c audacious/widgets/tbutton.h audacious/widgets/textbox.c audacious/widgets/textbox.h audacious/widgets/vis.c audacious/widgets/vis.h audacious/widgets/widget.c audacious/widgets/widget.h audacious/widgets/widgetcore.h
diffstat 57 files changed, 14252 insertions(+), 211 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Thu Sep 07 22:00:35 2006 -0700
+++ b/ChangeLog	Thu Sep 07 22:26:54 2006 -0700
@@ -1,3 +1,12 @@
+2006-09-08 05:00:35 +0000  William Pitcock <nenolod@nenolod.net>
+  revision [2235]
+  - more hammering
+  
+
+  Changes:        Modified:
+  +4 -4           trunk/audacious/main.c  
+
+
 2006-09-08 04:51:36 +0000  William Pitcock <nenolod@nenolod.net>
   revision [2233]
   - hammer a little more
--- a/audacious/Makefile	Thu Sep 07 22:00:35 2006 -0700
+++ b/audacious/Makefile	Thu Sep 07 22:26:54 2006 -0700
@@ -2,7 +2,7 @@
 include ../mk/init.mk
 include ../mk/objective.mk
 
-SUBDIRS = glade images
+SUBDIRS = widgets glade images
 
 beepincludedir = $(includedir)/audacious
 
@@ -16,7 +16,8 @@
 	../sqlite/libsqlite.a	\
 	$(CHARDET_LIBS) \
 	$(GTK_LIBS)      \
-	$(LIBGLADE_LIBS)
+	$(LIBGLADE_LIBS) \
+	./widgets/libwidgets.a
 
 CFLAGS += \
 	$(GTK_CFLAGS)      \
@@ -50,9 +51,12 @@
 	playback.c \
 	main.c \
 	logger.c \
+	mainwin.c \
 	skinwin.c \
 	prefswin.c \
 	ui_fileinfo.c \
+	ui_playlist.c \
+	equalizer.c \
 	glade.c \
 	hints.c \
 	about.c credits.c \
--- a/audacious/controlsocket.c	Thu Sep 07 22:00:35 2006 -0700
+++ b/audacious/controlsocket.c	Thu Sep 07 22:26:54 2006 -0700
@@ -44,12 +44,14 @@
 #include <arpa/inet.h>
 
 #include "main.h"
+#include "equalizer.h"
+#include "mainwin.h"
 #include "input.h"
 #include "playback.h"
 #include "playlist.h"
+#include "ui_playlist.h"
 #include "prefswin.h"
 #include "libaudacious/beepctrl.h"
-#include "interface.h"
 
 #define CTRLSOCKET_BACKLOG        100
 #define CTRLSOCKET_TIMEOUT        100000
@@ -432,6 +434,10 @@
             ctrl_write_gint(pkt->fd, b);
             ctrl_ack_packet(pkt);
             break;
+        case CMD_GET_SKIN:
+            ctrl_write_string(pkt->fd, bmp_active_skin->path);
+            ctrl_ack_packet(pkt);
+            break;
         case CMD_GET_PLAYLIST_FILE:
             if (pkt->data) {
                 gchar *filename;
@@ -750,7 +756,7 @@
             mainwin_quit_cb();
             break;
 	case CMD_ACTIVATE:
-	    current_interface->present();
+	    gtk_window_present(GTK_WINDOW(mainwin));
 	    break;
         default:
             g_message("Unknown socket command received");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/equalizer.c	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,1753 @@
+/*  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; 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "equalizer.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+#include "platform/smartinclude.h"
+#include "widgets/widgetcore.h"
+#include "dock.h"
+#include "hints.h"
+#include "input.h"
+#include "main.h"
+#include "playlist.h"
+#include "ui_playlist.h"
+#include "util.h"
+#include "output.h"
+
+#include "libaudacious/rcfile.h"
+#include "libaudacious/vfs.h"
+
+#include "images/audacious_eq.xpm"
+
+enum PresetViewCols {
+    PRESET_VIEW_COL_NAME,
+    PRESET_VIEW_N_COLS
+};
+
+enum {
+    EQUALIZER_PRESETS_LOAD_PRESET,
+    EQUALIZER_PRESETS_LOAD_AUTOPRESET,
+    EQUALIZER_PRESETS_LOAD_DEFAULT,
+    EQUALIZER_PRESETS_LOAD_ZERO,
+    EQUALIZER_PRESETS_LOAD_FROM_FILE,
+    EQUALIZER_PRESETS_LOAD_FROM_WINAMPFILE,
+    EQUALIZER_PRESETS_IMPORT_WINAMPFILE,
+    EQUALIZER_PRESETS_SAVE_PRESET,
+    EQUALIZER_PRESETS_SAVE_AUTOPRESET,
+    EQUALIZER_PRESETS_SAVE_DEFAULT,
+    EQUALIZER_PRESETS_SAVE_TO_FILE,
+    EQUALIZER_PRESETS_SAVE_TO_WINAMPFILE,
+    EQUALIZER_PRESETS_DELETE_PRESET,
+    EQUALIZER_PRESETS_DELETE_AUTOPRESET
+};
+
+
+struct _EqualizerPreset {
+    gchar *name;
+    gfloat preamp, bands[10];
+};
+
+typedef struct _EqualizerPreset EqualizerPreset;
+
+
+GtkWidget *equalizerwin;
+
+static GtkWidget *equalizerwin_load_window = NULL;
+static GtkWidget *equalizerwin_load_auto_window = NULL;
+static GtkWidget *equalizerwin_save_window = NULL;
+static GtkWidget *equalizerwin_save_entry;
+static GtkWidget *equalizerwin_save_auto_window = NULL;
+static GtkWidget *equalizerwin_save_auto_entry;
+static GtkWidget *equalizerwin_delete_window = NULL;
+static GtkWidget *equalizerwin_delete_auto_window = NULL;
+
+static GdkPixmap *equalizerwin_bg;
+static GdkGC *equalizerwin_gc;
+
+static GList *equalizerwin_wlist = NULL;
+
+static GtkAccelGroup *equalizerwin_accel;
+
+static TButton *equalizerwin_on, *equalizerwin_auto;
+
+static PButton *equalizerwin_presets, *equalizerwin_shade;
+PButton *equalizerwin_close;
+static EqGraph *equalizerwin_graph;
+static EqSlider *equalizerwin_preamp, *equalizerwin_bands[10];
+static HSlider *equalizerwin_volume, *equalizerwin_balance;
+
+static GtkItemFactory *equalizerwin_presets_menu;
+
+static GList *equalizer_presets = NULL, *equalizer_auto_presets = NULL;
+
+
+static void equalizerwin_presets_menu_cb(gpointer cb_data, guint action,
+                                         GtkWidget * w);
+
+static GtkItemFactoryEntry equalizerwin_presets_menu_entries[] = {
+    {N_("/Load"), NULL, NULL, 0, "<Branch>", NULL},
+    {N_("/Load/Preset"), NULL, equalizerwin_presets_menu_cb,
+     EQUALIZER_PRESETS_LOAD_PRESET, "<Item>", NULL},
+    {N_("/Load/Auto-load preset"), NULL, equalizerwin_presets_menu_cb,
+     EQUALIZER_PRESETS_LOAD_AUTOPRESET, "<Item>", NULL},
+    {N_("/Load/Default"), NULL, equalizerwin_presets_menu_cb,
+     EQUALIZER_PRESETS_LOAD_DEFAULT, "<Item>", NULL},
+    {"/Load/-", NULL, NULL, 0, "<Separator>", NULL},
+    {N_("/Load/Zero"), NULL, equalizerwin_presets_menu_cb,
+     EQUALIZER_PRESETS_LOAD_ZERO, "<Item>", NULL},
+    {"/Load/-", NULL, NULL, 0, "<Separator>", NULL},
+    {N_("/Load/From file"), NULL, equalizerwin_presets_menu_cb,
+     EQUALIZER_PRESETS_LOAD_FROM_FILE, "<Item>", NULL},
+    {N_("/Load/From WinAMP EQF file"), NULL, equalizerwin_presets_menu_cb,
+     EQUALIZER_PRESETS_LOAD_FROM_WINAMPFILE, "<Item>", NULL},
+    {N_("/Import"), NULL, NULL, 0, "<Branch>", NULL},
+    {N_("/Import/WinAMP Presets"), NULL, equalizerwin_presets_menu_cb,
+     EQUALIZER_PRESETS_IMPORT_WINAMPFILE, "<Item>", NULL},
+    {N_("/Save"), NULL, NULL, 0, "<Branch>", NULL},
+    {N_("/Save/Preset"), NULL, equalizerwin_presets_menu_cb,
+     EQUALIZER_PRESETS_SAVE_PRESET, "<Item>", NULL},
+    {N_("/Save/Auto-load preset"), NULL, equalizerwin_presets_menu_cb,
+     EQUALIZER_PRESETS_SAVE_AUTOPRESET, "<Item>", NULL},
+    {N_("/Save/Default"), NULL, equalizerwin_presets_menu_cb,
+     EQUALIZER_PRESETS_SAVE_DEFAULT, "<Item>", NULL},
+    {"/Save/-", NULL, NULL, 0, "<Separator>", NULL},
+    {N_("/Save/To file"), NULL, equalizerwin_presets_menu_cb,
+     EQUALIZER_PRESETS_SAVE_TO_FILE, "<Item>", NULL},
+    {N_("/Save/To WinAMP EQF file"), NULL, equalizerwin_presets_menu_cb,
+     EQUALIZER_PRESETS_SAVE_TO_WINAMPFILE, "<Item>", NULL},
+    {N_("/Delete"), NULL, NULL, 0, "<Branch>", NULL},
+    {N_("/Delete/Preset"), NULL, equalizerwin_presets_menu_cb,
+     EQUALIZER_PRESETS_DELETE_PRESET, "<Item>", NULL},
+    {N_("/Delete/Auto-load preset"), NULL, equalizerwin_presets_menu_cb,
+     EQUALIZER_PRESETS_DELETE_AUTOPRESET, "<Item>", NULL},
+};
+
+static gint equalizerwin_presets_menu_entries_num =
+    G_N_ELEMENTS(equalizerwin_presets_menu_entries);
+
+
+EqualizerPreset *
+equalizer_preset_new(const gchar * name)
+{
+    EqualizerPreset *preset = g_new0(EqualizerPreset, 1);
+    preset->name = g_strdup(name);
+    return preset;
+}
+
+void
+equalizer_preset_free(EqualizerPreset * preset)
+{
+    if (!preset)
+        return;
+
+    g_free(preset->name);
+    g_free(preset);
+}
+
+
+static void
+equalizerwin_set_shape_mask(void)
+{
+    GdkBitmap *mask;
+
+    if (cfg.show_wm_decorations)
+        return;
+
+    mask = skin_get_mask(bmp_active_skin, SKIN_MASK_EQ + cfg.equalizer_shaded);
+    gtk_widget_shape_combine_mask(equalizerwin, mask, 0, 0);
+}
+
+
+void
+equalizerwin_set_shade_menu_cb(gboolean shaded)
+{
+    cfg.equalizer_shaded = shaded;
+
+    equalizerwin_set_shape_mask();
+
+    if (shaded) {
+        dock_shade(dock_window_list, GTK_WINDOW(equalizerwin), 14);
+        pbutton_set_button_data(equalizerwin_shade, -1, 3, -1, 47);
+        pbutton_set_skin_index1(equalizerwin_shade, SKIN_EQ_EX);
+        pbutton_set_button_data(equalizerwin_close, 11, 38, 11, 47);
+        pbutton_set_skin_index(equalizerwin_close, SKIN_EQ_EX);
+        widget_show(WIDGET(equalizerwin_volume));
+        widget_show(WIDGET(equalizerwin_balance));
+    }
+    else {
+        dock_shade(dock_window_list, GTK_WINDOW(equalizerwin), 116);
+        pbutton_set_button_data(equalizerwin_shade, -1, 137, -1, 38);
+        pbutton_set_skin_index1(equalizerwin_shade, SKIN_EQMAIN);
+        pbutton_set_button_data(equalizerwin_close, 0, 116, 0, 125);
+        pbutton_set_skin_index(equalizerwin_close, SKIN_EQMAIN);
+        widget_hide(WIDGET(equalizerwin_volume));
+        widget_hide(WIDGET(equalizerwin_balance));
+    }
+
+    draw_equalizer_window(TRUE);
+}
+
+static void
+equalizerwin_set_shade(gboolean shaded)
+{
+    GtkWidget *widget;
+    widget = gtk_item_factory_get_widget(mainwin_view_menu,
+                                         "/Roll up Equalizer");
+    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), shaded);
+}
+
+static void
+equalizerwin_shade_toggle(void)
+{
+    equalizerwin_set_shade(!cfg.equalizer_shaded);
+}
+
+static void
+equalizerwin_raise(void)
+{
+    if (cfg.equalizer_visible)
+        gtk_window_present(GTK_WINDOW(equalizerwin));
+}
+
+void
+equalizerwin_eq_changed(void)
+{
+    gint i;
+
+    cfg.equalizer_preamp = eqslider_get_position(equalizerwin_preamp);
+    for (i = 0; i < 10; i++)
+        cfg.equalizer_bands[i] = eqslider_get_position(equalizerwin_bands[i]);
+    /* um .. i think we need both of these for xmms compatibility ..
+       not sure. -larne */
+    input_set_eq(cfg.equalizer_active, cfg.equalizer_preamp,
+                 cfg.equalizer_bands);
+    output_set_eq(cfg.equalizer_active, cfg.equalizer_preamp,
+                  cfg.equalizer_bands);
+
+    widget_draw(WIDGET(equalizerwin_graph));
+}
+
+static void
+equalizerwin_on_pushed(gboolean toggled)
+{
+    cfg.equalizer_active = toggled;
+    equalizerwin_eq_changed();
+}
+
+static void
+equalizerwin_presets_pushed(void)
+{
+    GdkModifierType modmask;
+    gint x, y;
+
+    gdk_window_get_pointer(NULL, &x, &y, &modmask);
+    util_item_factory_popup(equalizerwin_presets_menu, x, y, 1,
+                            GDK_CURRENT_TIME);
+}
+
+static void
+equalizerwin_auto_pushed(gboolean toggled)
+{
+    cfg.equalizer_autoload = toggled;
+}
+
+void
+draw_equalizer_window(gboolean force)
+{
+    gboolean redraw;
+
+    widget_list_lock(equalizerwin_wlist);
+
+    if (force) {
+        skin_draw_pixmap(bmp_active_skin, equalizerwin_bg, equalizerwin_gc,
+                         SKIN_EQMAIN, 0, 0, 0, 0, 275, 116);
+        if (gtk_window_has_toplevel_focus(GTK_WINDOW(equalizerwin)) ||
+            !cfg.dim_titlebar) {
+            if (!cfg.equalizer_shaded)
+                skin_draw_pixmap(bmp_active_skin, equalizerwin_bg,
+                                 equalizerwin_gc, SKIN_EQMAIN, 0, 134, 0,
+                                 0, 275, 14);
+            else
+                skin_draw_pixmap(bmp_active_skin, equalizerwin_bg,
+                                 equalizerwin_gc, SKIN_EQ_EX, 0, 0, 0, 0,
+                                 275, 14);
+        }
+        else {
+            if (!cfg.equalizer_shaded)
+                skin_draw_pixmap(bmp_active_skin, equalizerwin_bg,
+                                 equalizerwin_gc, SKIN_EQMAIN, 0, 149, 0,
+                                 0, 275, 14);
+            else
+                skin_draw_pixmap(bmp_active_skin, equalizerwin_bg,
+                                 equalizerwin_gc, SKIN_EQ_EX, 0, 15, 0, 0,
+                                 275, 14);
+
+        }
+    }
+
+    widget_list_draw(equalizerwin_wlist, &redraw, force);
+
+    if (force || redraw) {
+        widget_list_clear_redraw(equalizerwin_wlist);
+        gdk_window_clear(equalizerwin->window);
+        gdk_flush();
+    }
+
+    widget_list_unlock(equalizerwin_wlist);
+}
+
+static gboolean
+inside_sensitive_widgets(gint x, gint y)
+{
+    return (widget_contains(WIDGET(equalizerwin_on), x, y) ||
+            widget_contains(WIDGET(equalizerwin_auto), x, y) ||
+            widget_contains(WIDGET(equalizerwin_presets), x, y) ||
+            widget_contains(WIDGET(equalizerwin_close), x, y) ||
+            widget_contains(WIDGET(equalizerwin_shade), x, y) ||
+            widget_contains(WIDGET(equalizerwin_preamp), x, y) ||
+            widget_contains(WIDGET(equalizerwin_bands[0]), x, y) ||
+            widget_contains(WIDGET(equalizerwin_bands[1]), x, y) ||
+            widget_contains(WIDGET(equalizerwin_bands[2]), x, y) ||
+            widget_contains(WIDGET(equalizerwin_bands[3]), x, y) ||
+            widget_contains(WIDGET(equalizerwin_bands[4]), x, y) ||
+            widget_contains(WIDGET(equalizerwin_bands[5]), x, y) ||
+            widget_contains(WIDGET(equalizerwin_bands[6]), x, y) ||
+            widget_contains(WIDGET(equalizerwin_bands[7]), x, y) ||
+            widget_contains(WIDGET(equalizerwin_bands[8]), x, y) ||
+            widget_contains(WIDGET(equalizerwin_bands[9]), x, y) ||
+            widget_contains(WIDGET(equalizerwin_volume), x, y) ||
+            widget_contains(WIDGET(equalizerwin_balance), x, y));
+}
+
+gboolean
+equalizerwin_press(GtkWidget * widget, GdkEventButton * event,
+                   gpointer callback_data)
+{
+    gint mx, my;
+    gboolean grab = TRUE;
+
+    mx = event->x;
+    my = event->y;
+
+    if (event->button == 1 && event->type == GDK_BUTTON_PRESS &&
+        ((cfg.equalizer_shaded || event->y < 14) &&
+         !inside_sensitive_widgets(event->x, event->y))) {
+        if (0 && hint_move_resize_available()) {
+            hint_move_resize(equalizerwin, event->x_root,
+                             event->y_root, TRUE);
+            grab = FALSE;
+        }
+        else {
+            equalizerwin_raise();
+            dock_move_press(dock_window_list, GTK_WINDOW(equalizerwin), event,
+                            FALSE);
+        }
+    }
+    else if (event->button == 1 && event->type == GDK_2BUTTON_PRESS
+             && event->y < 14) {
+        equalizerwin_set_shade(!cfg.equalizer_shaded);
+        if (dock_is_moving(GTK_WINDOW(equalizerwin)))
+            dock_move_release(GTK_WINDOW(equalizerwin));
+    }
+    else if (event->button == 3 &&
+             !(widget_contains(WIDGET(equalizerwin_on), event->x, event->y) ||
+               widget_contains(WIDGET(equalizerwin_auto), event->x, event->y))) {
+        /*
+         * Pop up the main menu a few pixels down to avoid
+         * anything to be selected initially.
+         */
+        util_item_factory_popup(mainwin_general_menu, event->x_root,
+                                event->y_root + 2, 3, event->time);
+        grab = FALSE;
+    }
+    else {
+        handle_press_cb(equalizerwin_wlist, widget, event);
+        draw_equalizer_window(FALSE);
+    }
+    if (grab)
+        gdk_pointer_grab(GDK_WINDOW(equalizerwin->window), FALSE,
+                         GDK_BUTTON_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
+                         NULL, NULL, GDK_CURRENT_TIME);
+
+    return FALSE;
+}
+
+static void
+equalizerwin_scroll(GtkWidget * widget, GdkEventScroll * event, gpointer data)
+{
+    handle_scroll_cb(equalizerwin_wlist, widget, event);
+    draw_equalizer_window(FALSE);
+}
+
+static gboolean
+equalizerwin_motion(GtkWidget * widget,
+                    GdkEventMotion * event, gpointer callback_data)
+{
+    GdkEvent *gevent;
+
+    if (dock_is_moving(GTK_WINDOW(equalizerwin)))
+    {
+        dock_move_motion(GTK_WINDOW(equalizerwin), event);
+    }
+    else 
+    {
+        handle_motion_cb(equalizerwin_wlist, widget, event);
+        draw_equalizer_window(FALSE);
+    }
+
+    gdk_flush();
+
+    while ((gevent = gdk_event_get()) != NULL) gdk_event_free(gevent);
+
+    return FALSE;
+}
+
+static gboolean
+equalizerwin_release(GtkWidget * widget,
+                     GdkEventButton * event, gpointer callback_data)
+{
+    gdk_pointer_ungrab(GDK_CURRENT_TIME);
+    gdk_flush();
+
+    if (dock_is_moving(GTK_WINDOW(equalizerwin))) {
+        dock_move_release(GTK_WINDOW(equalizerwin));
+    }
+    else {
+        handle_release_cb(equalizerwin_wlist, widget, event);
+        draw_equalizer_window(FALSE);
+    }
+
+    return FALSE;
+}
+
+static gboolean
+equalizerwin_focus_in(GtkWidget * widget,
+                      GdkEvent * event,
+                      gpointer data)
+{
+    equalizerwin_close->pb_allow_draw = TRUE;
+    equalizerwin_shade->pb_allow_draw = TRUE;
+    draw_equalizer_window(TRUE);
+    return TRUE;
+}
+
+static gboolean
+equalizerwin_focus_out(GtkWidget * widget,
+                       GdkEventButton * event,
+                       gpointer data)
+{
+    equalizerwin_close->pb_allow_draw = FALSE;
+    equalizerwin_shade->pb_allow_draw = FALSE;
+    draw_equalizer_window(TRUE);
+    return TRUE;
+}
+
+static gboolean
+equalizerwin_keypress(GtkWidget * widget,
+                      GdkEventKey * event,
+                      gpointer data)
+{
+    if (!cfg.equalizer_shaded) {
+        gtk_widget_event(mainwin, (GdkEvent *) event);
+        return TRUE;
+    }
+
+    switch (event->keyval) {
+    case GDK_Left:
+    case GDK_KP_Left:
+        mainwin_set_balance_diff(-4);
+        break;
+    case GDK_Right:
+    case GDK_KP_Right:
+        mainwin_set_balance_diff(4);
+        break;
+    default:
+        gtk_widget_event(mainwin, (GdkEvent *) event);
+        break;
+    }
+
+    return FALSE;
+}
+
+static gboolean
+equalizerwin_configure(GtkWidget * window,
+                       GdkEventConfigure * event,
+                       gpointer data)
+{
+    if (!GTK_WIDGET_VISIBLE(window))
+        return FALSE;
+
+    cfg.equalizer_x = event->x;
+    cfg.equalizer_y = event->y;
+    return FALSE;
+}
+
+static void
+equalizerwin_set_back_pixmap(void)
+{
+    gdk_window_set_back_pixmap(equalizerwin->window, equalizerwin_bg, 0);
+    gdk_window_clear(equalizerwin->window);
+}
+
+static void
+equalizerwin_close_cb(void)
+{
+    equalizerwin_show(FALSE);
+}
+
+static gboolean
+equalizerwin_delete(GtkWidget * widget,
+                    gpointer data)
+{
+    equalizerwin_show(FALSE);
+    return TRUE;
+}
+
+static GList *
+equalizerwin_read_presets(const gchar * basename)
+{
+    gchar *filename, *name;
+    RcFile *rcfile;
+    GList *list = NULL;
+    gint i, p = 0;
+    EqualizerPreset *preset;
+
+    filename = g_build_filename(bmp_paths[BMP_PATH_USER_DIR], basename, NULL);
+
+    if ((rcfile = bmp_rcfile_open(filename)) == NULL) {
+        g_free(filename);
+        return NULL;
+    }
+
+    g_free(filename);
+
+    for (;;) {
+        gchar section[21];
+
+        g_snprintf(section, sizeof(section), "Preset%d", p++);
+        if (bmp_rcfile_read_string(rcfile, "Presets", section, &name)) {
+            preset = g_new0(EqualizerPreset, 1);
+            preset->name = name;
+            bmp_rcfile_read_float(rcfile, name, "Preamp", &preset->preamp);
+            for (i = 0; i < 10; i++) {
+                gchar band[7];
+                g_snprintf(band, sizeof(band), "Band%d", i);
+                bmp_rcfile_read_float(rcfile, name, band, &preset->bands[i]);
+            }
+            list = g_list_prepend(list, preset);
+        }
+        else
+            break;
+    }
+    list = g_list_reverse(list);
+    bmp_rcfile_free(rcfile);
+    return list;
+}
+
+gint
+equalizerwin_volume_frame_cb(gint pos)
+{
+    if (equalizerwin_volume) {
+        if (pos < 32)
+            equalizerwin_volume->hs_knob_nx =
+                equalizerwin_volume->hs_knob_px = 1;
+        else if (pos < 63)
+            equalizerwin_volume->hs_knob_nx =
+                equalizerwin_volume->hs_knob_px = 4;
+        else
+            equalizerwin_volume->hs_knob_nx =
+                equalizerwin_volume->hs_knob_px = 7;
+    }
+    return 1;
+}
+
+static void
+equalizerwin_volume_motion_cb(gint pos)
+{
+    gint v = (gint) rint(pos * 100 / 94.0);
+    mainwin_adjust_volume_motion(v);
+    mainwin_set_volume_slider(v);
+}
+
+static void
+equalizerwin_volume_release_cb(gint pos)
+{
+    mainwin_adjust_volume_release();
+}
+
+static gint
+equalizerwin_balance_frame_cb(gint pos)
+{
+    if (equalizerwin_balance) {
+        if (pos < 13)
+            equalizerwin_balance->hs_knob_nx =
+                equalizerwin_balance->hs_knob_px = 11;
+        else if (pos < 26)
+            equalizerwin_balance->hs_knob_nx =
+                equalizerwin_balance->hs_knob_px = 14;
+        else
+            equalizerwin_balance->hs_knob_nx =
+                equalizerwin_balance->hs_knob_px = 17;
+    }
+
+    return 1;
+}
+
+static void
+equalizerwin_balance_motion_cb(gint pos)
+{
+    gint b;
+    pos = MIN(pos, 38);         /* The skin uses a even number of pixels
+                                   for the balance-slider *sigh* */
+    b = (gint) rint((pos - 19) * 100 / 19.0);
+    mainwin_adjust_balance_motion(b);
+    mainwin_set_balance_slider(b);
+}
+
+static void
+equalizerwin_balance_release_cb(gint pos)
+{
+    mainwin_adjust_balance_release();
+}
+
+void
+equalizerwin_set_balance_slider(gint percent)
+{
+    hslider_set_position(equalizerwin_balance,
+                         (gint) rint((percent * 19 / 100.0) + 19));
+}
+
+void
+equalizerwin_set_volume_slider(gint percent)
+{
+    hslider_set_position(equalizerwin_volume,
+                         (gint) rint(percent * 94 / 100.0));
+}
+
+static void
+equalizerwin_create_widgets(void)
+{
+    gint i;
+
+    equalizerwin_on =
+        create_tbutton(&equalizerwin_wlist, equalizerwin_bg,
+                       equalizerwin_gc, 14, 18, 25, 12, 10, 119, 128, 119,
+                       69, 119, 187, 119, equalizerwin_on_pushed,
+                       SKIN_EQMAIN);
+    tbutton_set_toggled(equalizerwin_on, cfg.equalizer_active);
+    equalizerwin_auto =
+        create_tbutton(&equalizerwin_wlist, equalizerwin_bg,
+                       equalizerwin_gc, 39, 18, 33, 12, 35, 119, 153, 119,
+                       94, 119, 212, 119, equalizerwin_auto_pushed,
+                       SKIN_EQMAIN);
+    tbutton_set_toggled(equalizerwin_auto, cfg.equalizer_autoload);
+    equalizerwin_presets =
+        create_pbutton(&equalizerwin_wlist, equalizerwin_bg,
+                       equalizerwin_gc, 217, 18, 44, 12, 224, 164, 224,
+                       176, equalizerwin_presets_pushed, SKIN_EQMAIN);
+    equalizerwin_close =
+        create_pbutton(&equalizerwin_wlist, equalizerwin_bg,
+                       equalizerwin_gc, 264, 3, 9, 9, 0, 116, 0, 125,
+                       equalizerwin_close_cb, SKIN_EQMAIN);
+    equalizerwin_close->pb_allow_draw = FALSE;
+
+    equalizerwin_shade =
+        create_pbutton_ex(&equalizerwin_wlist, equalizerwin_bg,
+                          equalizerwin_gc, 254, 3, 9, 9, 254, 137, 1, 38,
+                          equalizerwin_shade_toggle, NULL, SKIN_EQMAIN, SKIN_EQ_EX);
+    equalizerwin_shade->pb_allow_draw = FALSE;
+
+    equalizerwin_graph =
+        create_eqgraph(&equalizerwin_wlist, equalizerwin_bg,
+                       equalizerwin_gc, 86, 17);
+    equalizerwin_preamp =
+        create_eqslider(&equalizerwin_wlist, equalizerwin_bg,
+                        equalizerwin_gc, 21, 38);
+    eqslider_set_position(equalizerwin_preamp, cfg.equalizer_preamp);
+    for (i = 0; i < 10; i++) {
+        equalizerwin_bands[i] =
+            create_eqslider(&equalizerwin_wlist, equalizerwin_bg,
+                            equalizerwin_gc, 78 + (i * 18), 38);
+        eqslider_set_position(equalizerwin_bands[i], cfg.equalizer_bands[i]);
+    }
+
+    equalizerwin_volume =
+        create_hslider(&equalizerwin_wlist, equalizerwin_bg,
+                       equalizerwin_gc, 61, 4, 97, 8, 1, 30, 1, 30, 3, 7,
+                       4, 61, 0, 94, equalizerwin_volume_frame_cb,
+                       equalizerwin_volume_motion_cb,
+                       equalizerwin_volume_release_cb, SKIN_EQ_EX);
+    equalizerwin_balance =
+        create_hslider(&equalizerwin_wlist, equalizerwin_bg,
+                       equalizerwin_gc, 164, 4, 42, 8, 11, 30, 11, 30, 3,
+                       7, 4, 164, 0, 39, equalizerwin_balance_frame_cb,
+                       equalizerwin_balance_motion_cb,
+                       equalizerwin_balance_release_cb, SKIN_EQ_EX);
+
+    if (!cfg.equalizer_shaded) {
+        widget_hide(WIDGET(equalizerwin_volume));
+        widget_hide(WIDGET(equalizerwin_balance));
+    }
+    else {
+        pbutton_set_button_data(equalizerwin_shade, -1, 3, -1, 47);
+        pbutton_set_skin_index1(equalizerwin_shade, SKIN_EQ_EX);
+        pbutton_set_button_data(equalizerwin_close, 11, 38, 11, 47);
+        pbutton_set_skin_index(equalizerwin_close, SKIN_EQ_EX);
+    }
+}
+
+
+static void
+equalizerwin_create_window(void)
+{
+    GdkPixbuf *icon;
+    gint width, height;
+
+    equalizerwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_title(GTK_WINDOW(equalizerwin), _("Audacious Equalizer"));
+    gtk_window_set_wmclass(GTK_WINDOW(equalizerwin), "equalizer", "Audacious");
+    gtk_window_set_role(GTK_WINDOW(equalizerwin), "equalizer");
+
+    width = 275;
+    height = cfg.equalizer_shaded ? 14 : 116;
+
+    gtk_window_set_default_size(GTK_WINDOW(equalizerwin), width, height);
+    gtk_window_set_resizable(GTK_WINDOW(equalizerwin), FALSE);
+
+    dock_window_list = dock_window_set_decorated(dock_window_list,
+                                                 GTK_WINDOW(equalizerwin),
+                                                 cfg.show_wm_decorations);
+
+    /* this will hide only mainwin. it's annoying! yaz */
+    gtk_window_set_transient_for(GTK_WINDOW(equalizerwin),
+                                 GTK_WINDOW(mainwin));
+    gtk_window_set_skip_taskbar_hint(GTK_WINDOW(equalizerwin), TRUE);
+
+    icon = gdk_pixbuf_new_from_xpm_data((const gchar **) bmp_eq_icon);
+    gtk_window_set_icon(GTK_WINDOW(equalizerwin), icon);
+    g_object_unref(icon);
+
+    gtk_widget_set_app_paintable(equalizerwin, TRUE);
+
+    if (cfg.equalizer_x != -1 && cfg.save_window_position)
+        gtk_window_move(GTK_WINDOW(equalizerwin),
+                        cfg.equalizer_x, cfg.equalizer_y);
+
+    gtk_widget_set_events(equalizerwin,
+                          GDK_FOCUS_CHANGE_MASK | GDK_BUTTON_MOTION_MASK |
+                          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+                          GDK_VISIBILITY_NOTIFY_MASK);
+    gtk_widget_realize(equalizerwin);
+
+    util_set_cursor(equalizerwin);
+
+    g_signal_connect(equalizerwin, "delete_event",
+                     G_CALLBACK(equalizerwin_delete), NULL);
+    g_signal_connect(equalizerwin, "button_press_event",
+                     G_CALLBACK(equalizerwin_press), NULL);
+    g_signal_connect(equalizerwin, "button_release_event",
+                     G_CALLBACK(equalizerwin_release), NULL);
+    g_signal_connect(equalizerwin, "motion_notify_event",
+                     G_CALLBACK(equalizerwin_motion), NULL);
+    g_signal_connect_after(equalizerwin, "focus_in_event",
+                           G_CALLBACK(equalizerwin_focus_in), NULL);
+    g_signal_connect_after(equalizerwin, "focus_out_event",
+                           G_CALLBACK(equalizerwin_focus_out), NULL);
+    g_signal_connect(equalizerwin, "configure_event",
+                     G_CALLBACK(equalizerwin_configure), NULL);
+    g_signal_connect(equalizerwin, "style_set",
+                     G_CALLBACK(equalizerwin_set_back_pixmap), NULL);
+    g_signal_connect(equalizerwin, "key_press_event",
+                     G_CALLBACK(equalizerwin_keypress), NULL);
+    g_signal_connect(equalizerwin, "scroll_event",
+                     G_CALLBACK(equalizerwin_scroll), NULL);
+}
+
+void
+equalizerwin_create_popup_menus(void)
+{
+    equalizerwin_accel = gtk_accel_group_new();
+    gtk_window_add_accel_group(GTK_WINDOW(equalizerwin), equalizerwin_accel);
+
+    equalizerwin_presets_menu = create_menu(equalizerwin_presets_menu_entries,
+                                            equalizerwin_presets_menu_entries_num,
+                                            NULL);
+}
+
+void
+equalizerwin_create(void)
+{
+    equalizer_presets = equalizerwin_read_presets("eq.preset");
+    equalizer_auto_presets = equalizerwin_read_presets("eq.auto_preset");
+
+    equalizerwin_create_window();
+    equalizerwin_create_popup_menus();
+
+    equalizerwin_gc = gdk_gc_new(equalizerwin->window);
+    equalizerwin_bg = gdk_pixmap_new(equalizerwin->window, 275, 116, -1);
+
+    equalizerwin_create_widgets();
+
+    equalizerwin_set_back_pixmap();
+    gdk_window_set_back_pixmap(equalizerwin->window, equalizerwin_bg, 0);
+}
+
+
+void
+equalizerwin_show(gboolean show)
+{
+    GtkWidget *item = gtk_item_factory_get_widget(mainwin_view_menu,
+                                                  "/Show Equalizer");
+    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), show);
+}
+
+void
+equalizerwin_real_show(void)
+{
+    /*
+     * This function should only be called from the
+     * main menu signal handler
+     */
+
+    gint x, y;
+
+    gtk_window_get_position(GTK_WINDOW(equalizerwin), &x, &y);
+    gtk_window_move(GTK_WINDOW(equalizerwin), x, y);
+    gtk_widget_set_size_request(equalizerwin, 275,
+                                (cfg.equalizer_shaded ? 14 : 116));
+    gdk_flush();
+    draw_equalizer_window(TRUE);
+    cfg.equalizer_visible = TRUE;
+    tbutton_set_toggled(mainwin_eq, TRUE);
+
+    gtk_widget_show(equalizerwin);
+}
+
+void
+equalizerwin_real_hide(void)
+{
+    /*
+     * This function should only be called from the
+     * main menu signal handler
+     */
+    gtk_widget_hide(equalizerwin);
+    cfg.equalizer_visible = FALSE;
+    tbutton_set_toggled(mainwin_eq, FALSE);
+}
+
+static EqualizerPreset *
+equalizerwin_find_preset(GList * list, const gchar * name)
+{
+    GList *node = list;
+    EqualizerPreset *preset;
+
+    while (node) {
+        preset = node->data;
+        if (!strcasecmp(preset->name, name))
+            return preset;
+        node = g_list_next(node);
+    }
+    return NULL;
+}
+
+static void
+equalizerwin_write_preset_file(GList * list, const gchar * basename)
+{
+    gchar *filename, *tmp;
+    gint i, p;
+    EqualizerPreset *preset;
+    RcFile *rcfile;
+    GList *node;
+
+    rcfile = bmp_rcfile_new();
+    p = 0;
+    for (node = list; node; node = g_list_next(node)) {
+        preset = node->data;
+        tmp = g_strdup_printf("Preset%d", p++);
+        bmp_rcfile_write_string(rcfile, "Presets", tmp, preset->name);
+        g_free(tmp);
+        bmp_rcfile_write_float(rcfile, preset->name, "Preamp",
+                               preset->preamp);
+        for (i = 0; i < 10; i++) {
+            tmp = g_strdup_printf("Band%d\n", i);
+            bmp_rcfile_write_float(rcfile, preset->name, tmp,
+                                   preset->bands[i]);
+            g_free(tmp);
+        }
+    }
+
+    filename = g_build_filename(bmp_paths[BMP_PATH_USER_DIR], basename, NULL);
+    bmp_rcfile_write(rcfile, filename);
+    bmp_rcfile_free(rcfile);
+    g_free(filename);
+}
+
+static gboolean
+equalizerwin_load_preset(GList * list, const gchar * name)
+{
+    EqualizerPreset *preset;
+    gint i;
+
+    if ((preset = equalizerwin_find_preset(list, name)) != NULL) {
+        eqslider_set_position(equalizerwin_preamp, preset->preamp);
+        for (i = 0; i < 10; i++)
+            eqslider_set_position(equalizerwin_bands[i], preset->bands[i]);
+        equalizerwin_eq_changed();
+        return TRUE;
+    }
+    return FALSE;
+}
+
+static GList *
+equalizerwin_save_preset(GList * list, const gchar * name,
+                         const gchar * filename)
+{
+    gint i;
+    EqualizerPreset *preset;
+
+    if (!(preset = equalizerwin_find_preset(list, name))) {
+        preset = g_new0(EqualizerPreset, 1);
+        preset->name = g_strdup(name);
+        list = g_list_append(list, preset);
+    }
+
+    preset->preamp = eqslider_get_position(equalizerwin_preamp);
+    for (i = 0; i < 10; i++)
+        preset->bands[i] = eqslider_get_position(equalizerwin_bands[i]);
+
+    equalizerwin_write_preset_file(list, filename);
+
+    return list;
+}
+
+static GList *
+equalizerwin_delete_preset(GList * list, gchar * name, gchar * filename)
+{
+    EqualizerPreset *preset;
+    GList *node;
+
+    if (!(preset = equalizerwin_find_preset(list, name)))
+        return list;
+
+    if (!(node = g_list_find(list, preset)))
+        return list;
+
+    list = g_list_remove_link(list, node);
+    equalizer_preset_free(preset);
+    g_list_free_1(node);
+
+    equalizerwin_write_preset_file(list, filename);
+
+    return list;
+}
+
+
+static GList *
+import_winamp_eqf(VFSFile * file)
+{
+    gchar header[31];
+    gchar name[257];
+    gchar bands[11];
+    gint i = 0;
+    GList *list = NULL;
+    EqualizerPreset *preset;
+
+    vfs_fread(header, 1, 31, file);
+    if (!strncmp(header, "Winamp EQ library file v1.1", 27)) {
+        while (vfs_fread(name, 1, 257, file)) {
+            preset = equalizer_preset_new(name);
+            preset->preamp = 20.0 - ((bands[10] * 40.0) / 64);
+
+            vfs_fread(bands, 1, 11, file);
+
+            for (i = 0; i < 10; i++)
+                preset->bands[i] = 20.0 - ((bands[i] * 40.0) / 64);
+
+            list = g_list_prepend(list, preset);
+        }
+    }
+
+    list = g_list_reverse(list);
+    return list;
+}
+
+static void
+equalizerwin_read_winamp_eqf(VFSFile * file)
+{
+    gchar header[31];
+    guchar bands[11];
+    gint i;
+
+    vfs_fread(header, 1, 31, file);
+
+    if (!strncmp(header, "Winamp EQ library file v1.1", 27)) {
+        /* Skip name */
+        if (vfs_fseek(file, 257, SEEK_CUR) == -1)
+            return;
+
+        if (vfs_fread(bands, 1, 11, file) != 11)
+            return;
+
+        eqslider_set_position(equalizerwin_preamp,
+                              20.0 - ((bands[10] * 40.0) / 63.0));
+
+        for (i = 0; i < 10; i++)
+            eqslider_set_position(equalizerwin_bands[i],
+                                  20.0 - ((bands[i] * 40.0) / 64.0));
+    }
+
+    equalizerwin_eq_changed();
+}
+
+static void
+equalizerwin_read_bmp_preset(RcFile * rcfile)
+{
+    gfloat val;
+    gint i;
+
+    if (bmp_rcfile_read_float(rcfile, "Equalizer preset", "Preamp", &val))
+        eqslider_set_position(equalizerwin_preamp, val);
+    for (i = 0; i < 10; i++) {
+        gchar tmp[7];
+        g_snprintf(tmp, sizeof(tmp), "Band%d", i);
+        if (bmp_rcfile_read_float(rcfile, "Equalizer preset", tmp, &val))
+            eqslider_set_position(equalizerwin_bands[i], val);
+    }
+    equalizerwin_eq_changed();
+}
+
+static void
+equalizerwin_save_ok(GtkWidget * widget, gpointer data)
+{
+    const gchar *text;
+
+    text = gtk_entry_get_text(GTK_ENTRY(equalizerwin_save_entry));
+    if (strlen(text) != 0)
+        equalizer_presets =
+            equalizerwin_save_preset(equalizer_presets, text, "eq.preset");
+    gtk_widget_destroy(equalizerwin_save_window);
+}
+
+static void
+equalizerwin_save_select(GtkCList * clist, gint row,
+                         gint column, GdkEventButton * event, gpointer data)
+{
+    gchar *text;
+
+    gtk_clist_get_text(clist, row, 0, &text);
+
+    gtk_entry_set_text(GTK_ENTRY(equalizerwin_save_entry), text);
+    if (event->type == GDK_2BUTTON_PRESS)
+        equalizerwin_save_ok(NULL, NULL);
+
+}
+
+static void
+equalizerwin_load_ok(GtkWidget * widget, gpointer data)
+{
+    gchar *text;
+    GtkCList *clist = GTK_CLIST(data);
+
+    if (clist && clist->selection) {
+        gtk_clist_get_text(clist, GPOINTER_TO_INT(clist->selection->data),
+                           0, &text);
+        equalizerwin_load_preset(equalizer_presets, text);
+    }
+    gtk_widget_destroy(equalizerwin_load_window);
+}
+
+static void
+equalizerwin_load_apply(GtkWidget * widget, gpointer data)
+{
+    gchar *text;
+    GtkCList *clist = GTK_CLIST(data);
+
+    if (clist && clist->selection) {
+        gtk_clist_get_text(clist, GPOINTER_TO_INT(clist->selection->data),
+                           0, &text);
+        equalizerwin_load_preset(equalizer_presets, text);
+    }
+}
+
+
+static void
+equalizerwin_load_select(GtkCList * widget, gint row,
+                         gint column, GdkEventButton * event, gpointer data)
+{
+    if (event->type == GDK_2BUTTON_PRESS)
+        equalizerwin_load_ok(NULL, widget);
+}
+
+static void
+equalizerwin_delete_delete(GtkWidget * widget, gpointer data)
+{
+    gchar *text;
+    GList *list, *next;
+    GtkCList *clist = GTK_CLIST(data);
+
+    g_return_if_fail(clist != NULL);
+
+    list = clist->selection;
+    gtk_clist_freeze(clist);
+    while (list) {
+        next = g_list_next(list);
+        gtk_clist_get_text(clist, GPOINTER_TO_INT(list->data), 0, &text);
+        equalizer_auto_presets =
+            equalizerwin_delete_preset(equalizer_presets, text, "eq.preset");
+        gtk_clist_remove(clist, GPOINTER_TO_INT(list->data));
+        list = next;
+    }
+    gtk_clist_thaw(clist);
+}
+
+static void
+equalizerwin_save_auto_ok(GtkWidget * widget, gpointer data)
+{
+    const gchar *text;
+
+    text = gtk_entry_get_text(GTK_ENTRY(equalizerwin_save_auto_entry));
+    if (strlen(text) != 0)
+        equalizer_auto_presets =
+            equalizerwin_save_preset(equalizer_auto_presets, text,
+                                     "eq.auto_preset");
+    gtk_widget_destroy(equalizerwin_save_auto_window);
+}
+
+static void
+equalizerwin_save_auto_select(GtkCList * clist, gint row,
+                              gint column,
+                              GdkEventButton * event, gpointer data)
+{
+    gchar *text;
+
+    gtk_clist_get_text(clist, row, 0, &text);
+
+    gtk_entry_set_text(GTK_ENTRY(equalizerwin_save_auto_entry), text);
+    if (event->type == GDK_2BUTTON_PRESS)
+        equalizerwin_save_auto_ok(NULL, NULL);
+
+}
+
+static void
+equalizerwin_load_auto_ok(GtkWidget * widget, gpointer data)
+{
+    gchar *text;
+    GtkCList *clist = GTK_CLIST(data);
+
+    if (clist && clist->selection) {
+        gtk_clist_get_text(clist, GPOINTER_TO_INT(clist->selection->data),
+                           0, &text);
+        equalizerwin_load_preset(equalizer_auto_presets, text);
+    }
+    gtk_widget_destroy(equalizerwin_load_auto_window);
+}
+
+static void
+equalizerwin_load_auto_select(GtkWidget * widget, gint row,
+                              gint column,
+                              GdkEventButton * event, gpointer data)
+{
+    if (event->type == GDK_2BUTTON_PRESS)
+        equalizerwin_load_auto_ok(NULL, widget);
+}
+
+static void
+equalizerwin_delete_auto_delete(GtkWidget * widget, gpointer data)
+{
+    gchar *text;
+    GList *list, *next;
+    GtkCList *clist = GTK_CLIST(data);
+
+    g_return_if_fail(clist != NULL);
+
+    list = clist->selection;
+    gtk_clist_freeze(clist);
+    while (list) {
+        next = g_list_next(list);
+        gtk_clist_get_text(clist, GPOINTER_TO_INT(list->data), 0, &text);
+        equalizer_auto_presets =
+            equalizerwin_delete_preset(equalizer_auto_presets, text,
+                                       "eq.auto_preset");
+        gtk_clist_remove(clist, GPOINTER_TO_INT(list->data));
+        list = next;
+    }
+    gtk_clist_thaw(clist);
+}
+
+
+typedef void (*ResponseHandler)(const gchar *filename);
+
+static void
+equalizerwin_file_chooser_on_response(GtkWidget * dialog,
+                                      gint response,
+                                      gpointer data)
+{
+    GtkFileChooser *file_chooser = GTK_FILE_CHOOSER(dialog);
+    ResponseHandler handler = (ResponseHandler) data;
+    gchar *filename;
+
+    gtk_widget_hide(dialog);
+
+    switch (response)
+    {
+    case GTK_RESPONSE_ACCEPT:
+        filename = gtk_file_chooser_get_filename(file_chooser);
+        handler(filename);
+        g_free(filename);
+        break;
+
+    case GTK_RESPONSE_REJECT:
+        break;
+    }
+
+    gtk_widget_destroy(dialog);
+}
+                                     
+
+
+static void
+load_preset_file(const gchar *filename)
+{
+    RcFile *rcfile;
+
+    if ((rcfile = bmp_rcfile_open(filename)) != NULL) {
+        equalizerwin_read_bmp_preset(rcfile);
+        bmp_rcfile_free(rcfile);
+    }
+}
+
+static void
+load_winamp_file(const gchar * filename)
+{
+    VFSFile *file;
+    gchar *tmp;
+
+    if (!(file = vfs_fopen(filename, "rb"))) {
+        tmp = g_strconcat("Failed to load WinAmp file: ",filename,"\n",NULL);
+        report_error(tmp);
+        g_free(tmp);
+        return;
+    }
+
+    equalizerwin_read_winamp_eqf(file);
+    vfs_fclose(file);
+}
+
+static void
+import_winamp_file(const gchar * filename)
+{
+    VFSFile *file;
+    gchar *tmp;
+
+    if (!(file = vfs_fopen(filename, "rb"))) {
+        tmp = g_strconcat("Failed to import WinAmp file: ",filename,"\n",NULL);
+        report_error(tmp);
+        g_free(tmp);
+        return;
+    }
+
+    equalizer_presets = g_list_concat(equalizer_presets,
+                                      import_winamp_eqf(file));
+    equalizerwin_write_preset_file(equalizer_presets, "eq.preset");
+
+    vfs_fclose(file);
+}
+
+static void
+save_preset_file(const gchar * filename)
+{
+    RcFile *rcfile;
+    gint i;
+
+    rcfile = bmp_rcfile_new();
+    bmp_rcfile_write_float(rcfile, "Equalizer preset", "Preamp",
+                           eqslider_get_position(equalizerwin_preamp));
+
+    for (i = 0; i < 10; i++) {
+        gchar tmp[7];
+        g_snprintf(tmp, sizeof(tmp), "Band%d", i);
+        bmp_rcfile_write_float(rcfile, "Equalizer preset", tmp,
+                               eqslider_get_position(equalizerwin_bands[i]));
+    }
+
+    bmp_rcfile_write(rcfile, filename);
+    bmp_rcfile_free(rcfile);
+}
+
+static void
+save_winamp_file(const gchar * filename)
+{
+    VFSFile *file;
+
+    gchar name[257];
+    gint i;
+    guchar bands[11];
+    gchar *tmp;
+
+    if (!(file = vfs_fopen(filename, "wb"))) {
+        tmp = g_strconcat("Failed to save WinAmp file: ",filename,"\n",NULL);
+        report_error(tmp);
+        g_free(tmp);
+        return;
+    }
+
+    vfs_fwrite("Winamp EQ library file v1.1\x1a!--", 1, 31, file);
+
+    memset(name, 0, 257);
+    strcpy(name, "Entry1");
+    vfs_fwrite(name, 1, 257, file);
+
+    for (i = 0; i < 10; i++)
+        bands[i] = 63 - (((eqslider_get_position(equalizerwin_bands[i]) + 20) * 63) / 40);
+    bands[10] = 63 - (((eqslider_get_position(equalizerwin_preamp) + 20) * 63) / 40);
+    vfs_fwrite(bands, 1, 11, file);
+
+    vfs_fclose(file);
+}
+
+static gint
+equalizerwin_list_sort_func(GtkCList * clist,
+                            gconstpointer ptr1, gconstpointer ptr2)
+{
+    GtkCListRow *row1 = (GtkCListRow *) ptr1;
+    GtkCListRow *row2 = (GtkCListRow *) ptr2;
+
+    return strcasecmp(GTK_CELL_TEXT(row1->cell[clist->sort_column])->text,
+                      GTK_CELL_TEXT(row2->cell[clist->sort_column])->text);
+}
+
+
+static GtkListStore *
+preset_list_store_new(GList * preset)
+{
+    GtkListStore *store;
+    GtkTreeIter iter;
+    GList *node;
+
+    store = gtk_list_store_new(PRESET_VIEW_N_COLS, G_TYPE_STRING);
+
+    for (node = preset; node; node = g_list_next(node)) {
+        gtk_list_store_append(store, &iter);
+        gtk_list_store_set(store, &iter,
+                           PRESET_VIEW_COL_NAME,
+                           &((EqualizerPreset *) node->data)->name, -1);
+    }
+
+    return store;
+}
+
+
+GtkWidget *
+preset_view_new(GList * preset)
+{
+    GtkWidget *treeview;
+    GtkTreeModel *model;
+    GtkTreeViewColumn *column;
+    GtkCellRenderer *renderer;
+    GtkListStore *store;
+
+    store = preset_list_store_new(preset);
+
+    model = gtk_tree_model_sort_new_with_model(GTK_TREE_MODEL(store));
+    gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model),
+                                         PRESET_VIEW_COL_NAME,
+                                         GTK_SORT_ASCENDING);
+
+    treeview = gtk_tree_view_new_with_model(model);
+    gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(treeview), TRUE);
+    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE);
+
+    renderer = gtk_cell_renderer_text_new();
+    column = gtk_tree_view_column_new_with_attributes(_("Preset"), renderer,
+                                                      "text",
+                                                      PRESET_VIEW_COL_NAME,
+                                                      NULL);
+    gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
+
+    return treeview;
+}
+
+
+static GtkWidget *
+equalizerwin_create_list_window(GList * preset_list,
+                                const gchar * title,
+                                GtkWidget ** window,
+                                GtkSelectionMode sel_mode,
+                                GtkWidget ** entry,
+                                const gchar * btn2_stock_name,
+				const gchar * btn3_stock_name,
+                                const gchar * btn1_stock_name,
+                                GCallback btn2_func,
+				GCallback btn3_func,
+                                GCallback select_row_func)
+{
+    GtkWidget *vbox, *scrolled_window, *bbox, *btn1, *btn2, *btn3, *clist;
+    gchar *preset_text[1];
+    GList *node;
+
+    *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_title(GTK_WINDOW(*window), title);
+    gtk_window_set_type_hint(GTK_WINDOW(*window), GDK_WINDOW_TYPE_HINT_DIALOG);
+    gtk_window_set_default_size(GTK_WINDOW(*window), 350, 300);
+    gtk_window_set_position(GTK_WINDOW(*window), GTK_WIN_POS_CENTER);
+    gtk_container_set_border_width(GTK_CONTAINER(*window), 10);
+    gtk_window_set_transient_for(GTK_WINDOW(*window),
+                                 GTK_WINDOW(equalizerwin));
+    g_signal_connect(*window, "destroy",
+                     G_CALLBACK(gtk_widget_destroyed), window);
+
+    vbox = gtk_vbox_new(FALSE, 10);
+    gtk_container_add(GTK_CONTAINER(*window), vbox);
+
+    scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+
+    preset_text[0] = _("Presets");
+    clist = gtk_clist_new_with_titles(1, preset_text);
+    if (select_row_func)
+        g_signal_connect(clist, "select_row",
+                         G_CALLBACK(select_row_func), NULL);
+    gtk_clist_column_titles_passive(GTK_CLIST(clist));
+    gtk_clist_set_selection_mode(GTK_CLIST(clist), sel_mode);
+
+    for (node = preset_list; node; node = g_list_next(node)) {
+        gtk_clist_append(GTK_CLIST(clist),
+                         &((EqualizerPreset *) node->data)->name);
+    }
+    gtk_clist_set_compare_func(GTK_CLIST(clist), equalizerwin_list_sort_func);
+    gtk_clist_sort(GTK_CLIST(clist));
+
+    gtk_container_add(GTK_CONTAINER(scrolled_window), clist);
+    gtk_box_pack_start(GTK_BOX(vbox), scrolled_window, TRUE, TRUE, 0);
+
+    if (entry) {
+        *entry = gtk_entry_new();
+        g_signal_connect(*entry, "activate", btn2_func, NULL);
+        gtk_box_pack_start(GTK_BOX(vbox), *entry, FALSE, FALSE, 0);
+    }
+
+    bbox = gtk_hbutton_box_new();
+    gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
+    gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
+    gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+    btn1 = gtk_button_new_from_stock(btn1_stock_name);
+    g_signal_connect_swapped(btn1, "clicked",
+                             G_CALLBACK(gtk_widget_destroy),
+                             GTK_OBJECT(*window));
+    gtk_box_pack_start(GTK_BOX(bbox), btn1, TRUE, TRUE, 0);
+
+    if (btn3_stock_name) {
+        btn3 = gtk_button_new_from_stock(btn3_stock_name);
+        g_signal_connect(btn3, "clicked", G_CALLBACK(btn3_func), clist);
+        gtk_box_pack_start(GTK_BOX(bbox), btn3, TRUE, TRUE, 0);
+    }
+
+    btn2 = gtk_button_new_from_stock(btn2_stock_name);
+    g_signal_connect(btn2, "clicked", G_CALLBACK(btn2_func), clist);
+    GTK_WIDGET_SET_FLAGS(btn2, GTK_CAN_DEFAULT);
+        
+    gtk_box_pack_start(GTK_BOX(bbox), btn2, TRUE, TRUE, 0);
+
+    gtk_widget_grab_default(btn2);
+
+
+    gtk_widget_show_all(*window);
+
+    return *window;
+}
+
+void
+equalizerwin_presets_menu_cb(gpointer cb_data, guint action, GtkWidget * w)
+{
+    GtkWidget *dialog;
+
+    switch (action) {
+    case EQUALIZER_PRESETS_LOAD_PRESET:
+        if (!equalizerwin_load_window)
+            equalizerwin_create_list_window(equalizer_presets,
+                                            _("Load preset"),
+                                            &equalizerwin_load_window,
+                                            GTK_SELECTION_SINGLE, NULL,
+                                            GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_CANCEL,
+                                            G_CALLBACK
+                                            (equalizerwin_load_ok),
+					    G_CALLBACK
+					    (equalizerwin_load_apply),
+                                            G_CALLBACK
+                                            (equalizerwin_load_select));
+        else
+            gtk_window_present(GTK_WINDOW(equalizerwin_load_window));
+        break;
+    case EQUALIZER_PRESETS_LOAD_AUTOPRESET:
+        if (!equalizerwin_load_auto_window)
+            equalizerwin_create_list_window(equalizer_auto_presets,
+                                            _("Load auto-preset"),
+                                            &equalizerwin_load_auto_window,
+                                            GTK_SELECTION_SINGLE, NULL,
+                                            GTK_STOCK_OK, NULL, GTK_STOCK_CANCEL,
+                                            G_CALLBACK
+                                            (equalizerwin_load_auto_ok),
+					    NULL,
+                                            G_CALLBACK
+                                            (equalizerwin_load_auto_select));
+        else
+            gtk_window_present(GTK_WINDOW(equalizerwin_load_auto_window));
+        break;
+    case EQUALIZER_PRESETS_LOAD_DEFAULT:
+        equalizerwin_load_preset(equalizer_presets, "Default");
+        break;
+    case EQUALIZER_PRESETS_LOAD_ZERO:
+        {
+            gint i;
+
+            eqslider_set_position(equalizerwin_preamp, 0);
+            for (i = 0; i < 10; i++)
+                eqslider_set_position(equalizerwin_bands[i], 0);
+            equalizerwin_eq_changed();
+            break;
+        }
+    case EQUALIZER_PRESETS_LOAD_FROM_FILE:
+        dialog = make_filebrowser(_("Load equalizer preset"), FALSE);
+        g_signal_connect(dialog , "response",
+                         G_CALLBACK(equalizerwin_file_chooser_on_response),
+                         load_preset_file);
+        break;
+    case EQUALIZER_PRESETS_LOAD_FROM_WINAMPFILE:
+        dialog = make_filebrowser(_("Load equalizer preset"), FALSE);
+        g_signal_connect(dialog, "response",
+                         G_CALLBACK(equalizerwin_file_chooser_on_response),
+                         load_winamp_file);
+        break;
+
+    case EQUALIZER_PRESETS_IMPORT_WINAMPFILE:
+        dialog = make_filebrowser(_("Load equalizer preset"), FALSE);
+        g_signal_connect(dialog, "response",
+                         G_CALLBACK(equalizerwin_file_chooser_on_response),
+                         import_winamp_file);
+        break;
+
+    case EQUALIZER_PRESETS_SAVE_PRESET:
+        if (!equalizerwin_save_window)
+            equalizerwin_create_list_window(equalizer_presets,
+                                            _("Save preset"),
+                                            &equalizerwin_save_window,
+                                            GTK_SELECTION_SINGLE,
+                                            &equalizerwin_save_entry,
+                                            GTK_STOCK_OK, NULL, GTK_STOCK_CANCEL, 
+                                            G_CALLBACK
+                                            (equalizerwin_save_ok),
+					    NULL,
+                                            G_CALLBACK
+                                            (equalizerwin_save_select));
+        else
+            gtk_window_present(GTK_WINDOW(equalizerwin_save_window));
+        break;
+
+    case EQUALIZER_PRESETS_SAVE_AUTOPRESET:
+        {
+            gchar *name;
+
+            if (!equalizerwin_save_auto_window)
+                equalizerwin_create_list_window(equalizer_auto_presets,
+                                                _("Save auto-preset"),
+                                                &equalizerwin_save_auto_window,
+                                                GTK_SELECTION_SINGLE,
+                                                &equalizerwin_save_auto_entry,
+                                                GTK_STOCK_OK,
+						NULL,
+                                                GTK_STOCK_CANCEL,
+                                                G_CALLBACK
+                                                (equalizerwin_save_auto_ok),
+						NULL,
+                                                G_CALLBACK
+                                                (equalizerwin_save_auto_select));
+            else
+                gtk_window_present(GTK_WINDOW(equalizerwin_save_auto_window));
+
+            name = playlist_get_filename(playlist_get_position());
+
+            if (name) {
+                gtk_entry_set_text(GTK_ENTRY(equalizerwin_save_auto_entry),
+                                   g_basename(name));
+                g_free(name);
+            }
+            break;
+        }
+
+    case EQUALIZER_PRESETS_SAVE_DEFAULT:
+        equalizer_presets =
+            equalizerwin_save_preset(equalizer_presets, "Default",
+                                     "eq.preset");
+        break;
+
+    case EQUALIZER_PRESETS_SAVE_TO_FILE:
+        {
+            gchar *songname;
+
+            dialog = make_filebrowser(_("Save equalizer preset"), TRUE);
+            g_signal_connect(dialog, "response",
+                             G_CALLBACK(equalizerwin_file_chooser_on_response),
+                             save_preset_file);
+
+            songname = playlist_get_filename(playlist_get_position());
+            if (songname) {
+                gchar *eqname = g_strdup_printf("%s.%s", songname,
+                                                cfg.eqpreset_extension);
+                g_free(songname);
+                gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog),
+                                              eqname);
+                g_free(eqname);
+            }
+
+            break;
+        }
+
+    case EQUALIZER_PRESETS_SAVE_TO_WINAMPFILE:
+        dialog = make_filebrowser(_("Save equalizer preset"), TRUE);
+        g_signal_connect(dialog, "response",
+                         G_CALLBACK(equalizerwin_file_chooser_on_response),
+                         save_winamp_file);
+        break;
+
+    case EQUALIZER_PRESETS_DELETE_PRESET:
+        if (!equalizerwin_delete_window)
+            equalizerwin_create_list_window(equalizer_presets,
+                                            _("Delete preset"),
+                                            &equalizerwin_delete_window,
+                                            GTK_SELECTION_EXTENDED, NULL,
+                                            GTK_STOCK_DELETE,
+					    NULL,
+                                            GTK_STOCK_CLOSE,
+                                            G_CALLBACK
+                                            (equalizerwin_delete_delete),
+					    NULL,
+                                            NULL);
+        else
+            gtk_window_present(GTK_WINDOW(equalizerwin_delete_window));
+
+        break;
+
+    case EQUALIZER_PRESETS_DELETE_AUTOPRESET:
+        if (!equalizerwin_delete_auto_window)
+            equalizerwin_create_list_window(equalizer_auto_presets,
+                                            _("Delete auto-preset"),
+                                            &equalizerwin_delete_auto_window,
+                                            GTK_SELECTION_EXTENDED, NULL,
+                                            GTK_STOCK_DELETE,
+					    NULL,
+                                            GTK_STOCK_CLOSE,
+                                            G_CALLBACK
+                                            (equalizerwin_delete_auto_delete),
+					    NULL,
+                                            NULL);
+        else
+            gtk_window_present(GTK_WINDOW(equalizerwin_delete_auto_window));
+        
+        break;
+    }
+}
+
+void
+equalizerwin_load_auto_preset(const gchar * filename)
+{
+    gchar *presetfilename, *directory;
+    RcFile *rcfile;
+
+    g_return_if_fail(filename != NULL);
+
+    if (!cfg.equalizer_autoload)
+        return;
+
+    presetfilename = g_strconcat(filename, ".", cfg.eqpreset_extension, NULL);
+
+    /* First try to find a per file preset file */
+    if (strlen(cfg.eqpreset_extension) > 0 &&
+        (rcfile = bmp_rcfile_open(presetfilename)) != NULL) {
+        g_free(presetfilename);
+        equalizerwin_read_bmp_preset(rcfile);
+        bmp_rcfile_free(rcfile);
+        return;
+    }
+
+    g_free(presetfilename);
+
+    directory = g_path_get_dirname(filename);
+    presetfilename = g_build_filename(directory, cfg.eqpreset_default_file,
+                                      NULL);
+    g_free(directory);
+
+    /* Try to find a per directory preset file */
+    if (strlen(cfg.eqpreset_default_file) > 0 &&
+        (rcfile = bmp_rcfile_open(presetfilename)) != NULL) {
+        equalizerwin_read_bmp_preset(rcfile);
+        bmp_rcfile_free(rcfile);
+    }
+    else if (!equalizerwin_load_preset
+             (equalizer_auto_presets, g_basename(filename))) {
+        /* Fall back to the oldstyle auto presets */
+        equalizerwin_load_preset(equalizer_presets, "Default");
+    }
+
+    g_free(presetfilename);
+}
+
+void
+equalizerwin_set_preamp(gfloat preamp)
+{
+    eqslider_set_position(equalizerwin_preamp, preamp);
+    equalizerwin_eq_changed();
+}
+
+void
+equalizerwin_set_band(gint band, gfloat value)
+{
+    g_return_if_fail(band >= 0 && band < 10);
+    eqslider_set_position(equalizerwin_bands[band], value);
+}
+
+gfloat
+equalizerwin_get_preamp(void)
+{
+    return eqslider_get_position(equalizerwin_preamp);
+}
+
+gfloat
+equalizerwin_get_band(gint band)
+{
+    g_return_val_if_fail(band >= 0 && band < 10, 0);
+    return eqslider_get_position(equalizerwin_bands[band]);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/equalizer.h	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,60 @@
+/*  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; 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.
+ */
+
+#ifndef EQUALIZER_H
+#define EQUALIZER_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+
+#include "widgets/widgetcore.h"
+
+#define EQUALIZER_HEIGHT         (gint)(cfg.equalizer_shaded ? 14 : 116)
+#define EQUALIZER_WIDTH          (gint)275
+
+#define EQUALIZER_DEFAULT_POS_X  20
+#define EQUALIZER_DEFAULT_POS_Y  136
+
+#define EQUALIZER_DEFAULT_DIR_PRESET "dir_default.preset"
+#define EQUALIZER_DEFAULT_PRESET_EXT "preset"
+
+void equalizerwin_set_shade_menu_cb(gboolean shaded);
+void draw_equalizer_window(gboolean force);
+void equalizerwin_create(void);
+void equalizerwin_show(gboolean show);
+void equalizerwin_real_show(void);
+void equalizerwin_real_hide(void);
+void equalizerwin_load_auto_preset(const gchar * filename);
+void equalizerwin_set_volume_slider(gint percent);
+void equalizerwin_set_balance_slider(gint percent);
+void equalizerwin_eq_changed(void);
+void equalizerwin_set_preamp(gfloat preamp);
+void equalizerwin_set_band(gint band, gfloat value);
+gfloat equalizerwin_get_preamp(void);
+gfloat equalizerwin_get_band(gint band);
+
+gboolean equalizerwin_has_focus(void);
+
+extern GtkWidget *equalizerwin;
+extern PButton *equalizerwin_close;
+extern gboolean equalizerwin_focus;
+
+#endif
--- a/audacious/genevent.c	Thu Sep 07 22:00:35 2006 -0700
+++ b/audacious/genevent.c	Thu Sep 07 22:26:54 2006 -0700
@@ -52,13 +52,16 @@
 #include "controlsocket.h"
 #include "dnd.h"
 #include "effect.h"
+#include "equalizer.h"
 #include "general.h"
 #include "hints.h"
 #include "input.h"
 #include "logger.h"
+#include "mainwin.h"
 #include "output.h"
 #include "playback.h"
 #include "playlist.h"
+#include "ui_playlist.h"
 #include "pluginenum.h"
 #include "prefswin.h"
 #include "skinwin.h"
@@ -67,8 +70,6 @@
 
 gboolean ev_waiting = FALSE;
 
-#if 0
-/* *** TO WA2GUI *** */
 static gboolean
 idle_func_change_song(gboolean waiting)
 {
@@ -120,7 +121,6 @@
 
     return waiting;
 }
-#endif
 
 gint
 audcore_generic_events(void)
@@ -135,9 +135,7 @@
         switch (time) {
         case -1:
             /* no song playing */
-#if 0
             ev_waiting = idle_func_change_song(ev_waiting);
-#endif
             break;
 
         default:
--- a/audacious/genevent.h	Thu Sep 07 22:00:35 2006 -0700
+++ b/audacious/genevent.h	Thu Sep 07 22:26:54 2006 -0700
@@ -25,6 +25,8 @@
 #ifndef GENEVENT_H
 #define GENEVENT_H
 
+#include "mainwin.h"
+
 extern gboolean ev_waiting;
 
 gint audcore_generic_events(void);
--- a/audacious/input.c	Thu Sep 07 22:00:35 2006 -0700
+++ b/audacious/input.c	Thu Sep 07 22:26:54 2006 -0700
@@ -31,12 +31,13 @@
 #include "fft.h"
 #include "input.h"
 #include "main.h"
+#include "mainwin.h"
 #include "output.h"
 #include "util.h"
 #include "visualization.h"
 #include "playback.h"
+#include "widgets/widgetcore.h"
 #include "pluginenum.h"
-#include "interface.h"
 
 #include "libaudacious/titlestring.h"
 #include "libaudacious/util.h"
@@ -314,7 +315,7 @@
         GtkCellRenderer *renderer;
 
         dialog =
-            gtk_message_dialog_new_with_markup(GTK_WINDOW(current_interface->parentwin),
+            gtk_message_dialog_new_with_markup(GTK_WINDOW(mainwin),
                                                GTK_DIALOG_DESTROY_WITH_PARENT,
                                                GTK_MESSAGE_ERROR,
                                                GTK_BUTTONS_OK,
@@ -737,13 +738,15 @@
 gchar *
 input_get_info_text(void)
 {
-    return current_interface->get_status_text();
+    return g_strdup(input_info_text);
 }
 
 void
 input_set_info_text(const gchar * text)
 {
-    current_interface->set_status_text(text);
+    g_free(input_info_text);
+    input_info_text = g_strdup(text);
+    mainwin_set_info_text();
 }
 
 void
@@ -755,15 +758,11 @@
     if (!get_current_input_plugin())
         return;
 
-    current_interface->buffering_notify(ip_data.buffering);
-
-#if 0
     ip_data.buffering = status;
 
     if (ip_data.buffering == TRUE && mainwin_playstatus->ps_status == STATUS_STOP)
         mainwin_playstatus->ps_status = STATUS_PLAY;
     playstatus_set_status_buffering(mainwin_playstatus, ip_data.buffering);
-#endif
 }
 
 void
--- a/audacious/interface.h	Thu Sep 07 22:00:35 2006 -0700
+++ b/audacious/interface.h	Thu Sep 07 22:26:54 2006 -0700
@@ -1,54 +0,0 @@
-/*
- *  Copyright (c) 2006 William Pitcock <nenolod -at- nenolod.net>
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; 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.
- */
-
-#ifndef _INTERFACE_H_
-#define _INTERFACE_H_
-
-#include <glib.h>
-
-struct _InterfacePlugin {
-    gchar *name;
-    GtkWidget *parentwin;
-
-    void (*init) (void);
-    void (*cleanup) (void);
-    void (*about) (void);
-    void (*configure) (void);
-    void (*disable_plugin) (struct _VisPlugin *);
-    void (*playback_start) (void);
-    void (*playback_stop) (void);
-    void (*clear_pcm_data) (void);
-    void (*send_pcm_data) (gint16 pcm_data[2][512], gint nch, gint length);
-    void (*redraw) (void);
-    gboolean (*idle_callback) (gboolean waiting);
-    void (*playstatus_notify) (gint status);
-    void (*buffering_notify) (gint status);
-    void (*set_status_text) (gchar *text);
-    gchar *(*get_status_text) (void);
-    void (*set_song_info) (gint, gint, gint);
-    void (*present) (void);
-};
-
-typedef struct _InterfacePlugin InterfacePlugin;
-
-extern void register_interface_plugin(InterfacePlugin *);
-extern void start_interface_plugin(InterfacePlugin *);
-
-extern InterfacePlugin *current_interface;
-
-#endif
--- a/audacious/main.c	Thu Sep 07 22:00:35 2006 -0700
+++ b/audacious/main.c	Thu Sep 07 22:26:54 2006 -0700
@@ -54,14 +54,17 @@
 #include "controlsocket.h"
 #include "dnd.h"
 #include "effect.h"
+#include "equalizer.h"
 #include "general.h"
 #include "genevent.h"
 #include "hints.h"
 #include "input.h"
 #include "logger.h"
+#include "mainwin.h"
 #include "output.h"
 #include "playback.h"
 #include "playlist.h"
+#include "ui_playlist.h"
 #include "pluginenum.h"
 #include "prefswin.h"
 #include "skinwin.h"
@@ -80,6 +83,7 @@
 
 const gchar *application_name = N_("Audacious");
 
+
 struct _BmpCmdLineOpt {
     GList *filenames;
     gint session;
@@ -532,10 +536,10 @@
     }
 
     if (!cfg.playlist_font)
-        cfg.playlist_font = g_strdup("Sans Bold 8");
+        cfg.playlist_font = g_strdup(PLAYLISTWIN_DEFAULT_FONT);
 
     if (!cfg.mainwin_font)
-        cfg.mainwin_font = g_strdup("Sans Bold 9");
+        cfg.mainwin_font = g_strdup(MAINWIN_DEFAULT_FONT);
 
     if (!cfg.gentitle_format)
         cfg.gentitle_format = g_strdup("%{p:%p - %}%{a:%a - %}%t");
@@ -553,10 +557,10 @@
     }
 
     if (!cfg.eqpreset_default_file)
-        cfg.eqpreset_default_file = g_strdup("dir_default.preset");
+        cfg.eqpreset_default_file = g_strdup(EQUALIZER_DEFAULT_DIR_PRESET);
 
     if (!cfg.eqpreset_extension)
-        cfg.eqpreset_extension = g_strdup("preset");
+        cfg.eqpreset_extension = g_strdup(EQUALIZER_DEFAULT_PRESET_EXT);
 
     if (!cfg.cover_name_include)
 	    cfg.cover_name_include = g_strdup("");
@@ -1057,14 +1061,10 @@
 
         gtk_accel_map_load(bmp_paths[BMP_PATH_ACCEL_FILE]);
 
-        current_interface->init();
-#if 0
-	/* *** TO WA2GUI *** */
         mainwin_create();
 
         playlistwin_create();
         equalizerwin_create();
-#endif
 
         if (!init_skins(cfg.skin)) {
             run_load_skin_error_dialog(cfg.skin);
@@ -1079,14 +1079,11 @@
     playlist_load(bmp_paths[BMP_PATH_PLAYLIST_FILE]);
     playlist_set_position(cfg.playlist_position);
 
-#if 0
-    /* *** TO WA2GUI *** */
     /* this needs to be called after all 3 windows are created and
      * input plugins are setup'ed 
      * but not if we're running headless --nenolod
      */
     mainwin_setup_menus();
-#endif
 
     if (options.headless != 1)
         GDK_THREADS_LEAVE();
@@ -1109,7 +1106,6 @@
 	create_fileinfo_window();
 	create_filepopup_window();
 
-#if 0
         if (cfg.player_visible)
             mainwin_show(TRUE);
         else if (!cfg.playlist_visible && !cfg.equalizer_visible)
@@ -1122,9 +1118,6 @@
             playlistwin_show();
 
         hint_set_always(cfg.always_on_top);
-#endif
-
-        current_interface->present();
 
         playlist_start_get_info_thread();
         mainwin_attach_idle_func();
--- a/audacious/main.h	Thu Sep 07 22:00:35 2006 -0700
+++ b/audacious/main.h	Thu Sep 07 22:26:54 2006 -0700
@@ -22,8 +22,8 @@
 #ifndef MAIN_H
 #define MAIN_H
 
-#include <glib.h>
-#include <glib/gi18n.h>
+#include "widgets/widgetcore.h"
+#include "mainwin.h"
 
 #include <sys/types.h>
 #include <sys/stat.h>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/mainwin.c	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,3570 @@
+/*  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; 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gprintf.h>
+#include <gtk/gtk.h>
+#include <gtk/gtkmessagedialog.h>
+
+/* GDK including */
+#include "platform/smartinclude.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <X11/Xlib.h>
+
+#include "widgets/widgetcore.h"
+#include "mainwin.h"
+#include "pixmaps.h"
+
+#include "main.h"
+
+#include "controlsocket.h"
+#include "pluginenum.h"
+
+#include "credits.h"
+#include "dnd.h"
+#include "dock.h"
+#include "equalizer.h"
+#include "hints.h"
+#include "input.h"
+#include "ui_playlist.h"
+#include "prefswin.h"
+#include "skinwin.h"
+#include "genevent.h"
+#include "playback.h"
+#include "playlist.h"
+#include "urldecode.h"
+#include "util.h"
+#include "visualization.h"
+#include "libaudacious/configdb.h"
+
+static GTimeVal cb_time; /* click delay for tristate is defined by TRISTATE_THRESHOLD */
+
+#define ITEM_SEPARATOR {"/-", NULL, NULL, 0, "<Separator>"}
+#define TRISTATE_THRESHOLD 200
+
+/*
+ * If you change the menu above change these defines also
+ */
+
+#define MAINWIN_VIS_MENU_VIS_MODE               1
+#define MAINWIN_VIS_MENU_NUM_VIS_MODE           3
+#define MAINWIN_VIS_MENU_ANALYZER_MODE          5
+#define MAINWIN_VIS_MENU_NUM_ANALYZER_MODE      3
+#define MAINWIN_VIS_MENU_ANALYZER_TYPE          9
+#define MAINWIN_VIS_MENU_NUM_ANALYZER_TYPE      2
+#define MAINWIN_VIS_MENU_ANALYZER_PEAKS         12
+#define MAINWIN_VIS_MENU_SCOPE_MODE             14
+#define MAINWIN_VIS_MENU_NUM_SCOPE_MODE         3
+#define MAINWIN_VIS_MENU_WSHADEVU_MODE          18
+#define MAINWIN_VIS_MENU_NUM_WSHADEVU_MODE      2
+#define MAINWIN_VIS_MENU_REFRESH_RATE           21
+#define MAINWIN_VIS_MENU_NUM_REFRESH_RATE       4
+#define MAINWIN_VIS_MENU_AFALLOFF               26
+#define MAINWIN_VIS_MENU_NUM_AFALLOFF           5
+#define MAINWIN_VIS_MENU_PFALLOFF               32
+#define MAINWIN_VIS_MENU_NUM_PFALLOFF           5
+
+#define VOLSET_DISP_TIMES 5
+
+enum {
+    MAINWIN_SEEK_REV = -1,
+    MAINWIN_SEEK_NIL,
+    MAINWIN_SEEK_FWD
+};
+
+enum {
+    MAINWIN_SONGNAME_FILEINFO,
+    MAINWIN_SONGNAME_JTF,
+    MAINWIN_SONGNAME_JTT,
+    MAINWIN_SONGNAME_SCROLL,
+    MAINWIN_SONGNAME_STOPAFTERSONG
+};
+
+enum {
+    MAINWIN_OPT_SKIN, MAINWIN_OPT_RELOADSKIN,
+    MAINWIN_OPT_REPEAT, MAINWIN_OPT_SHUFFLE, MAINWIN_OPT_NPA,
+    MAINWIN_OPT_TELAPSED, MAINWIN_OPT_TREMAINING,
+    MAINWIN_OPT_ALWAYS,
+    MAINWIN_OPT_STICKY,
+    MAINWIN_OPT_WS,
+    MAINWIN_OPT_PWS,
+    MAINWIN_OPT_EQWS
+};
+
+enum {
+    MAINWIN_VIS_ANALYZER, MAINWIN_VIS_SCOPE, MAINWIN_VIS_OFF,
+    MAINWIN_VIS_ANALYZER_NORMAL, MAINWIN_VIS_ANALYZER_FIRE,
+    MAINWIN_VIS_ANALYZER_VLINES,
+    MAINWIN_VIS_ANALYZER_LINES, MAINWIN_VIS_ANALYZER_BARS,
+    MAINWIN_VIS_ANALYZER_PEAKS,
+    MAINWIN_VIS_SCOPE_DOT, MAINWIN_VIS_SCOPE_LINE, MAINWIN_VIS_SCOPE_SOLID,
+    MAINWIN_VIS_VU_NORMAL, MAINWIN_VIS_VU_SMOOTH,
+    MAINWIN_VIS_REFRESH_FULL, MAINWIN_VIS_REFRESH_HALF,
+    MAINWIN_VIS_REFRESH_QUARTER, MAINWIN_VIS_REFRESH_EIGHTH,
+    MAINWIN_VIS_AFALLOFF_SLOWEST, MAINWIN_VIS_AFALLOFF_SLOW,
+    MAINWIN_VIS_AFALLOFF_MEDIUM, MAINWIN_VIS_AFALLOFF_FAST,
+    MAINWIN_VIS_AFALLOFF_FASTEST,
+    MAINWIN_VIS_PFALLOFF_SLOWEST, MAINWIN_VIS_PFALLOFF_SLOW,
+    MAINWIN_VIS_PFALLOFF_MEDIUM, MAINWIN_VIS_PFALLOFF_FAST,
+    MAINWIN_VIS_PFALLOFF_FASTEST,
+    MAINWIN_VIS_PLUGINS
+};
+
+enum {
+    MAINWIN_VIS_ACTIVE_MAINWIN, MAINWIN_VIS_ACTIVE_PLAYLISTWIN
+};
+
+
+typedef struct _PlaybackInfo PlaybackInfo;
+
+struct _PlaybackInfo {
+    gchar *title;
+    gint bitrate;
+    gint frequency;
+    gint n_channels;
+};
+
+
+GtkWidget *mainwin = NULL;
+GtkWidget *err = NULL; /* an error dialog for miscellaneous error messages */
+
+static GdkBitmap *nullmask;
+static gint balance;
+
+GtkWidget *mainwin_jtf = NULL;
+static GtkWidget *mainwin_jtt = NULL;
+
+GtkItemFactory *mainwin_songname_menu, *mainwin_vis_menu;
+GtkItemFactory *mainwin_general_menu, *mainwin_play_menu, *mainwin_add_menu;
+GtkItemFactory *mainwin_view_menu;
+
+gint seek_state = MAINWIN_SEEK_NIL;
+gint seek_initial_pos = 0;
+
+GdkGC *mainwin_gc;
+static GdkPixmap *mainwin_bg = NULL;
+
+GtkAccelGroup *mainwin_accel = NULL;
+
+static PButton *mainwin_menubtn;
+static PButton *mainwin_minimize, *mainwin_shade, *mainwin_close;
+
+static PButton *mainwin_rew, *mainwin_fwd;
+static PButton *mainwin_eject;
+static PButton *mainwin_play, *mainwin_pause, *mainwin_stop;
+
+TButton *mainwin_shuffle, *mainwin_repeat, *mainwin_eq, *mainwin_pl;
+TextBox *mainwin_info;
+TextBox *mainwin_stime_min, *mainwin_stime_sec;
+
+static TextBox *mainwin_rate_text, *mainwin_freq_text, 
+	*mainwin_othertext;
+
+PlayStatus *mainwin_playstatus;
+
+Number *mainwin_minus_num, *mainwin_10min_num, *mainwin_min_num;
+Number *mainwin_10sec_num, *mainwin_sec_num;
+
+static gboolean setting_volume = FALSE;
+
+Vis *active_vis;
+Vis *mainwin_vis;
+SVis *mainwin_svis;
+
+HSlider *mainwin_sposition = NULL;
+
+static MenuRow *mainwin_menurow;
+static HSlider *mainwin_volume, *mainwin_balance, *mainwin_position;
+static MonoStereo *mainwin_monostereo;
+static SButton *mainwin_srew, *mainwin_splay, *mainwin_spause;
+static SButton *mainwin_sstop, *mainwin_sfwd, *mainwin_seject, *mainwin_about;
+
+static GList *mainwin_wlist = NULL;
+
+static gint mainwin_timeout_id;
+
+G_LOCK_DEFINE_STATIC(mainwin_title);
+
+static gboolean mainwin_force_redraw = FALSE;
+static gchar *mainwin_title_text = NULL;
+static gboolean mainwin_info_text_locked = FALSE;
+
+
+static void mainwin_songname_menu_callback(gpointer user_data,
+                                           guint action,
+                                           GtkWidget * widget);
+
+static void mainwin_vis_menu_callback(gpointer user_data,
+                                      guint action,
+                                      GtkWidget * widget);
+
+static void mainwin_view_menu_callback(gpointer user_data,
+                                       guint action,
+                                       GtkWidget * widget);
+
+static void mainwin_play_menu_callback(gpointer user_data,
+                                       guint action,
+                                       GtkWidget * widget);
+
+/* Song name area menu */
+
+GtkItemFactoryEntry mainwin_songname_menu_entries[] = {
+    {N_("/View Track Details"), "<alt>i", mainwin_general_menu_callback,
+     MAINWIN_GENERAL_FILEINFO, "<ImageItem>", my_pixbuf},
+    {N_("/Jump to File"), "J", mainwin_songname_menu_callback,
+     MAINWIN_SONGNAME_JTF, "<StockItem>", GTK_STOCK_JUMP_TO},
+    {"/-", NULL, NULL, 0, "<Separator>", NULL},
+    {N_("/Autoscroll Songname"), NULL, mainwin_songname_menu_callback,
+     MAINWIN_SONGNAME_SCROLL, "<ToggleItem>", NULL},
+    {N_("/Stop After Current Song"), "<control>M", mainwin_songname_menu_callback,
+     MAINWIN_SONGNAME_STOPAFTERSONG, "<ToggleItem>", NULL},
+};
+
+static gint mainwin_songname_menu_entries_num =
+    G_N_ELEMENTS(mainwin_songname_menu_entries);
+
+/* Mini-visualizer area menu */
+
+GtkItemFactoryEntry mainwin_vis_menu_entries[] = {
+    {N_("/Visualization Mode"), NULL, NULL, 0, "<Branch>", NULL},
+    {N_("/Visualization Mode/Analyzer"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_ANALYZER, "<RadioItem>", NULL},
+    {N_("/Visualization Mode/Scope"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_SCOPE, "/Visualization Mode/Analyzer", NULL},
+    {N_("/Visualization Mode/Off"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_OFF, "/Visualization Mode/Analyzer", NULL},
+    {N_("/Analyzer Mode"), NULL, NULL, 0, "<Branch>", NULL},
+    {N_("/Analyzer Mode/Normal"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_ANALYZER_NORMAL, "<RadioItem>", NULL},
+    {N_("/Analyzer Mode/Fire"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_ANALYZER_FIRE, "/Analyzer Mode/Normal", NULL},
+    {N_("/Analyzer Mode/Vertical Lines"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_ANALYZER_VLINES, "/Analyzer Mode/Normal", NULL},
+    {"/Analyzer Mode/-", NULL, NULL, 0, "<Separator>", NULL},
+    {N_("/Analyzer Mode/Lines"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_ANALYZER_LINES, "<RadioItem>", NULL},
+    {N_("/Analyzer Mode/Bars"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_ANALYZER_BARS, "/Analyzer Mode/Lines", NULL},
+    {"/Analyzer Mode/-", NULL, NULL, 0, "<Separator>", NULL},
+    {N_("/Analyzer Mode/Peaks"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_ANALYZER_PEAKS, "<ToggleItem>", NULL},
+    {N_("/Scope Mode"), NULL, NULL, 0, "<Branch>", NULL},
+    {N_("/Scope Mode/Dot Scope"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_SCOPE_DOT, "<RadioItem>", NULL},
+    {N_("/Scope Mode/Line Scope"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_SCOPE_LINE, "/Scope Mode/Dot Scope", NULL},
+    {N_("/Scope Mode/Solid Scope"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_SCOPE_SOLID, "/Scope Mode/Dot Scope", NULL},
+    {N_("/WindowShade VU Mode"), NULL, NULL, 0, "<Branch>", NULL},
+    {N_("/WindowShade VU Mode/Normal"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_VU_NORMAL, "<RadioItem>", NULL},
+    {N_("/WindowShade VU Mode/Smooth"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_VU_SMOOTH, "/WindowShade VU Mode/Normal", NULL},
+    {N_("/Refresh Rate"), NULL, NULL, 0, "<Branch>", NULL},
+    {N_("/Refresh Rate/Full (~50 fps)"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_REFRESH_FULL, "<RadioItem>", NULL},
+    {N_("/Refresh Rate/Half (~25 fps)"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_REFRESH_HALF, "/Refresh Rate/Full (~50 fps)", NULL},
+    {N_("/Refresh Rate/Quarter (~13 fps)"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_REFRESH_QUARTER, "/Refresh Rate/Full (~50 fps)", NULL},
+    {N_("/Refresh Rate/Eighth (~6 fps)"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_REFRESH_EIGHTH, "/Refresh Rate/Full (~50 fps)", NULL},
+    {N_("/Analyzer Falloff"), NULL, NULL, 0, "<Branch>", NULL},
+    {N_("/Analyzer Falloff/Slowest"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_AFALLOFF_SLOWEST, "<RadioItem>", NULL},
+    {N_("/Analyzer Falloff/Slow"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_AFALLOFF_SLOW, "/Analyzer Falloff/Slowest", NULL},
+    {N_("/Analyzer Falloff/Medium"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_AFALLOFF_MEDIUM, "/Analyzer Falloff/Slowest", NULL},
+    {N_("/Analyzer Falloff/Fast"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_AFALLOFF_FAST, "/Analyzer Falloff/Slowest", NULL},
+    {N_("/Analyzer Falloff/Fastest"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_AFALLOFF_FASTEST, "/Analyzer Falloff/Slowest", NULL},
+    {N_("/Peaks Falloff"), NULL, NULL, 0, "<Branch>", NULL},
+    {N_("/Peaks Falloff/Slowest"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_PFALLOFF_SLOWEST, "<RadioItem>", NULL},
+    {N_("/Peaks Falloff/Slow"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_PFALLOFF_SLOW, "/Peaks Falloff/Slowest", NULL},
+    {N_("/Peaks Falloff/Medium"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_PFALLOFF_MEDIUM, "/Peaks Falloff/Slowest", NULL},
+    {N_("/Peaks Falloff/Fast"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_PFALLOFF_FAST, "/Peaks Falloff/Slowest", NULL},
+    {N_("/Peaks Falloff/Fastest"), NULL, mainwin_vis_menu_callback,
+     MAINWIN_VIS_PFALLOFF_FASTEST, "/Peaks Falloff/Slowest", NULL}
+};
+
+static const gint mainwin_vis_menu_entries_num =
+    G_N_ELEMENTS(mainwin_vis_menu_entries);
+
+/* Playback menu (now used only for accelerators) */
+
+GtkItemFactoryEntry mainwin_playback_menu_entries[] = {
+    {N_("/Play CD"), "<alt>C", mainwin_general_menu_callback,
+     MAINWIN_GENERAL_PLAYCD, "<StockItem>", GTK_STOCK_CDROM},
+    {"/-", NULL, NULL, 0, "<Separator>", NULL},
+    {N_("/Repeat"), "R", mainwin_play_menu_callback,
+     MAINWIN_OPT_REPEAT, "<ToggleItem>", NULL},
+    {N_("/Shuffle"), "S", mainwin_play_menu_callback,
+     MAINWIN_OPT_SHUFFLE, "<ToggleItem>", NULL},
+    {N_("/No Playlist Advance"), "<control>N", mainwin_play_menu_callback,
+     MAINWIN_OPT_NPA, "<ToggleItem>", NULL},
+    {N_("/Stop After Current Song"), "<control>M", mainwin_songname_menu_callback,
+     MAINWIN_SONGNAME_STOPAFTERSONG, "<ToggleItem>", NULL},
+    {"/-", NULL, NULL, 0, "<Separator>", NULL},
+    {N_("/Play"), "x", mainwin_general_menu_callback,
+     MAINWIN_GENERAL_PLAY, "<StockItem>", GTK_STOCK_MEDIA_PLAY},
+    {N_("/Pause"), "c", mainwin_general_menu_callback,
+     MAINWIN_GENERAL_PAUSE, "<StockItem>", GTK_STOCK_MEDIA_PAUSE},
+    {N_("/Stop"), "v", mainwin_general_menu_callback,
+     MAINWIN_GENERAL_STOP, "<StockItem>", GTK_STOCK_MEDIA_STOP},
+    {N_("/Previous"), "z", mainwin_general_menu_callback,
+     MAINWIN_GENERAL_PREV, "<StockItem>", GTK_STOCK_MEDIA_PREVIOUS},
+    {N_("/Next"), "b", mainwin_general_menu_callback,
+     MAINWIN_GENERAL_NEXT, "<StockItem>", GTK_STOCK_MEDIA_NEXT},
+    {"/-", NULL, NULL, 0, "<Separator>", NULL},
+    {N_("/Jump to Playlist Start"), "<control>Z", mainwin_general_menu_callback,
+     MAINWIN_GENERAL_START, "<StockItem>", GTK_STOCK_GOTO_TOP},
+    {"/-", NULL, NULL, 0, "<Separator>", NULL},
+    {N_("/Jump to File"), "J", mainwin_general_menu_callback,
+     MAINWIN_GENERAL_JTF, "<StockItem>", GTK_STOCK_JUMP_TO},
+    {N_("/Jump to Time"), "<control>J", mainwin_general_menu_callback,
+     MAINWIN_GENERAL_JTT, "<StockItem>", GTK_STOCK_JUMP_TO},
+    {"/-", NULL, NULL, 0, "<Separator>", NULL},
+    {N_("/View Track Details"), "<alt>I", mainwin_general_menu_callback,
+     MAINWIN_GENERAL_FILEINFO, "<ImageItem>", my_pixbuf}
+};
+
+static const gint mainwin_playback_menu_entries_num =
+    G_N_ELEMENTS(mainwin_playback_menu_entries);
+
+/* Main menu */
+
+GtkItemFactoryEntry mainwin_general_menu_entries[] = {
+    {N_("/About Audacious"), NULL, mainwin_general_menu_callback,
+     MAINWIN_GENERAL_ABOUT, "<StockItem>", GTK_STOCK_DIALOG_INFO},
+    {"/-", NULL, NULL, 0, "<Separator>", NULL},
+    {N_("/Play File"), "L", mainwin_general_menu_callback,
+     MAINWIN_GENERAL_PLAYFILE, "<StockItem>", GTK_STOCK_OPEN},
+    {N_("/Play Location"), "<control>L", mainwin_general_menu_callback,
+     MAINWIN_GENERAL_PLAYLOCATION, "<StockItem>", GTK_STOCK_NETWORK},
+    {"/-", NULL, NULL, 0, "<Separator>", NULL},
+    {N_("/V_isualization"), NULL, NULL, 0, "<Item>", NULL},
+    {N_("/_Playback"), NULL, NULL, 0, "<Item>", NULL},
+    {N_("/_View"), NULL, NULL, 0, "<Item>", NULL},
+    {"/-", NULL, NULL, 0, "<Separator>", NULL},
+    {N_("/Preferences"), "<control>P", mainwin_general_menu_callback,
+     MAINWIN_GENERAL_PREFS, "<StockItem>", GTK_STOCK_PREFERENCES},
+    {N_("/_Quit"), NULL, mainwin_general_menu_callback,
+     MAINWIN_GENERAL_EXIT, "<StockItem>", GTK_STOCK_QUIT}
+};
+
+static const gint mainwin_general_menu_entries_num =
+    G_N_ELEMENTS(mainwin_general_menu_entries);
+
+/* Add submenu */
+
+GtkItemFactoryEntry mainwin_add_menu_entries[] = {
+    {N_("/Files..."), "f", mainwin_general_menu_callback,
+     MAINWIN_GENERAL_PLAYFILE, "<StockItem>", GTK_STOCK_OPEN},
+    {N_("/Internet location..."), "<control>h", mainwin_general_menu_callback,
+     MAINWIN_GENERAL_PLAYLOCATION, "<StockItem>", GTK_STOCK_NETWORK},
+};
+
+static const gint mainwin_add_menu_entries_num =
+    G_N_ELEMENTS(mainwin_add_menu_entries);
+
+/* View submenu */
+
+GtkItemFactoryEntry mainwin_view_menu_entries[] = {
+    {N_("/Show Player"), "<alt>M", mainwin_general_menu_callback,
+     MAINWIN_GENERAL_SHOWMWIN, "<ToggleItem>", NULL},
+    {N_("/Show Playlist Editor"), "<alt>E", mainwin_general_menu_callback,
+     MAINWIN_GENERAL_SHOWPLWIN, "<ToggleItem>", NULL},
+    {N_("/Show Equalizer"), "<alt>G", mainwin_general_menu_callback,
+     MAINWIN_GENERAL_SHOWEQWIN, "<ToggleItem>", NULL},
+    {"/-", NULL, NULL, 0, "<Separator>", NULL},
+    {N_("/Time Elapsed"), "<control>E", mainwin_view_menu_callback,
+     MAINWIN_OPT_TELAPSED, "<RadioItem>", NULL},
+    {N_("/Time Remaining"), "<control>R", mainwin_view_menu_callback,
+     MAINWIN_OPT_TREMAINING, "/Time Elapsed", NULL},
+    {"/-", NULL, NULL, 0, "<Separator>", NULL},
+    {N_("/Always On Top"), "<control>o", mainwin_view_menu_callback,
+     MAINWIN_OPT_ALWAYS, "<ToggleItem>", NULL},
+    {N_("/Put on All Workspaces"), "<control>S",
+     mainwin_view_menu_callback, MAINWIN_OPT_STICKY, "<ToggleItem>", NULL},
+    {"/-", NULL, NULL, 0, "<Separator>", NULL},
+    {N_("/Roll up Player"), "<control>W", mainwin_view_menu_callback,
+     MAINWIN_OPT_WS, "<ToggleItem>", NULL},
+    {N_("/Roll up Playlist Editor"), "<control><shift>W", mainwin_view_menu_callback,
+     MAINWIN_OPT_PWS, "<ToggleItem>", NULL},
+    {N_("/Roll up Equalizer"), "<control><alt>W", mainwin_view_menu_callback,
+     MAINWIN_OPT_EQWS, "<ToggleItem>", NULL}
+};
+
+static const gint mainwin_view_menu_entries_num =
+    G_N_ELEMENTS(mainwin_view_menu_entries);
+
+
+static PlaybackInfo playback_info = { NULL, 0, 0, 0 };
+
+
+static gint mainwin_idle_func(gpointer data);
+
+static void set_timer_mode_menu_cb(TimerMode mode);
+static void set_timer_mode(TimerMode mode);
+
+static void mainwin_refresh_hints(void);
+
+void mainwin_position_motion_cb(gint pos);
+void mainwin_position_release_cb(gint pos);
+
+
+/* FIXME: placed here for now */
+void
+playback_get_sample_params(gint * bitrate,
+                           gint * frequency,
+                           gint * n_channels)
+{
+    if (bitrate)
+        *bitrate = playback_info.bitrate;
+
+    if (frequency)
+        *frequency = playback_info.frequency;
+
+    if (n_channels)
+        *n_channels = playback_info.n_channels;
+}
+
+static void
+playback_set_sample_params(gint bitrate,
+                           gint frequency,
+                           gint n_channels)
+{
+    if (bitrate >= 0)
+        playback_info.bitrate = bitrate;
+
+    if (frequency >= 0)
+        playback_info.frequency = frequency;
+
+    if (n_channels >= 0)
+        playback_info.n_channels = n_channels;
+}
+
+static void
+mainwin_set_title_scroll(gboolean scroll)
+{
+    cfg.autoscroll = scroll;
+    textbox_set_scroll(mainwin_info, cfg.autoscroll);
+}
+
+
+void
+mainwin_set_always_on_top(gboolean always)
+{
+    GtkWidget *widget = gtk_item_factory_get_widget(mainwin_view_menu,
+                                                    "/Always On Top");
+    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget),
+                                   mainwin_menurow->mr_always_selected);
+}
+
+static void
+mainwin_set_shape_mask(void)
+{
+    GdkBitmap *mask;
+
+    if (!cfg.player_visible)
+        return;
+
+    mask = skin_get_mask(bmp_active_skin, SKIN_MASK_MAIN + cfg.player_shaded);
+    gtk_widget_shape_combine_mask(mainwin, mask, 0, 0);
+}
+
+static void
+mainwin_set_shade(gboolean shaded)
+{
+    GtkWidget *widget;
+    widget = gtk_item_factory_get_widget(mainwin_view_menu,
+                                         "/Roll up Player");
+    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), shaded);
+}
+
+static void
+mainwin_set_shade_menu_cb(gboolean shaded)
+{
+    cfg.player_shaded = shaded;
+
+    mainwin_set_shape_mask();
+
+    if (shaded) {
+        dock_shade(dock_window_list, GTK_WINDOW(mainwin),
+                   MAINWIN_SHADED_HEIGHT);
+
+        widget_show(WIDGET(mainwin_svis));
+        vis_clear_data(mainwin_vis);
+
+        widget_show(WIDGET(mainwin_srew));
+        widget_show(WIDGET(mainwin_splay));
+        widget_show(WIDGET(mainwin_spause));
+        widget_show(WIDGET(mainwin_sstop));
+        widget_show(WIDGET(mainwin_sfwd));
+        widget_show(WIDGET(mainwin_seject));
+
+        textbox_set_scroll(mainwin_info, FALSE);
+        if (bmp_playback_get_playing())
+		{
+            	widget_show(WIDGET(mainwin_sposition));
+	        widget_show(WIDGET(mainwin_stime_min));
+        	widget_show(WIDGET(mainwin_stime_sec));
+		}
+	else
+		{
+            	widget_hide(WIDGET(mainwin_sposition));
+	        widget_hide(WIDGET(mainwin_stime_min));
+        	widget_hide(WIDGET(mainwin_stime_sec));
+		}
+	
+        mainwin_shade->pb_ny = mainwin_shade->pb_py = 27;
+    }
+    else {
+        dock_shade(dock_window_list, GTK_WINDOW(mainwin), MAINWIN_HEIGHT);
+
+        widget_hide(WIDGET(mainwin_svis));
+        svis_clear_data(mainwin_svis);
+
+        widget_hide(WIDGET(mainwin_srew));
+        widget_hide(WIDGET(mainwin_splay));
+        widget_hide(WIDGET(mainwin_spause));
+        widget_hide(WIDGET(mainwin_sstop));
+        widget_hide(WIDGET(mainwin_sfwd));
+        widget_hide(WIDGET(mainwin_seject));
+
+        widget_hide(WIDGET(mainwin_stime_min));
+        widget_hide(WIDGET(mainwin_stime_sec));
+        widget_hide(WIDGET(mainwin_sposition));
+
+        textbox_set_scroll(mainwin_info, TRUE);
+        mainwin_shade->pb_ny = mainwin_shade->pb_py = 18;
+    }
+
+    draw_main_window(TRUE);
+}
+
+static void
+mainwin_vis_set_active_vis(gint new_vis)
+{
+    active_vis = mainwin_vis;
+}
+
+static void
+mainwin_vis_set_refresh(RefreshRate rate)
+{
+    cfg.vis_refresh = rate;
+}
+
+static void
+mainwin_vis_set_afalloff(FalloffSpeed speed)
+{
+    cfg.analyzer_falloff = speed;
+}
+
+static void
+mainwin_vis_set_pfalloff(FalloffSpeed speed)
+{
+    cfg.peaks_falloff = speed;
+}
+
+static void
+mainwin_vis_set_analyzer_mode(AnalyzerMode mode)
+{
+    cfg.analyzer_mode = mode;
+}
+
+static void
+mainwin_vis_set_analyzer_type(AnalyzerType mode)
+{
+    cfg.analyzer_type = mode;
+}
+
+void
+mainwin_vis_set_type(VisType mode)
+{
+    gchar *path =
+        mainwin_vis_menu_entries[MAINWIN_VIS_MENU_VIS_MODE + mode].path;
+    GtkWidget *widget = gtk_item_factory_get_widget(mainwin_vis_menu, path);
+    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), TRUE);
+}
+
+static void
+mainwin_vis_set_type_menu_cb(VisType mode)
+{
+    cfg.vis_type = mode;
+
+    if (mode == VIS_OFF) {
+        if (cfg.player_shaded && cfg.player_visible)
+            svis_clear(mainwin_svis);
+        else
+            vis_clear(active_vis);
+    }
+    if (mode == VIS_ANALYZER || mode == VIS_SCOPE) {
+        vis_clear_data(active_vis);
+        svis_clear_data(mainwin_svis);
+    }
+}
+
+static void
+mainwin_menubtn_cb(void)
+{
+    gint x, y;
+    gtk_window_get_position(GTK_WINDOW(mainwin), &x, &y);
+    util_item_factory_popup(mainwin_general_menu,
+                            x + 6,
+                            y + MAINWIN_SHADED_HEIGHT,
+                            1, GDK_CURRENT_TIME);
+}
+
+void
+mainwin_minimize_cb(void)
+{
+    if (!mainwin)
+        return;
+
+    gtk_window_iconify(GTK_WINDOW(mainwin));
+}
+
+static void
+mainwin_shade_toggle(void)
+{
+    mainwin_set_shade(!cfg.player_shaded);
+}
+
+void
+mainwin_quit_cb(void)
+{
+    gtk_widget_hide(equalizerwin);
+    gtk_widget_hide(playlistwin);
+    gtk_widget_hide(mainwin);
+    gdk_flush();
+
+    g_source_remove(mainwin_timeout_id);
+
+    util_set_cursor(NULL);
+
+    bmp_config_save();
+    gtk_accel_map_save(bmp_paths[BMP_PATH_ACCEL_FILE]);
+
+    ctrlsocket_cleanup();
+
+    playlist_stop_get_info_thread();
+    playlist_clear();
+
+    plugin_system_cleanup();
+    
+    gtk_main_quit();
+
+    exit(EXIT_SUCCESS);
+}
+
+static void
+mainwin_destroy(GtkWidget * widget, gpointer data)
+{
+    mainwin_quit_cb();
+}
+
+static void
+mainwin_draw_titlebar(gboolean focus)
+{
+    skin_draw_mainwin_titlebar(bmp_active_skin, mainwin_bg, mainwin_gc,
+                               cfg.player_shaded, focus || !cfg.dim_titlebar);
+}
+
+void
+draw_main_window(gboolean force)
+{
+    GList *wl;
+    Widget *w;
+    gboolean redraw;
+
+    if (!cfg.player_visible)
+        return;
+
+    if (force)
+        mainwin_refresh_hints();
+
+    widget_list_lock(mainwin_wlist);
+
+    if (force) {
+        if (!cfg.player_shaded)
+            skin_draw_pixmap(bmp_active_skin, mainwin_bg, mainwin_gc,
+                             SKIN_MAIN, 0, 0, 0, 0, MAINWIN_WIDTH,
+                             MAINWIN_HEIGHT);
+        mainwin_draw_titlebar(gtk_window_has_toplevel_focus
+                              (GTK_WINDOW(mainwin)));
+    }
+
+    widget_list_draw(mainwin_wlist, &redraw, force);
+
+    if (redraw || force) {
+        if (force) {
+            gdk_window_clear(mainwin->window);
+        }
+        else {
+            for (wl = mainwin_wlist; wl; wl = g_list_next(wl)) {
+                w = WIDGET(wl->data);
+
+                if (!w->redraw || !w->visible)
+                    continue;
+
+                gdk_window_clear_area(mainwin->window, w->x, w->y,
+                                      w->width, w->height);
+                w->redraw = FALSE;
+            }
+        }
+
+        gdk_flush();
+    }
+
+    widget_list_unlock(mainwin_wlist);
+}
+
+
+void
+mainwin_set_info_text(void)
+{
+    gchar *text;
+
+    if (mainwin_info_text_locked)
+        return;
+
+    if ((text = input_get_info_text()) != NULL) {
+        textbox_set_text(mainwin_info, text);
+        g_free(text);
+    }
+    else if ((text = playlist_get_info_text()) != NULL) {
+        textbox_set_text(mainwin_info, text);
+        g_free(text);
+    }
+}
+
+void
+mainwin_lock_info_text(const gchar * text)
+{
+    mainwin_info_text_locked = TRUE;
+    textbox_set_text(mainwin_info, text);
+}
+
+void
+mainwin_release_info_text(void)
+{
+    mainwin_info_text_locked = FALSE;
+    mainwin_set_info_text();
+}
+
+
+static gchar *
+make_mainwin_title(const gchar * title)
+{
+    if (title)
+        return g_strdup_printf(_("%s - Audacious"), title);
+    else
+        return g_strdup(_("Audacious"));
+}
+
+void
+mainwin_set_song_title(const gchar * title)
+{
+    G_LOCK(mainwin_title);
+    g_free(mainwin_title_text);
+    mainwin_title_text = make_mainwin_title(title);
+    G_UNLOCK(mainwin_title);
+}
+
+static void
+mainwin_refresh_hints(void)
+{
+    if (bmp_active_skin && bmp_active_skin->properties.mainwin_othertext
+	== TRUE)
+    {
+	widget_hide(WIDGET(mainwin_rate_text));
+	widget_hide(WIDGET(mainwin_freq_text));
+	widget_hide(WIDGET(mainwin_monostereo));
+	widget_show(WIDGET(mainwin_othertext));
+    }
+    else
+    {
+	widget_show(WIDGET(mainwin_rate_text));
+	widget_show(WIDGET(mainwin_freq_text));
+	widget_show(WIDGET(mainwin_monostereo));
+	widget_hide(WIDGET(mainwin_othertext));
+    }
+}
+
+void
+mainwin_set_song_info(gint bitrate,
+                      gint frequency,
+                      gint n_channels)
+{
+    gchar text[512];
+    gchar *title;
+
+    playback_set_sample_params(bitrate, frequency, n_channels);
+
+    if (bitrate != -1) {
+        bitrate /= 1000;
+
+        if (bitrate < 1000) {
+            /* Show bitrate in 1000s */
+            g_snprintf(text, sizeof(text), "%3d", bitrate);
+            textbox_set_text(mainwin_rate_text, text);
+        }
+        else {
+            /* Show bitrate in 100,000s */
+            g_snprintf(text, sizeof(text), "%2dH", bitrate / 100);
+            textbox_set_text(mainwin_rate_text, text);
+        }
+    }
+    else
+        textbox_set_text(mainwin_rate_text, _("VBR"));
+
+    /* Show sampling frequency in kHz */
+    g_snprintf(text, sizeof(text), "%2d", frequency / 1000);
+    textbox_set_text(mainwin_freq_text, text);
+
+    monostereo_set_num_channels(mainwin_monostereo, n_channels);
+
+    if (cfg.player_shaded)
+    {
+        widget_show(WIDGET(mainwin_stime_min));
+        widget_show(WIDGET(mainwin_stime_sec));
+    }
+
+    widget_show(WIDGET(mainwin_minus_num));
+    widget_show(WIDGET(mainwin_10min_num));
+    widget_show(WIDGET(mainwin_min_num));
+    widget_show(WIDGET(mainwin_10sec_num));
+    widget_show(WIDGET(mainwin_sec_num));
+
+    if (!bmp_playback_get_paused())
+        playstatus_set_status(mainwin_playstatus, STATUS_PLAY);
+
+    if (playlist_get_current_length() != -1) {
+        if (cfg.player_shaded)
+            widget_show(WIDGET(mainwin_sposition));
+        widget_show(WIDGET(mainwin_position));
+    }
+    else {
+        widget_hide(WIDGET(mainwin_position));
+        widget_hide(WIDGET(mainwin_sposition));
+        mainwin_force_redraw = TRUE;
+    }
+
+    if (bmp_active_skin && bmp_active_skin->properties.mainwin_othertext 
+	== TRUE)
+    {
+        if (bitrate != -1)
+            g_snprintf(text, 512, "%d kbps, %0.1f kHz, %s",
+            bitrate,
+            (gfloat) frequency / 1000,
+            (n_channels > 1) ? _("stereo") : _("mono"));
+        else
+            g_snprintf(text, 512, "VBR, %0.1f kHz, %s",
+            (gfloat) frequency / 1000,
+            (n_channels > 1) ? _("stereo") : _("mono"));
+
+        textbox_set_text(mainwin_othertext, text);
+
+        widget_hide(WIDGET(mainwin_rate_text));
+        widget_hide(WIDGET(mainwin_freq_text));
+        widget_hide(WIDGET(mainwin_monostereo));
+        widget_show(WIDGET(mainwin_othertext));
+    }
+    else
+    {
+        widget_show(WIDGET(mainwin_rate_text));
+        widget_show(WIDGET(mainwin_freq_text));
+        widget_show(WIDGET(mainwin_monostereo));
+        widget_hide(WIDGET(mainwin_othertext));
+    }
+
+    title = playlist_get_info_text();
+    mainwin_set_song_title(title);
+    g_free(title);
+}
+
+void
+mainwin_clear_song_info(void)
+{
+    if (!mainwin)
+        return;
+
+    /* clear title */
+    G_LOCK(mainwin_title);
+    g_free(mainwin_title_text);
+    mainwin_title_text = NULL;
+    G_UNLOCK(mainwin_title);
+
+    /* clear sampling parameters */
+    playback_set_sample_params(0, 0, 0);
+
+    mainwin_position->hs_pressed = FALSE;
+    mainwin_sposition->hs_pressed = FALSE;
+
+    /* clear sampling parameter displays */
+    textbox_set_text(mainwin_rate_text, "   ");
+    textbox_set_text(mainwin_freq_text, "  ");
+    monostereo_set_num_channels(mainwin_monostereo, 0);
+
+    playstatus_set_status(mainwin_playstatus, STATUS_STOP);
+
+    /* hide playback time */
+    widget_hide(WIDGET(mainwin_minus_num));
+    widget_hide(WIDGET(mainwin_10min_num));
+    widget_hide(WIDGET(mainwin_min_num));
+    widget_hide(WIDGET(mainwin_10sec_num));
+    widget_hide(WIDGET(mainwin_sec_num));
+
+    widget_hide(WIDGET(mainwin_stime_min));
+    widget_hide(WIDGET(mainwin_stime_sec));
+
+    widget_hide(WIDGET(mainwin_position));
+    widget_hide(WIDGET(mainwin_sposition));
+
+    widget_hide(WIDGET(mainwin_othertext));
+
+    playlistwin_hide_timer();
+    draw_main_window(TRUE);
+
+    vis_clear(active_vis);
+}
+
+void
+mainwin_disable_seekbar(void)
+{
+    if (!mainwin)
+        return;
+
+    /*
+     * We dont call draw_main_window() here so this will not
+     * remove them visually.  It will only prevent us from sending
+     * any seek calls to the input plugin before the input plugin
+     * calls ->set_info().
+     */
+    widget_hide(WIDGET(mainwin_position));
+    widget_hide(WIDGET(mainwin_sposition));
+}
+
+static gboolean
+mainwin_mouse_button_release(GtkWidget * widget,
+                             GdkEventButton * event,
+                             gpointer callback_data)
+{
+    gdk_pointer_ungrab(GDK_CURRENT_TIME);
+
+    /*
+     * The gdk_flush() is just for making sure that the pointer really
+     * gets ungrabbed before calling any button callbacks
+     *
+     */
+
+    gdk_flush();
+
+    if (dock_is_moving(GTK_WINDOW(mainwin))) {
+        dock_move_release(GTK_WINDOW(mainwin));
+		draw_playlist_window(TRUE);
+    }
+
+    if (mainwin_menurow->mr_doublesize_selected) {
+        event->x /= 2;
+        event->y /= 2;
+    }
+
+    handle_release_cb(mainwin_wlist, widget, event);
+
+    draw_main_window(FALSE);
+
+    return FALSE;
+}
+
+static gboolean
+mainwin_motion(GtkWidget * widget,
+               GdkEventMotion * event,
+               gpointer callback_data)
+{
+    int x, y;
+    GdkModifierType state;
+
+    if (event->is_hint != FALSE)
+    {
+        gdk_window_get_pointer(GDK_WINDOW(mainwin->window),
+		&x, &y, &state);
+
+	/* If it's a hint, we had to query X, so override the 
+         * information we we're given... it's probably useless... --nenolod
+	 */
+        event->x = x;
+        event->y = y;
+        event->state = state;
+    }
+    else
+    {
+        x = event->x;
+        y = event->y;
+        state = event->state;
+    }
+
+    if (dock_is_moving(GTK_WINDOW(mainwin))) {
+        dock_move_motion(GTK_WINDOW(mainwin), event);
+    }
+    else {
+        handle_motion_cb(mainwin_wlist, widget, event);
+        draw_main_window(FALSE);
+    }
+
+    gdk_flush();
+
+    return FALSE;
+}
+
+static gboolean
+inside_sensitive_widgets(gint x, gint y)
+{
+    return (widget_contains(WIDGET(mainwin_menubtn), x, y)
+            || widget_contains(WIDGET(mainwin_minimize), x, y)
+            || widget_contains(WIDGET(mainwin_shade), x, y)
+            || widget_contains(WIDGET(mainwin_close), x, y)
+            || widget_contains(WIDGET(mainwin_rew), x, y)
+            || widget_contains(WIDGET(mainwin_play), x, y)
+            || widget_contains(WIDGET(mainwin_pause), x, y)
+            || widget_contains(WIDGET(mainwin_stop), x, y)
+            || widget_contains(WIDGET(mainwin_fwd), x, y)
+            || widget_contains(WIDGET(mainwin_eject), x, y)
+            || widget_contains(WIDGET(mainwin_shuffle), x, y)
+            || widget_contains(WIDGET(mainwin_repeat), x, y)
+            || widget_contains(WIDGET(mainwin_pl), x, y)
+            || widget_contains(WIDGET(mainwin_eq), x, y)
+            || widget_contains(WIDGET(mainwin_info), x, y)
+            || widget_contains(WIDGET(mainwin_menurow), x, y)
+            || widget_contains(WIDGET(mainwin_volume), x, y)
+            || widget_contains(WIDGET(mainwin_balance), x, y)
+            || (widget_contains(WIDGET(mainwin_position), x, y) &&
+                widget_is_visible(WIDGET(mainwin_position)))
+            || widget_contains(WIDGET(mainwin_minus_num), x, y)
+            || widget_contains(WIDGET(mainwin_10min_num), x, y)
+            || widget_contains(WIDGET(mainwin_min_num), x, y)
+            || widget_contains(WIDGET(mainwin_10sec_num), x, y)
+            || widget_contains(WIDGET(mainwin_sec_num), x, y)
+            || widget_contains(WIDGET(mainwin_vis), x, y)
+            || widget_contains(WIDGET(mainwin_minimize), x, y)
+            || widget_contains(WIDGET(mainwin_shade), x, y)
+            || widget_contains(WIDGET(mainwin_close), x, y)
+            || widget_contains(WIDGET(mainwin_menubtn), x, y)
+            || widget_contains(WIDGET(mainwin_sposition), x, y)
+            || widget_contains(WIDGET(mainwin_stime_min), x, y)
+            || widget_contains(WIDGET(mainwin_stime_sec), x, y)
+            || widget_contains(WIDGET(mainwin_srew), x, y)
+            || widget_contains(WIDGET(mainwin_splay), x, y)
+            || widget_contains(WIDGET(mainwin_spause), x, y)
+            || widget_contains(WIDGET(mainwin_sstop), x, y)
+            || widget_contains(WIDGET(mainwin_sfwd), x, y)
+            || widget_contains(WIDGET(mainwin_seject), x, y)
+            || widget_contains(WIDGET(mainwin_svis), x, y)
+            || widget_contains(WIDGET(mainwin_about), x, y));
+}
+
+void
+mainwin_scrolled(GtkWidget * widget,
+                 GdkEventScroll * event,
+                 gpointer callback_data)
+{
+    gint d = cfg.mouse_change;
+    if (event->direction == GDK_SCROLL_DOWN)
+        d *= -1;
+    mainwin_set_volume_diff(d);
+}
+
+
+static gboolean
+mainwin_mouse_button_press(GtkWidget * widget,
+                           GdkEventButton * event,
+                           gpointer callback_data)
+{
+
+    gboolean grab = TRUE;
+
+    if (event->button == 1 && event->type == GDK_BUTTON_PRESS &&
+        !inside_sensitive_widgets(event->x, event->y) && event->y < 14) {
+        if (0 && hint_move_resize_available()) {
+            hint_move_resize(mainwin, event->x_root, event->y_root, TRUE);
+            grab = FALSE;
+        }
+        else {
+            gtk_window_present(GTK_WINDOW(mainwin));
+            dock_move_press(dock_window_list, GTK_WINDOW(mainwin), event,
+                            TRUE);
+        }
+    }
+    else if (event->button == 1 && event->type == GDK_2BUTTON_PRESS &&
+             event->y < 14 && !inside_sensitive_widgets(event->x, event->y)) {
+        mainwin_set_shade(!cfg.player_shaded);
+        if (dock_is_moving(GTK_WINDOW(mainwin)))
+            dock_move_release(GTK_WINDOW(mainwin));
+    }
+    else if (event->button == 1 && event->type == GDK_2BUTTON_PRESS &&
+             widget_contains(WIDGET(mainwin_info), event->x, event->y)) {
+        playlist_fileinfo_current();
+    }
+    else {
+        handle_press_cb(mainwin_wlist, widget, event);
+        draw_main_window(FALSE);
+    }
+
+    if ((event->button == 1) && event->type != GDK_2BUTTON_PRESS &&
+        (widget_contains(WIDGET(mainwin_vis), event->x, event->y) ||
+         widget_contains(WIDGET(mainwin_svis), event->x, event->y))) {
+
+        cfg.vis_type++;
+
+        if (cfg.vis_type > VIS_OFF)
+            cfg.vis_type = VIS_ANALYZER;
+
+        mainwin_vis_set_type(cfg.vis_type);
+    }
+
+    if (event->button == 3) {
+        if (widget_contains(WIDGET(mainwin_info), event->x, event->y)) {
+            util_item_factory_popup(mainwin_songname_menu,
+                                    event->x_root, event->y_root,
+                                    3, event->time);
+            grab = FALSE;
+        }
+        else if (widget_contains(WIDGET(mainwin_vis), event->x, event->y) ||
+                 widget_contains(WIDGET(mainwin_svis), event->x, event->y)) {
+            util_item_factory_popup(mainwin_vis_menu, event->x_root,
+                                    event->y_root, 3, event->time);
+            grab = FALSE;
+        }
+        else if ( (event->y > 70) && (event->x < 128) )
+        {
+
+            util_item_factory_popup(mainwin_play_menu,
+                                    event->x_root,
+                                    event->y_root, 3, event->time);
+            grab = FALSE;
+        } else {
+            /*
+             * Pop up the main menu a few pixels down.
+             * This will avoid that anything is selected
+             * if one right-clicks to focus the window
+             * without raising it.
+             *
+             ***MD I think the above is stupid, people don't expect this
+             *
+             */
+            util_item_factory_popup(mainwin_general_menu,
+                                    event->x_root,
+                                    event->y_root, 3, event->time);
+            grab = FALSE;
+        }
+    }
+    if (event->button == 1) {
+        if ((event->x > 35 && event->x < 100 &&
+             event->y > 25 && event->y < 40) ||
+            widget_contains(WIDGET(mainwin_stime_min), event->x, event->y) ||
+            widget_contains(WIDGET(mainwin_stime_sec), event->x, event->y)) {
+
+            if (cfg.timer_mode == TIMER_ELAPSED)
+                set_timer_mode(TIMER_REMAINING);
+            else
+                set_timer_mode(TIMER_ELAPSED);
+        }
+
+    }
+
+    if (grab)
+        gdk_pointer_grab(mainwin->window, FALSE,
+                         GDK_BUTTON_MOTION_MASK |
+                         GDK_BUTTON_RELEASE_MASK,
+                         GDK_WINDOW(GDK_NONE), NULL, GDK_CURRENT_TIME);
+
+    return FALSE;
+}
+
+static gboolean
+mainwin_focus_in(GtkWidget * window,
+                 GdkEventFocus * event,
+                 gpointer data)
+{
+    mainwin_menubtn->pb_allow_draw = TRUE;
+    mainwin_minimize->pb_allow_draw = TRUE;
+    mainwin_shade->pb_allow_draw = TRUE;
+    mainwin_close->pb_allow_draw = TRUE;
+    draw_main_window(TRUE);
+
+    return TRUE;
+}
+
+
+static gboolean
+mainwin_focus_out(GtkWidget * widget,
+                  GdkEventFocus * event,
+                  gpointer callback_data)
+{
+    mainwin_menubtn->pb_allow_draw = FALSE;
+    mainwin_minimize->pb_allow_draw = FALSE;
+    mainwin_shade->pb_allow_draw = FALSE;
+    mainwin_close->pb_allow_draw = FALSE;
+    draw_main_window(TRUE);
+
+    return TRUE;
+}
+
+static gboolean
+mainwin_keypress(GtkWidget * grab_widget,
+                 GdkEventKey * event,
+                 gpointer data)
+{
+
+    switch (event->keyval) {
+
+    case GDK_Up:
+    case GDK_KP_Up:
+    case GDK_KP_8:
+        mainwin_set_volume_diff(2);
+        break;
+    case GDK_Down:
+    case GDK_KP_Down:
+    case GDK_KP_2:
+        mainwin_set_volume_diff(-2);
+        break;
+    case GDK_Left:
+    case GDK_KP_Left:
+    case GDK_KP_7:
+        if (playlist_get_current_length() != -1)
+            bmp_playback_seek(CLAMP
+                              (bmp_playback_get_time() - 1000, 0,
+                               playlist_get_current_length()) / 1000);
+        break;
+    case GDK_Right:
+    case GDK_KP_Right:
+    case GDK_KP_9:
+        if (playlist_get_current_length() != -1)
+            bmp_playback_seek(CLAMP
+                              (bmp_playback_get_time() + 1000, 0,
+                               playlist_get_current_length()) / 1000);
+        break;
+    case GDK_KP_4:
+        playlist_prev();
+        break;
+    case GDK_KP_6:
+        playlist_next();
+        break;
+    case GDK_KP_Insert:
+        mainwin_jump_to_file();
+        break;
+    case GDK_KP_5:
+        mainwin_play_pushed();
+        break;
+    case GDK_Escape:
+        mainwin_minimize_cb();
+        break;
+    default:
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static void
+mainwin_jump_to_time_cb(GtkWidget * widget,
+                        GtkWidget * entry)
+{
+    guint min = 0, sec = 0, params;
+    gint time;
+
+    params = sscanf(gtk_entry_get_text(GTK_ENTRY(entry)), "%u:%u",
+                    &min, &sec);
+    if (params == 2)
+        time = (min * 60) + sec;
+    else if (params == 1)
+        time = min;
+    else
+        return;
+
+    if (playlist_get_current_length() > -1 &&
+        time <= (playlist_get_current_length() / 1000)) {
+        bmp_playback_seek(time);
+        gtk_widget_destroy(mainwin_jtt);
+    }
+}
+
+
+void
+mainwin_jump_to_time(void)
+{
+    GtkWidget *vbox, *hbox_new, *hbox_total;
+    GtkWidget *time_entry, *label, *bbox, *jump, *cancel;
+    guint tindex;
+    gchar time_str[10];
+
+    if (!bmp_playback_get_playing()) {
+        report_error("JIT can't be launched when no track is being played.\n");
+        return;
+    }
+
+    if (mainwin_jtt) {
+        gtk_window_present(GTK_WINDOW(mainwin_jtt));
+        return;
+    }
+
+    mainwin_jtt = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_type_hint(GTK_WINDOW(mainwin_jtt),
+                             GDK_WINDOW_TYPE_HINT_DIALOG);
+
+    gtk_window_set_title(GTK_WINDOW(mainwin_jtt), _("Jump to Time"));
+    gtk_window_set_position(GTK_WINDOW(mainwin_jtt), GTK_WIN_POS_CENTER);
+    gtk_window_set_transient_for(GTK_WINDOW(mainwin_jtt),
+                                 GTK_WINDOW(mainwin));
+
+    g_signal_connect(mainwin_jtt, "destroy",
+                     G_CALLBACK(gtk_widget_destroyed), &mainwin_jtt);
+    gtk_container_border_width(GTK_CONTAINER(mainwin_jtt), 10);
+
+    vbox = gtk_vbox_new(FALSE, 5);
+    gtk_container_add(GTK_CONTAINER(mainwin_jtt), vbox);
+
+    hbox_new = gtk_hbox_new(FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(vbox), hbox_new, TRUE, TRUE, 5);
+
+    time_entry = gtk_entry_new();
+    gtk_box_pack_start(GTK_BOX(hbox_new), time_entry, FALSE, FALSE, 5);
+    g_signal_connect(time_entry, "activate",
+                     G_CALLBACK(mainwin_jump_to_time_cb), time_entry);
+
+    gtk_widget_set_size_request(time_entry, 70, -1);
+    label = gtk_label_new(_("minutes:seconds"));
+    gtk_box_pack_start(GTK_BOX(hbox_new), label, FALSE, FALSE, 5);
+
+    hbox_total = gtk_hbox_new(FALSE, 0);
+    gtk_box_pack_start(GTK_BOX(vbox), hbox_total, TRUE, TRUE, 5);
+    gtk_widget_show(hbox_total);
+
+    /* FIXME: Disable display of current track length. It's not
+       updated when track changes */
+#if 0
+    label = gtk_label_new(_("Track length:"));
+    gtk_box_pack_start(GTK_BOX(hbox_total), label, FALSE, FALSE, 5);
+
+    len = playlist_get_current_length() / 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);
+    gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
+    gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
+
+    cancel = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
+    GTK_WIDGET_SET_FLAGS(cancel, GTK_CAN_DEFAULT);
+    gtk_container_add(GTK_CONTAINER(bbox), cancel);
+    g_signal_connect_swapped(cancel, "clicked",
+                             G_CALLBACK(gtk_widget_destroy), mainwin_jtt);
+
+    jump = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
+    GTK_WIDGET_SET_FLAGS(jump, GTK_CAN_DEFAULT);
+    gtk_container_add(GTK_CONTAINER(bbox), jump);
+    g_signal_connect(jump, "clicked",
+                     G_CALLBACK(mainwin_jump_to_time_cb), time_entry);
+
+    tindex = bmp_playback_get_time() / 1000;
+    g_snprintf(time_str, sizeof(time_str), "%u:%2.2u", tindex / 60,
+               tindex % 60);
+    gtk_entry_set_text(GTK_ENTRY(time_entry), time_str);
+
+    gtk_entry_select_region(GTK_ENTRY(time_entry), 0, strlen(time_str));
+
+    gtk_widget_show_all(mainwin_jtt);
+
+    gtk_widget_grab_focus(time_entry);
+    gtk_widget_grab_default(jump);
+}
+
+static void
+change_song(guint pos)
+{
+    if (bmp_playback_get_playing())
+        bmp_playback_stop();
+
+    playlist_set_position(pos);
+    bmp_playback_initiate();
+}
+
+static void
+mainwin_jump_to_file_jump(GtkTreeView * treeview)
+{
+    GtkTreeModel *model;
+    GtkTreeSelection *selection;
+    GtkTreeIter iter;
+    gchar *pos_str;
+    guint pos;
+
+    model = gtk_tree_view_get_model(treeview);
+    selection = gtk_tree_view_get_selection(treeview);
+
+    if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
+        return;
+
+    gtk_tree_model_get(model, &iter, 0, &pos_str, -1);
+    pos = g_ascii_strtoull(pos_str, NULL, 10) - 1;
+    g_free(pos_str);
+
+    change_song(pos);
+
+    /* FIXME: should only hide window */
+    gtk_widget_destroy(mainwin_jtf);
+    mainwin_jtf = NULL;
+}
+
+static void
+mainwin_jump_to_file_jump_cb(GtkTreeView * treeview,
+                             gpointer data)
+{
+    mainwin_jump_to_file_jump(treeview);
+}
+
+static void
+mainwin_jump_to_file_set_queue_button_label(GtkButton * button,
+                                      guint pos)
+{
+    if (playlist_is_position_queued(pos))
+        gtk_button_set_label(button, _("Un_queue"));
+    else
+        gtk_button_set_label(button, _("_Queue"));
+}
+
+static void
+mainwin_jump_to_file_queue_cb(GtkButton * button,
+                              gpointer data)
+{
+    GtkTreeView *treeview;
+    GtkTreeModel *model;
+    GtkTreeSelection *selection;
+    GtkTreeIter iter;
+    gchar *pos_str;
+    guint pos;
+
+    treeview = GTK_TREE_VIEW(data);
+    model = gtk_tree_view_get_model(treeview);
+    selection = gtk_tree_view_get_selection(treeview);
+
+    if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
+        return;
+
+    gtk_tree_model_get(model, &iter, 0, &pos_str, -1);
+    pos = g_ascii_strtoull(pos_str, NULL, 10) - 1;
+
+    playlist_queue_position(pos);
+
+    mainwin_jump_to_file_set_queue_button_label(button, pos);
+}
+
+static void
+mainwin_jump_to_file_selection_changed_cb(GtkTreeSelection *treesel,
+                                          gpointer data)
+{
+    GtkTreeView *treeview;
+    GtkTreeModel *model;
+    GtkTreeSelection *selection;
+    GtkTreeIter iter;
+    gchar *pos_str;
+    guint pos;
+
+    treeview = gtk_tree_selection_get_tree_view(treesel);
+    model = gtk_tree_view_get_model(treeview);
+    selection = gtk_tree_view_get_selection(treeview);
+
+    if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
+        return;
+
+    gtk_tree_model_get(model, &iter, 0, &pos_str, -1);
+    pos = g_ascii_strtoull(pos_str, NULL, 10) - 1;
+    g_free(pos_str);
+
+    mainwin_jump_to_file_set_queue_button_label(GTK_BUTTON(data), pos);
+}
+
+static gboolean
+mainwin_jump_to_file_edit_keypress_cb(GtkWidget * object,
+			     GdkEventKey * event,
+			     gpointer data)
+{
+	switch (event->keyval) {
+	case GDK_Return:
+		if (gtk_im_context_filter_keypress (GTK_ENTRY (object)->im_context, event)) {
+			GTK_ENTRY (object)->need_im_reset = TRUE;
+			return TRUE;
+		} else {
+			mainwin_jump_to_file_jump(GTK_TREE_VIEW(data));
+			return TRUE;
+		}
+	default:
+		return FALSE;
+	}
+}
+
+static gboolean
+mainwin_jump_to_file_keypress_cb(GtkWidget * object,
+                                 GdkEventKey * event,
+                                 gpointer data)
+{
+    switch (event->keyval) {
+    case GDK_Escape:
+        /* FIXME: show only hide window */
+        gtk_widget_destroy(mainwin_jtf);
+        mainwin_jtf = NULL;
+        return TRUE;
+    default:
+        return FALSE;
+    };
+}
+
+static gboolean
+mainwin_jump_to_file_match(const gchar * song, gchar ** keys)
+{
+    gint i = 0;
+    gchar *key;
+    gchar *song_lc;
+
+    song_lc = g_ascii_strdown(song, -1);
+
+    while (keys[i]) {
+        key = g_ascii_strdown(keys[i], -1);
+        if (!g_strrstr(song_lc, key)) {
+            g_free(key);
+            g_free(song_lc);
+            return FALSE;
+        }
+
+        g_free(key);
+        i++;
+    }
+
+    g_free(song_lc);
+
+    return TRUE;
+}
+
+/* FIXME: Clear the entry when the list gets updated */
+static void
+mainwin_update_jtf(GtkWidget * widget, gpointer user_data)
+{
+    /* FIXME: Is not in sync with playlist due to delayed extinfo
+     * reading */
+    gint row;
+    GList *playlist;
+    gchar *desc_buf = NULL;
+    gchar *row_str;
+    GtkTreeIter iter;
+    GtkTreeSelection *selection;
+
+    GtkTreeModel *store;
+
+    if (!mainwin_jtf)
+        return;
+
+    store = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data));
+    gtk_list_store_clear(GTK_LIST_STORE(store));
+
+    row = 1;
+    for (playlist = playlist_get(); playlist;
+         playlist = g_list_next(playlist)) {
+        PlaylistEntry *entry = PLAYLIST_ENTRY(playlist->data);
+
+        if (entry->title)
+		desc_buf = g_strdup(entry->title);
+        else if (strchr(entry->filename, '/'))
+		desc_buf = str_to_utf8(strrchr(entry->filename, '/') + 1);
+        else
+		desc_buf = str_to_utf8(entry->filename);
+
+        row_str = g_strdup_printf("%d", row++);
+
+        gtk_list_store_append(GTK_LIST_STORE(store), &iter);
+        gtk_list_store_set(GTK_LIST_STORE(store), &iter,
+                           0, row_str, 1, desc_buf, -1);
+
+	if(desc_buf) {
+		g_free(desc_buf);
+		desc_buf = NULL;
+	}
+
+        g_free(row_str);
+    }
+
+    gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter);
+    selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(user_data));
+    gtk_tree_selection_select_iter(selection, &iter);
+}
+
+static void
+mainwin_jump_to_file_edit_cb(GtkEntry * entry, gpointer user_data)
+{
+    GtkTreeView *treeview = GTK_TREE_VIEW(user_data);
+    GtkTreeSelection *selection;
+    GtkTreeIter iter;
+
+    GtkListStore *store;
+
+    gint song_index = 0;
+    gchar **words;
+    GList *playlist;
+
+    gboolean match = FALSE;
+
+    /* Chop the key string into ' '-separated key words */
+    words = g_strsplit(gtk_entry_get_text(entry), " ", 0);
+
+    /* FIXME: Remove the connected signals before clearing
+     * (row-selected will still eventually arrive once) */
+    store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview));
+    gtk_list_store_clear(store);
+
+    PLAYLIST_LOCK();
+
+    for (playlist = playlist_get(); playlist;
+         playlist = g_list_next(playlist)) {
+
+        PlaylistEntry *entry = PLAYLIST_ENTRY(playlist->data);
+        const gchar *title;
+        gchar *filename = NULL;
+
+        title = entry->title;
+        if (!title) {
+		filename = str_to_utf8(entry->filename);
+
+            if (strchr(filename, '/'))
+                title = strrchr(filename, '/') + 1;
+            else
+                title = filename;
+        }
+
+        /* Compare the key words to the string - if all the words
+           match, add to the ListStore */
+
+        /*
+         * FIXME: The search string should be adapted to the
+         * current display setting, e.g. if the user has set it to
+         * "%p - %t" then build the match string like that too, or
+         * even better, search for each of the tags seperatly.
+         *
+         * In any case the string to match should _never_ contain
+         * something the user can't actually see in the playlist.
+         */
+        if (words[0])
+            match = mainwin_jump_to_file_match(title, words);
+        else
+            match = TRUE;
+
+        if (match) {
+            gchar *song_index_str = g_strdup_printf("%d", song_index + 1);
+            gtk_list_store_append(store, &iter);
+            gtk_list_store_set(store, &iter, 0, song_index_str, 1, title, -1);
+            g_free(song_index_str);
+        }
+
+        song_index++;
+	if (filename) {
+		g_free(filename);
+		filename = NULL;
+	}
+    }
+
+    PLAYLIST_UNLOCK();
+
+    g_strfreev(words);
+
+    if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) {
+        selection = gtk_tree_view_get_selection(treeview);
+        gtk_tree_selection_select_iter(selection, &iter);
+    }
+}
+
+void
+mainwin_jump_to_file(void)
+{
+    GtkWidget *scrollwin;
+    GtkWidget *vbox, *bbox, *sep;
+    GtkWidget *jump, *queue, *cancel;
+    GtkWidget *rescan, *edit;
+    GtkWidget *search_label, *hbox;
+    GList *playlist;
+    gchar *desc_buf = NULL;
+    gchar *row_str;
+    gint row;
+
+    GtkWidget *treeview;
+    GtkListStore *jtf_store;
+
+    GtkTreeIter iter;
+    GtkCellRenderer *renderer;
+    GtkTreeViewColumn *column;
+
+    if (mainwin_jtf) {
+        gtk_window_present(GTK_WINDOW(mainwin_jtf));
+        return;
+    }
+
+    mainwin_jtf = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_type_hint(GTK_WINDOW(mainwin_jtf),
+                             GDK_WINDOW_TYPE_HINT_DIALOG);
+
+    gtk_window_set_title(GTK_WINDOW(mainwin_jtf), _("Jump to Track"));
+
+    gtk_window_set_position(GTK_WINDOW(mainwin_jtf), GTK_WIN_POS_CENTER);
+    g_signal_connect(mainwin_jtf, "destroy",
+                     G_CALLBACK(gtk_widget_destroyed), &mainwin_jtf);
+
+    gtk_container_border_width(GTK_CONTAINER(mainwin_jtf), 10);
+    gtk_window_set_default_size(GTK_WINDOW(mainwin_jtf), 550, 350);
+
+    vbox = gtk_vbox_new(FALSE, 5);
+    gtk_container_add(GTK_CONTAINER(mainwin_jtf), vbox);
+
+    jtf_store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
+    treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(jtf_store));
+    g_object_unref(jtf_store);
+
+    gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE);
+
+    column = gtk_tree_view_column_new();
+    gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE);
+    gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
+
+    renderer = gtk_cell_renderer_text_new();
+    gtk_tree_view_column_pack_start(column, renderer, FALSE);
+    gtk_tree_view_column_set_attributes(column, renderer, "text", 0, NULL);
+    gtk_tree_view_column_set_spacing(column, 4);
+
+    renderer = gtk_cell_renderer_text_new();
+    gtk_tree_view_column_pack_start(column, renderer, FALSE);
+    gtk_tree_view_column_set_attributes(column, renderer, "text", 1, NULL);
+    gtk_tree_view_column_set_spacing(column, 4);
+    gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
+
+    gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), 1);
+
+    g_signal_connect(treeview, "row-activated",
+                     G_CALLBACK(mainwin_jump_to_file_jump), NULL);
+
+    hbox = gtk_hbox_new(FALSE, 3);
+    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
+
+    search_label = gtk_label_new(_("Filter: "));
+    gtk_label_set_markup_with_mnemonic(GTK_LABEL(search_label), "_Filter:");
+    gtk_box_pack_start(GTK_BOX(hbox), search_label, FALSE, FALSE, 0);
+
+    edit = gtk_entry_new();
+    gtk_entry_set_editable(GTK_ENTRY(edit), TRUE);
+    gtk_label_set_mnemonic_widget(GTK_LABEL(search_label), edit);
+    g_signal_connect(edit, "changed",
+                     G_CALLBACK(mainwin_jump_to_file_edit_cb), treeview);
+
+    g_signal_connect(edit, "key_press_event",
+                     G_CALLBACK(mainwin_jump_to_file_edit_keypress_cb), treeview);
+
+    g_signal_connect(mainwin_jtf, "key_press_event",
+                     G_CALLBACK(mainwin_jump_to_file_keypress_cb), treeview);
+
+    gtk_box_pack_start(GTK_BOX(hbox), edit, TRUE, TRUE, 3);
+
+    scrollwin = gtk_scrolled_window_new(NULL, NULL);
+    gtk_container_add(GTK_CONTAINER(scrollwin), treeview);
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin),
+                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+    gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrollwin),
+                                        GTK_SHADOW_IN);
+    gtk_box_pack_start(GTK_BOX(vbox), scrollwin, TRUE, TRUE, 0);
+
+    sep = gtk_hseparator_new();
+    gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0);
+
+    bbox = gtk_hbutton_box_new();
+    gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
+    gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
+    gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0);
+
+    queue = gtk_button_new_with_mnemonic(_("_Queue"));
+    gtk_box_pack_start(GTK_BOX(bbox), queue, FALSE, FALSE, 0);
+    GTK_WIDGET_SET_FLAGS(queue, GTK_CAN_DEFAULT);
+    g_signal_connect(queue, "clicked", 
+                     G_CALLBACK(mainwin_jump_to_file_queue_cb),
+                     treeview);
+    g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), "changed",
+                     G_CALLBACK(mainwin_jump_to_file_selection_changed_cb),
+                     queue);
+
+    rescan = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
+    gtk_box_pack_start(GTK_BOX(bbox), rescan, FALSE, FALSE, 0);
+    g_signal_connect(rescan, "clicked",
+                     G_CALLBACK(mainwin_update_jtf), treeview);
+    GTK_WIDGET_SET_FLAGS(rescan, GTK_CAN_DEFAULT);
+    gtk_widget_grab_default(rescan);
+
+    jump = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
+    gtk_box_pack_start(GTK_BOX(bbox), jump, FALSE, FALSE, 0);
+
+    g_signal_connect_swapped(jump, "clicked",
+                             G_CALLBACK(mainwin_jump_to_file_jump_cb),
+                             treeview);
+
+    GTK_WIDGET_SET_FLAGS(jump, GTK_CAN_DEFAULT);
+    gtk_widget_grab_default(jump);
+
+    cancel = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
+    gtk_box_pack_start(GTK_BOX(bbox), cancel, FALSE, FALSE, 0);
+    g_signal_connect_swapped(cancel, "clicked",
+                             G_CALLBACK(gtk_widget_destroy),
+                             mainwin_jtf);
+    GTK_WIDGET_SET_FLAGS(cancel, GTK_CAN_DEFAULT);
+
+    gtk_list_store_clear(jtf_store);
+
+    row = 1;
+
+    PLAYLIST_LOCK();
+
+    for (playlist = playlist_get(); playlist;
+         playlist = g_list_next(playlist)) {
+
+        PlaylistEntry *entry = PLAYLIST_ENTRY(playlist->data);
+
+        if (entry->title)
+		desc_buf = g_strdup(entry->title);
+        else if (strchr(entry->filename, '/'))
+		desc_buf = str_to_utf8(strrchr(entry->filename, '/') + 1);
+        else
+		desc_buf = str_to_utf8(entry->filename);
+
+        row_str = g_strdup_printf("%d", row++);
+
+        gtk_list_store_append(GTK_LIST_STORE(jtf_store), &iter);
+        gtk_list_store_set(GTK_LIST_STORE(jtf_store), &iter,
+                           0, row_str, 1, desc_buf, -1);
+
+	if (desc_buf) {
+		g_free(desc_buf);
+		desc_buf = NULL;
+	}
+        g_free(row_str);
+    }
+
+    PLAYLIST_UNLOCK();
+
+    gtk_widget_show_all(mainwin_jtf);
+}
+
+static gboolean
+mainwin_configure(GtkWidget * window,
+                  GdkEventConfigure * event,
+                  gpointer data)
+{
+    if (!GTK_WIDGET_VISIBLE(window))
+        return FALSE;
+
+    if (cfg.show_wm_decorations)
+        gdk_window_get_root_origin(window->window,
+                                   &cfg.player_x, &cfg.player_y);
+    else
+        gdk_window_get_deskrelative_origin(window->window,
+                                           &cfg.player_x, &cfg.player_y);
+    return FALSE;
+}
+
+void
+mainwin_set_back_pixmap(void)
+{
+    gdk_window_set_back_pixmap(mainwin->window, mainwin_bg, 0);
+    gdk_window_clear(mainwin->window);
+}
+
+void
+mainwin_drag_data_received(GtkWidget * widget,
+                           GdkDragContext * context,
+                           gint x,
+                           gint y,
+                           GtkSelectionData * selection_data,
+                           guint info,
+                           guint time,
+                           gpointer user_data)
+{
+    gchar **iter, **sourcelist, *path;
+    gchar *decoded;
+    gboolean not_font = FALSE;
+    
+    if (!selection_data->data)
+    {
+        g_warning("DND data string is NULL");
+        return;
+    }
+
+    iter = sourcelist = g_strsplit((gchar *)(selection_data->data),"\n",-1);
+
+    for (path = *sourcelist; *path; path = *(++sourcelist))
+    {
+	if (path == NULL)	/* damn konqueror */
+	    break;
+
+	if (str_has_prefix_nocase(path, "fonts:///"))
+	{
+    	    path += 8;
+
+	    /* plain, since we already stripped the first URI part */
+    	    decoded = xmms_urldecode_plain(path);
+
+            /* Get the old font's size, and add it to the dropped
+	     * font's name
+	     */
+    	    cfg.playlist_font = g_strconcat(decoded + 1,
+        	                            strrchr(cfg.playlist_font, ' '),
+            	                            NULL);
+    	    playlist_list_set_font(cfg.playlist_font);
+    	    playlistwin_update_list();
+        
+    	    g_free(decoded);
+    	    return;
+	}
+
+	if (str_has_prefix_nocase(path,"file:///"))
+	{
+	    if (not_font == FALSE)
+	    {
+		playlist_clear();
+		not_font = TRUE;
+	    }	
+    	    playlist_add_url(path);
+	}
+    }
+    
+    g_strfreev(iter);
+    
+    if (not_font)
+	bmp_playback_initiate();
+}
+
+static void
+on_add_url_add_clicked(GtkWidget * widget,
+                       GtkWidget * entry)
+{
+    const gchar *text = gtk_entry_get_text(GTK_ENTRY(entry));
+    if (text && *text)
+        playlist_add_url(text);
+}
+
+static void
+on_add_url_ok_clicked(GtkWidget * widget,
+                      GtkWidget * entry)
+{
+    const gchar *text = gtk_entry_get_text(GTK_ENTRY(entry));
+    if (text && *text)
+    {
+        playlist_clear();
+        playlist_add_url(text);
+        bmp_playback_initiate();
+    }
+}
+
+void
+mainwin_show_add_url_window(void)
+{
+    static GtkWidget *url_window = NULL;
+
+    if (!url_window) {
+        url_window =
+            util_add_url_dialog_new(_("Enter location to play:"),
+				    G_CALLBACK(on_add_url_ok_clicked),
+                                    G_CALLBACK(on_add_url_add_clicked));
+        gtk_window_set_transient_for(GTK_WINDOW(url_window),
+                                     GTK_WINDOW(mainwin));
+        g_signal_connect(url_window, "destroy",
+                         G_CALLBACK(gtk_widget_destroyed),
+                         &url_window);
+    }
+
+    gtk_window_present(GTK_WINDOW(url_window));
+}
+
+static void
+check_set(GtkItemFactory * factory, 
+          const gchar * path,
+          gboolean active)
+{
+    GtkWidget *item = gtk_item_factory_get_widget(factory, path);
+    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), active);
+}
+
+void
+mainwin_eject_pushed(void)
+{
+    util_run_filebrowser(PLAY_BUTTON);
+}
+
+void
+mainwin_rev_pushed(void)
+{
+    g_get_current_time(&cb_time);
+
+    seek_initial_pos = hslider_get_position(mainwin_position);
+    seek_state = MAINWIN_SEEK_REV;
+}
+
+void
+mainwin_rev_release(void)
+{
+    GTimeVal now_time;
+    GTimeVal delta_time;
+    gulong now_dur;
+
+    g_get_current_time(&now_time);
+
+    delta_time.tv_usec = now_time.tv_usec - cb_time.tv_usec;
+    delta_time.tv_sec = now_time.tv_sec - cb_time.tv_sec;
+
+    now_dur = labs((delta_time.tv_sec * 1000) + (glong) (delta_time.tv_usec / 1000));
+
+    if ( now_dur <= TRISTATE_THRESHOLD )
+    {
+      /* interpret as 'skip to previous song' */
+      playlist_prev();
+    }
+    else
+    {
+      /* interpret as 'seek' */
+      mainwin_position_release_cb( hslider_get_position(mainwin_position) );
+    }
+
+    seek_state = MAINWIN_SEEK_NIL;
+}
+
+void
+mainwin_fwd_pushed(void)
+{
+    g_get_current_time(&cb_time);
+
+    seek_initial_pos = hslider_get_position(mainwin_position);
+    seek_state = MAINWIN_SEEK_FWD;
+}
+
+void
+mainwin_fwd_release(void)
+{
+    GTimeVal now_time;
+    GTimeVal delta_time;
+    gulong now_dur;
+
+    g_get_current_time(&now_time);
+
+    delta_time.tv_usec = now_time.tv_usec - cb_time.tv_usec;
+    delta_time.tv_sec = now_time.tv_sec - cb_time.tv_sec;
+
+    now_dur = labs((delta_time.tv_sec * 1000) + (glong) (delta_time.tv_usec / 1000));
+
+    if ( now_dur <= TRISTATE_THRESHOLD )
+    {
+      /* interpret as 'skip to previous song' */
+      playlist_next();
+    }
+    else
+    {
+      /* interpret as 'seek' */
+      mainwin_position_release_cb( hslider_get_position(mainwin_position) );
+    }
+
+    seek_state = MAINWIN_SEEK_NIL;
+}
+
+void
+mainwin_play_pushed(void)
+{
+    if (bmp_playback_get_paused()) {
+        bmp_playback_pause();
+        return;
+    }
+
+    if (playlist_get_length())
+        bmp_playback_initiate();
+    else
+        mainwin_eject_pushed();
+}
+
+void
+mainwin_stop_pushed(void)
+{
+    ip_data.stop = TRUE;
+    mainwin_clear_song_info();
+    bmp_playback_stop();
+    ip_data.stop = FALSE;
+}
+
+void
+mainwin_shuffle_pushed(gboolean toggled)
+{
+    check_set(mainwin_play_menu, "/Shuffle", toggled);
+}
+
+void
+mainwin_repeat_pushed(gboolean toggled)
+{
+    check_set(mainwin_play_menu, "/Repeat", toggled);
+}
+
+void
+mainwin_pl_pushed(gboolean toggled)
+{
+    if (toggled)
+        playlistwin_show();
+    else
+        playlistwin_hide();
+}
+
+gint
+mainwin_spos_frame_cb(gint pos)
+{
+    if (mainwin_sposition) {
+        if (pos < 6)
+            mainwin_sposition->hs_knob_nx = mainwin_sposition->hs_knob_px =
+                17;
+        else if (pos < 9)
+            mainwin_sposition->hs_knob_nx = mainwin_sposition->hs_knob_px =
+                20;
+        else
+            mainwin_sposition->hs_knob_nx = mainwin_sposition->hs_knob_px =
+                23;
+    }
+    return 1;
+}
+
+void
+mainwin_spos_motion_cb(gint pos)
+{
+    gint time;
+    gchar *time_msg;
+
+    pos--;
+
+    time = ((playlist_get_current_length() / 1000) * pos) / 12;
+
+    if (cfg.timer_mode == TIMER_REMAINING) {
+        time = (playlist_get_current_length() / 1000) - time;
+        time_msg = g_strdup_printf("-%2.2d", time / 60);
+        textbox_set_text(mainwin_stime_min, time_msg);
+        g_free(time_msg);
+    }
+    else {
+        time_msg = g_strdup_printf(" %2.2d", time / 60);
+        textbox_set_text(mainwin_stime_min, time_msg);
+        g_free(time_msg);
+    }
+
+    time_msg = g_strdup_printf("%2.2d", time % 60);
+    textbox_set_text(mainwin_stime_sec, time_msg);
+    g_free(time_msg);
+}
+
+void
+mainwin_spos_release_cb(gint pos)
+{
+    bmp_playback_seek(((playlist_get_current_length() / 1000) *
+                       (pos - 1)) / 12);
+}
+
+void
+mainwin_position_motion_cb(gint pos)
+{
+    gint length, time;
+    gchar *seek_msg;
+
+    length = playlist_get_current_length() / 1000;
+    time = (length * pos) / 219;
+    seek_msg = g_strdup_printf(_("SEEK TO: %d:%-2.2d/%d:%-2.2d (%d%%)"),
+                               time / 60, time % 60,
+                               length / 60, length % 60,
+                               (length != 0) ? (time * 100) / length : 0);
+    mainwin_lock_info_text(seek_msg);
+    g_free(seek_msg);
+}
+
+void
+mainwin_position_release_cb(gint pos)
+{
+    gint length, time;
+
+    length = playlist_get_current_length() / 1000;
+    time = (length * pos) / 219;
+    bmp_playback_seek(time);
+    mainwin_release_info_text();
+}
+
+gint
+mainwin_volume_frame_cb(gint pos)
+{
+    return (gint) rint((pos / 52.0) * 28);
+}
+
+void
+mainwin_adjust_volume_motion(gint v)
+{
+    gchar *volume_msg;
+
+    setting_volume = TRUE;
+
+    volume_msg = g_strdup_printf(_("VOLUME: %d%%"), v);
+    mainwin_lock_info_text(volume_msg);
+    g_free(volume_msg);
+
+    if (balance < 0)
+        input_set_volume(v, (v * (100 - abs(balance))) / 100);
+    else if (balance > 0)
+        input_set_volume((v * (100 - abs(balance))) / 100, v);
+    else
+        input_set_volume(v, v);
+}
+
+void
+mainwin_adjust_volume_release(void)
+{
+    mainwin_release_info_text();
+    setting_volume = FALSE;
+    read_volume(VOLUME_ADJUSTED);
+}
+
+void
+mainwin_adjust_balance_motion(gint b)
+{
+    gchar *balance_msg;
+    gint v, pvl, pvr;
+
+    setting_volume = TRUE;
+    balance = b;
+    input_get_volume(&pvl, &pvr);
+    v = MAX(pvl, pvr);
+    if (b < 0) {
+        balance_msg = g_strdup_printf(_("BALANCE: %d%% LEFT"), -b);
+        input_set_volume(v, (gint) rint(((100 + b) / 100.0) * v));
+    }
+    else if (b == 0) {
+        balance_msg = g_strdup_printf(_("BALANCE: CENTER"));
+        input_set_volume(v, v);
+    }
+    else {                      /* b > 0 */
+        balance_msg = g_strdup_printf(_("BALANCE: %d%% RIGHT"), b);
+        input_set_volume((gint) rint(((100 - b) / 100.0) * v), v);
+    }
+    mainwin_lock_info_text(balance_msg);
+    g_free(balance_msg);
+}
+
+void
+mainwin_adjust_balance_release(void)
+{
+    mainwin_release_info_text();
+    setting_volume = FALSE;
+    read_volume(VOLUME_ADJUSTED);
+}
+
+void
+mainwin_set_volume_slider(gint percent)
+{
+    hslider_set_position(mainwin_volume, (gint) rint((percent * 51) / 100.0));
+}
+
+void
+mainwin_set_balance_slider(gint percent)
+{
+    hslider_set_position(mainwin_balance,
+                         (gint) rint(((percent * 12) / 100.0) + 12));
+}
+
+void
+mainwin_volume_motion_cb(gint pos)
+{
+    gint vol = (pos * 100) / 51;
+    mainwin_adjust_volume_motion(vol);
+    equalizerwin_set_volume_slider(vol);
+}
+
+void
+mainwin_volume_release_cb(gint pos)
+{
+    mainwin_adjust_volume_release();
+}
+
+gint
+mainwin_balance_frame_cb(gint pos)
+{
+    return ((abs(pos - 12) * 28) / 13);
+}
+
+void
+mainwin_balance_motion_cb(gint pos)
+{
+    gint bal = ((pos - 12) * 100) / 12;
+    mainwin_adjust_balance_motion(bal);
+    equalizerwin_set_balance_slider(bal);
+}
+
+void
+mainwin_balance_release_cb(gint pos)
+{
+    mainwin_adjust_volume_release();
+}
+
+void
+mainwin_set_volume_diff(gint diff)
+{
+    gint vl, vr, vol;
+
+    input_get_volume(&vl, &vr);
+    vol = MAX(vl, vr);
+    vol = CLAMP(vol + diff, 0, 100);
+
+    mainwin_adjust_volume_motion(vol);
+    setting_volume = FALSE;
+    mainwin_set_volume_slider(vol);
+    equalizerwin_set_volume_slider(vol);
+    read_volume(VOLUME_SET);
+}
+
+void
+mainwin_set_balance_diff(gint diff)
+{
+    gint b;
+    b = CLAMP(balance + diff, -100, 100);
+    mainwin_adjust_balance_motion(b);
+    setting_volume = FALSE;
+    mainwin_set_balance_slider(b);
+    equalizerwin_set_balance_slider(b);
+    read_volume(VOLUME_SET);
+}
+
+void
+mainwin_show(gboolean show)
+{
+    if (show)
+        mainwin_real_show();
+    else
+        mainwin_real_hide();
+}
+
+void
+mainwin_real_show(void)
+{
+    cfg.player_visible = TRUE;
+
+    check_set(mainwin_view_menu, "/Show Player", TRUE);
+
+    if (cfg.player_shaded)
+        vis_clear_data(active_vis);
+
+    mainwin_vis_set_active_vis(MAINWIN_VIS_ACTIVE_MAINWIN);
+    mainwin_set_shape_mask();
+
+    if (cfg.show_wm_decorations) {
+        if (!pposition_broken && cfg.player_x != -1
+            && cfg.save_window_position)
+            gtk_window_move(GTK_WINDOW(mainwin), cfg.player_x, cfg.player_y);
+
+        gtk_widget_show(mainwin);
+
+        if (pposition_broken && cfg.player_x != -1
+            && cfg.save_window_position)
+            gtk_window_move(GTK_WINDOW(mainwin), cfg.player_x, cfg.player_y);
+
+        return;
+    }
+
+    gtk_widget_show_all(mainwin);
+
+    if (!nullmask)
+        return;
+
+    g_object_unref(nullmask);
+    nullmask = NULL;
+
+    gdk_window_set_hints(mainwin->window, 0, 0,
+                         PLAYER_WIDTH, PLAYER_HEIGHT,
+                         PLAYER_WIDTH, PLAYER_HEIGHT,
+                         GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
+    gtk_window_resize(GTK_WINDOW(mainwin), PLAYER_WIDTH, PLAYER_HEIGHT);
+
+    if (cfg.player_x != -1 && cfg.player_y != -1)
+        gtk_window_move(GTK_WINDOW(mainwin), cfg.player_x, cfg.player_y);
+
+    draw_main_window(TRUE);
+
+    gtk_window_present(GTK_WINDOW(mainwin));
+}
+
+void
+mainwin_real_hide(void)
+{
+    GdkGC *gc;
+    GdkColor pattern;
+
+    check_set(mainwin_view_menu, "/Show Player", FALSE);
+
+    if (cfg.player_shaded)
+        svis_clear_data(mainwin_svis);
+
+    if (!cfg.show_wm_decorations) {
+        nullmask = gdk_pixmap_new(mainwin->window, 20, 20, 1);
+        gc = gdk_gc_new(nullmask);
+        pattern.pixel = 0;
+        gdk_gc_set_foreground(gc, &pattern);
+        gdk_draw_rectangle(nullmask, gc, TRUE, 0, 0, 20, 20);
+        gdk_gc_destroy(gc);
+        gtk_widget_shape_combine_mask(mainwin, nullmask, 0, 0);
+
+        gdk_window_set_hints(mainwin->window, 0, 0, 0, 0, 0, 0,
+                             GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE);
+        gdk_window_resize(mainwin->window, 0, 0);
+    }
+
+    gtk_widget_hide(mainwin);
+
+    mainwin_vis_set_active_vis(MAINWIN_VIS_ACTIVE_PLAYLISTWIN);
+    cfg.player_visible = FALSE;
+}
+
+static void
+mainwin_songname_menu_callback(gpointer data,
+                               guint action,
+                               GtkWidget * item)
+{
+    GtkCheckMenuItem *check;
+
+    switch (action) {
+    case MAINWIN_SONGNAME_FILEINFO:
+        playlist_fileinfo_current();
+        break;
+    case MAINWIN_SONGNAME_JTF:
+        mainwin_jump_to_file();
+        break;
+    case MAINWIN_SONGNAME_JTT:
+        mainwin_jump_to_time();
+        break;
+    case MAINWIN_SONGNAME_SCROLL:
+        check = GTK_CHECK_MENU_ITEM(item);
+        mainwin_set_title_scroll(gtk_check_menu_item_get_active(check));
+        break;
+    case MAINWIN_SONGNAME_STOPAFTERSONG:
+        check = GTK_CHECK_MENU_ITEM(item);
+        cfg.stopaftersong = gtk_check_menu_item_get_active(check);
+        check_set(mainwin_songname_menu, "/Stop After Current Song", cfg.stopaftersong);
+        check_set(mainwin_play_menu, "/Stop After Current Song", cfg.stopaftersong);
+        break;
+    }
+}
+
+void
+mainwin_set_stopaftersong(gboolean stop)
+{
+    cfg.stopaftersong = stop;
+    check_set(mainwin_songname_menu, "/Stop After Current Song", cfg.stopaftersong);
+}
+
+static void
+mainwin_play_menu_callback(gpointer data,
+                           guint action,
+                           GtkWidget * item)
+{
+    GtkCheckMenuItem *check;
+
+    switch (action) {
+    case MAINWIN_OPT_SHUFFLE:
+        check = GTK_CHECK_MENU_ITEM(item);
+        cfg.shuffle = gtk_check_menu_item_get_active(check);
+        playlist_set_shuffle(cfg.shuffle);
+        tbutton_set_toggled(mainwin_shuffle, cfg.shuffle);
+        break;
+    case MAINWIN_OPT_REPEAT:
+        check = GTK_CHECK_MENU_ITEM(item);
+        cfg.repeat = gtk_check_menu_item_get_active(check);
+        tbutton_set_toggled(mainwin_repeat, cfg.repeat);
+        break;
+    case MAINWIN_OPT_NPA:
+        check = GTK_CHECK_MENU_ITEM(item);
+        cfg.no_playlist_advance = gtk_check_menu_item_get_active(check);
+        break;
+    }
+}
+
+                               
+static void
+mainwin_view_menu_callback(gpointer data,
+                           guint action,
+                           GtkWidget * item)
+{
+    switch (action) {
+    case MAINWIN_OPT_TELAPSED:
+        set_timer_mode_menu_cb(TIMER_ELAPSED);
+        break;
+    case MAINWIN_OPT_TREMAINING:
+        set_timer_mode_menu_cb(TIMER_REMAINING);
+        break;
+    case MAINWIN_OPT_ALWAYS:
+        mainwin_menurow->mr_always_selected = GTK_CHECK_MENU_ITEM(item)->active;
+        cfg.always_on_top = mainwin_menurow->mr_always_selected;
+        widget_draw(WIDGET(mainwin_menurow));
+
+        if (starting_up == FALSE)
+            hint_set_always(cfg.always_on_top);
+
+        break;
+    case MAINWIN_OPT_STICKY:
+        cfg.sticky = GTK_CHECK_MENU_ITEM(item)->active;
+        hint_set_sticky(cfg.sticky);
+        break;
+    case MAINWIN_OPT_WS:
+        mainwin_set_shade_menu_cb(GTK_CHECK_MENU_ITEM(item)->active);
+        break;
+    case MAINWIN_OPT_PWS:
+        playlistwin_set_shade(GTK_CHECK_MENU_ITEM(item)->active);
+        break;
+    case MAINWIN_OPT_EQWS:
+        equalizerwin_set_shade_menu_cb(GTK_CHECK_MENU_ITEM(item)->active);
+        break;
+    }
+}
+
+void
+mainwin_vis_menu_callback(gpointer data,
+                          guint action,
+                          GtkWidget * item)
+{
+    switch (action) {
+    case MAINWIN_VIS_ANALYZER:
+    case MAINWIN_VIS_SCOPE:
+    case MAINWIN_VIS_OFF:
+        mainwin_vis_set_type_menu_cb(action - MAINWIN_VIS_ANALYZER);
+        break;
+    case MAINWIN_VIS_ANALYZER_NORMAL:
+    case MAINWIN_VIS_ANALYZER_FIRE:
+    case MAINWIN_VIS_ANALYZER_VLINES:
+        mainwin_vis_set_analyzer_mode(action - MAINWIN_VIS_ANALYZER_NORMAL);
+        break;
+    case MAINWIN_VIS_ANALYZER_LINES:
+    case MAINWIN_VIS_ANALYZER_BARS:
+        mainwin_vis_set_analyzer_type(action - MAINWIN_VIS_ANALYZER_LINES);
+        break;
+    case MAINWIN_VIS_ANALYZER_PEAKS:
+        cfg.analyzer_peaks = GTK_CHECK_MENU_ITEM(item)->active;
+        break;
+    case MAINWIN_VIS_SCOPE_DOT:
+    case MAINWIN_VIS_SCOPE_LINE:
+    case MAINWIN_VIS_SCOPE_SOLID:
+        cfg.scope_mode = action - MAINWIN_VIS_SCOPE_DOT;
+        break;
+    case MAINWIN_VIS_VU_NORMAL:
+    case MAINWIN_VIS_VU_SMOOTH:
+        cfg.vu_mode = action - MAINWIN_VIS_VU_NORMAL;
+        break;
+    case MAINWIN_VIS_REFRESH_FULL:
+    case MAINWIN_VIS_REFRESH_HALF:
+    case MAINWIN_VIS_REFRESH_QUARTER:
+    case MAINWIN_VIS_REFRESH_EIGHTH:
+        mainwin_vis_set_refresh(action - MAINWIN_VIS_REFRESH_FULL);
+        break;
+    case MAINWIN_VIS_AFALLOFF_SLOWEST:
+    case MAINWIN_VIS_AFALLOFF_SLOW:
+    case MAINWIN_VIS_AFALLOFF_MEDIUM:
+    case MAINWIN_VIS_AFALLOFF_FAST:
+    case MAINWIN_VIS_AFALLOFF_FASTEST:
+        mainwin_vis_set_afalloff(action - MAINWIN_VIS_AFALLOFF_SLOWEST);
+        break;
+    case MAINWIN_VIS_PFALLOFF_SLOWEST:
+    case MAINWIN_VIS_PFALLOFF_SLOW:
+    case MAINWIN_VIS_PFALLOFF_MEDIUM:
+    case MAINWIN_VIS_PFALLOFF_FAST:
+    case MAINWIN_VIS_PFALLOFF_FASTEST:
+        mainwin_vis_set_pfalloff(action - MAINWIN_VIS_PFALLOFF_SLOWEST);
+        break;
+    }
+}
+
+void
+mainwin_general_menu_callback(gpointer data,
+                              guint action,
+                              GtkWidget * item)
+{
+    switch (action) {
+    case MAINWIN_GENERAL_PREFS:
+        show_prefs_window();
+        break;
+    case MAINWIN_GENERAL_ABOUT:
+        show_about_window();
+        break;
+    case MAINWIN_GENERAL_PLAYFILE:
+        util_run_filebrowser(NO_PLAY_BUTTON);
+        break;
+    case MAINWIN_GENERAL_PLAYCD:
+        play_medium();
+        break;
+    case MAINWIN_GENERAL_ADDCD:
+        add_medium();
+        break;
+    case MAINWIN_GENERAL_PLAYLOCATION:
+        mainwin_show_add_url_window();
+        break;
+    case MAINWIN_GENERAL_FILEINFO:
+        playlist_fileinfo_current();
+        break;
+    case MAINWIN_GENERAL_FOCUSPLWIN:
+        gtk_window_present(GTK_WINDOW(playlistwin));
+        break;
+    case MAINWIN_GENERAL_SHOWMWIN:
+        mainwin_show(GTK_CHECK_MENU_ITEM(item)->active);
+        break;
+    case MAINWIN_GENERAL_SHOWPLWIN:
+        if (GTK_CHECK_MENU_ITEM(item)->active)
+            playlistwin_show();
+        else
+            playlistwin_hide();
+        break;
+    case MAINWIN_GENERAL_SHOWEQWIN:
+        if (GTK_CHECK_MENU_ITEM(item)->active)
+            equalizerwin_real_show();
+        else
+            equalizerwin_real_hide();
+        break;
+    case MAINWIN_GENERAL_PREV:
+        playlist_prev();
+        break;
+    case MAINWIN_GENERAL_PLAY:
+        mainwin_play_pushed();
+        break;
+    case MAINWIN_GENERAL_PAUSE:
+        bmp_playback_pause();
+        break;
+    case MAINWIN_GENERAL_STOP:
+        mainwin_stop_pushed();
+        break;
+    case MAINWIN_GENERAL_NEXT:
+        playlist_next();
+        break;
+    case MAINWIN_GENERAL_BACK5SEC:
+        if (bmp_playback_get_playing()
+            && playlist_get_current_length() != -1)
+            bmp_playback_seek_relative(-5);
+        break;
+    case MAINWIN_GENERAL_FWD5SEC:
+        if (bmp_playback_get_playing()
+            && playlist_get_current_length() != -1)
+            bmp_playback_seek_relative(5);
+        break;
+    case MAINWIN_GENERAL_START:
+        playlist_set_position(0);
+        break;
+    case MAINWIN_GENERAL_JTT:
+        mainwin_jump_to_time();
+        break;
+    case MAINWIN_GENERAL_JTF:
+        mainwin_jump_to_file();
+        break;
+    case MAINWIN_GENERAL_EXIT:
+        mainwin_quit_cb();
+        break;
+    }
+}
+
+static void
+mainwin_mr_change(MenuRowItem i)
+{
+    switch (i) {
+    case MENUROW_NONE:
+        mainwin_set_info_text();
+        break;
+    case MENUROW_OPTIONS:
+        mainwin_lock_info_text(_("OPTIONS MENU"));
+        break;
+    case MENUROW_ALWAYS:
+        if (mainwin_menurow->mr_always_selected)
+            mainwin_lock_info_text(_("DISABLE ALWAYS ON TOP"));
+        else
+            mainwin_lock_info_text(_("ENABLE ALWAYS ON TOP"));
+        break;
+    case MENUROW_FILEINFOBOX:
+        mainwin_lock_info_text(_("FILE INFO BOX"));
+        break;
+    case MENUROW_DOUBLESIZE:
+        mainwin_lock_info_text(_("** DOUBLESIZE HAS BEEN REMOVED **"));
+        break;
+    case MENUROW_VISUALIZATION:
+        mainwin_lock_info_text(_("VISUALIZATION MENU"));
+        break;
+    }
+}
+
+static void
+mainwin_mr_release(MenuRowItem i)
+{
+    GdkModifierType modmask;
+    GtkWidget *widget;
+    gint x, y;
+
+    switch (i) {
+    case MENUROW_OPTIONS:
+        gdk_window_get_pointer(NULL, &x, &y, &modmask);
+        util_item_factory_popup(mainwin_view_menu, x, y, 1,
+                                GDK_CURRENT_TIME);
+        break;
+    case MENUROW_ALWAYS:
+        widget =
+            gtk_item_factory_get_widget(mainwin_view_menu,
+                                        "/Always On Top");
+        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget),
+                                       mainwin_menurow->mr_always_selected);
+        break;
+    case MENUROW_FILEINFOBOX:
+        playlist_fileinfo_current();
+        break;
+    case MENUROW_DOUBLESIZE:
+        /* double size removed, do nothing */
+        break;
+    case MENUROW_VISUALIZATION:
+        gdk_window_get_pointer(NULL, &x, &y, &modmask);
+        util_item_factory_popup(mainwin_vis_menu, x, y, 1, GDK_CURRENT_TIME);
+        break;
+    case MENUROW_NONE:
+        break;
+    }
+    mainwin_release_info_text();
+}
+
+static void
+run_no_audiocd_dialog(void)
+{
+    const gchar *markup =
+        N_("<b><big>No playable CD found.</big></b>\n\n"
+           "No CD inserted, or inserted CD is not an audio CD.\n");
+
+    GtkWidget *dialog =
+        gtk_message_dialog_new_with_markup(GTK_WINDOW(mainwin),
+                                           GTK_DIALOG_DESTROY_WITH_PARENT,
+                                           GTK_MESSAGE_ERROR,
+                                           GTK_BUTTONS_OK,
+                                           _(markup));
+    gtk_dialog_run(GTK_DIALOG(dialog));
+    gtk_widget_destroy(dialog);
+}
+
+static void
+run_no_output_device_dialog(void)
+{
+    const gchar *markup =
+        N_("<b><big>Couldn't open audio.</big></b>\n\n"
+           "Please check that:\n"
+           "1. You have the correct output plugin selected.\n"
+           "2. No other programs is blocking the soundcard.\n"
+           "3. Your soundcard is configured properly.\n");
+
+    GtkWidget *dialog =
+        gtk_message_dialog_new_with_markup(GTK_WINDOW(mainwin),
+                                           GTK_DIALOG_DESTROY_WITH_PARENT,
+                                           GTK_MESSAGE_ERROR,
+                                           GTK_BUTTONS_OK,
+                                           _(markup));
+    gtk_dialog_run(GTK_DIALOG(dialog));
+    gtk_widget_destroy(dialog);
+}
+
+
+void
+add_medium(void)
+{
+    GList *list, *node;
+    gchar *filename;
+    gchar *path;
+    ConfigDb *db;
+
+    db = bmp_cfg_db_open();
+
+    bmp_cfg_db_get_string(db, "CDDA", "directory", &path);
+    bmp_cfg_db_close(db);
+
+    if (!(list = input_scan_dir(path))) {
+        run_no_audiocd_dialog();
+        return;
+    }
+
+    for (node = list; node; node = g_list_next(node)) {
+        filename = g_build_filename(path, node->data, NULL);
+        playlist_add(filename);
+        g_free(filename);
+        g_free(node->data);
+    }
+
+    g_free(path);
+    g_list_free(list);
+
+}
+
+void
+play_medium(void)
+{
+    GList *list, *node;
+    gchar *filename;
+    gchar *path;
+    ConfigDb *db;
+
+    db = bmp_cfg_db_open();
+    bmp_cfg_db_get_string(db, "CDDA", "directory", &path);
+    bmp_cfg_db_close(db);
+
+    if (!(list = input_scan_dir(path))) {
+        run_no_audiocd_dialog();
+        return;
+    }
+
+    playlist_clear();
+
+    for (node = list; node; node = g_list_next(node)) {
+        filename = g_build_filename(path, node->data, NULL);
+        playlist_add(filename);
+        g_free(filename);
+        g_free(node->data);
+    }
+
+    g_free(path);
+    g_list_free(list);
+
+    playlist_set_position(0);
+    bmp_playback_initiate();
+}
+
+void
+read_volume(gint when)
+{
+    static gint pvl = 0, pvr = 0;
+    static gint times = VOLSET_DISP_TIMES;
+    static gboolean changing = FALSE;
+
+    gint vl, vr, b, v;
+
+    input_get_volume(&vl, &vr);
+
+    switch (when) {
+    case VOLSET_STARTUP:
+        vl = CLAMP(vl, 0, 100);
+        vr = CLAMP(vr, 0, 100);
+        pvl = vl;
+        pvr = vr;
+        v = MAX(vl, vr);
+        if (vl > vr)
+            b = (gint) rint(((gdouble) vr / vl) * 100) - 100;
+        else if (vl < vr)
+            b = 100 - (gint) rint(((gdouble) vl / vr) * 100);
+        else
+            b = 0;
+
+        balance = b;
+        mainwin_set_volume_slider(v);
+        equalizerwin_set_volume_slider(v);
+        mainwin_set_balance_slider(b);
+        equalizerwin_set_balance_slider(b);
+        return;
+
+    case VOLSET_UPDATE:
+        if (vl == -1 || vr == -1)
+            return;
+
+        if (setting_volume) {
+            pvl = vl;
+            pvr = vr;
+            return;
+        }
+
+        if (pvr == vr && pvl == vl && changing) {
+            if (times < VOLSET_DISP_TIMES)
+                times++;
+            else {
+                mainwin_release_info_text();
+                changing = FALSE;
+            }
+        }
+        else if (pvr != vr || pvl != vl) {
+            gchar *tmp;
+
+            v = MAX(vl, vr);
+            if (vl > vr)
+                b = (gint) rint(((gdouble) vr / vl) * 100) - 100;
+            else if (vl < vr)
+                b = 100 - (gint) rint(((gdouble) vl / vr) * 100);
+            else
+                b = 0;
+
+            if (MAX(vl, vr) != MAX(pvl, pvr))
+                tmp = g_strdup_printf(_("VOLUME: %d%%"), v);
+            else {
+                if (vl > vr) {
+                    tmp = g_strdup_printf(_("BALANCE: %d%% LEFT"), -b);
+                }
+                else if (vr == vl)
+                    tmp = g_strdup_printf(_("BALANCE: CENTER"));
+                else {          /* (vl < vr) */
+                    tmp = g_strdup_printf(_("BALANCE: %d%% RIGHT"), b);
+                }
+            }
+            mainwin_lock_info_text(tmp);
+            g_free(tmp);
+
+            pvr = vr;
+            pvl = vl;
+            times = 0;
+            changing = TRUE;
+            mainwin_set_volume_slider(v);
+            equalizerwin_set_volume_slider(v);
+
+            /* Don't change the balance slider if the volume has been
+             * set to zero.  The balance can be anything, and our best
+             * guess is what is was before. */
+            if (v > 0) {
+                balance = b;
+                mainwin_set_balance_slider(b);
+                equalizerwin_set_balance_slider(b);
+            }
+        }
+        break;
+
+    case VOLUME_ADJUSTED:
+        pvl = vl;
+        pvr = vr;
+        break;
+
+    case VOLUME_SET:
+        times = 0;
+        changing = TRUE;
+        pvl = vl;
+        pvr = vr;
+        break;
+    }
+}
+
+
+/* TODO: HAL! */
+gboolean
+can_play_cd(void)
+{
+    GList *ilist;
+
+    for (ilist = get_input_list(); ilist; ilist = g_list_next(ilist)) {
+        InputPlugin *ip = INPUT_PLUGIN(ilist->data);
+
+        if (!g_ascii_strcasecmp(g_basename(ip->filename),
+                                PLUGIN_FILENAME("cdaudio"))) {
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+
+static void
+set_timer_mode(TimerMode mode)
+{
+    if (mode == TIMER_ELAPSED)
+        check_set(mainwin_view_menu, "/Time Elapsed", TRUE);
+    else
+        check_set(mainwin_view_menu, "/Time Remaining", TRUE);
+}
+
+static void
+set_timer_mode_menu_cb(TimerMode mode)
+{
+    cfg.timer_mode = mode;
+}
+
+
+void
+mainwin_setup_menus(void)
+{
+    set_timer_mode(cfg.timer_mode);
+
+    /* View menu */
+
+    check_set(mainwin_view_menu, "/Always On Top", cfg.always_on_top);
+    check_set(mainwin_view_menu, "/Put on All Workspaces", cfg.sticky);
+    check_set(mainwin_view_menu, "/Roll up Player", cfg.player_shaded);
+    check_set(mainwin_view_menu, "/Roll up Playlist Editor", cfg.playlist_shaded);
+    check_set(mainwin_view_menu, "/Roll up Equalizer", cfg.equalizer_shaded);
+
+    /* Songname menu */
+
+    check_set(mainwin_songname_menu, "/Autoscroll Songname", cfg.autoscroll);
+    check_set(mainwin_songname_menu, "/Stop After Current Song", cfg.stopaftersong);
+
+    /* Playback menu */
+
+    check_set(mainwin_play_menu, "/Repeat", cfg.repeat);
+    check_set(mainwin_play_menu, "/Shuffle", cfg.shuffle);
+    check_set(mainwin_play_menu, "/No Playlist Advance", cfg.no_playlist_advance);
+
+    /* Visualization menu */
+
+    check_set(mainwin_vis_menu,
+              mainwin_vis_menu_entries[MAINWIN_VIS_MENU_VIS_MODE +
+                                       cfg.vis_type].path, TRUE);
+    check_set(mainwin_vis_menu,
+              mainwin_vis_menu_entries[MAINWIN_VIS_MENU_ANALYZER_MODE +
+                                       cfg.analyzer_mode].path, TRUE);
+    check_set(mainwin_vis_menu,
+              mainwin_vis_menu_entries[MAINWIN_VIS_MENU_ANALYZER_TYPE +
+                                       cfg.analyzer_type].path, TRUE);
+    check_set(mainwin_vis_menu,
+              mainwin_vis_menu_entries[MAINWIN_VIS_MENU_ANALYZER_PEAKS].
+              path, cfg.analyzer_peaks);
+    check_set(mainwin_vis_menu,
+              mainwin_vis_menu_entries[MAINWIN_VIS_MENU_SCOPE_MODE +
+                                       cfg.scope_mode].path, TRUE);
+    check_set(mainwin_vis_menu,
+              mainwin_vis_menu_entries[MAINWIN_VIS_MENU_WSHADEVU_MODE +
+                                       cfg.vu_mode].path, TRUE);
+    check_set(mainwin_vis_menu,
+              mainwin_vis_menu_entries[MAINWIN_VIS_MENU_REFRESH_RATE +
+                                       cfg.vis_refresh].path, TRUE);
+    check_set(mainwin_vis_menu,
+              mainwin_vis_menu_entries[MAINWIN_VIS_MENU_AFALLOFF +
+                                       cfg.analyzer_falloff].path, TRUE);
+    check_set(mainwin_vis_menu,
+              mainwin_vis_menu_entries[MAINWIN_VIS_MENU_PFALLOFF +
+                                       cfg.peaks_falloff].path, TRUE);
+}
+
+static void
+mainwin_create_widgets(void)
+{
+    mainwin_menubtn =
+        create_pbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 6, 3, 9, 9,
+                       0, 0, 0, 9, mainwin_menubtn_cb, SKIN_TITLEBAR);
+    mainwin_menubtn->pb_allow_draw = FALSE;
+    mainwin_minimize =
+        create_pbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 244, 3, 9,
+                       9, 9, 0, 9, 9, mainwin_minimize_cb, SKIN_TITLEBAR);
+    mainwin_minimize->pb_allow_draw = FALSE;
+    mainwin_shade =
+        create_pbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 254, 3, 9,
+                       9, 0, cfg.player_shaded ? 27 : 18, 9,
+                       cfg.player_shaded ? 27 : 18, mainwin_shade_toggle,
+                       SKIN_TITLEBAR);
+    mainwin_shade->pb_allow_draw = FALSE;
+    mainwin_close =
+        create_pbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 264, 3, 9,
+                       9, 18, 0, 18, 9, mainwin_quit_cb, SKIN_TITLEBAR);
+    mainwin_close->pb_allow_draw = FALSE;
+
+    mainwin_rew =
+        create_pbutton_ex(&mainwin_wlist, mainwin_bg, mainwin_gc, 16, 88, 23,
+                       18, 0, 0, 0, 18, mainwin_rev_pushed, mainwin_rev_release,
+		       SKIN_CBUTTONS, SKIN_CBUTTONS);
+    mainwin_play =
+        create_pbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 39, 88, 23,
+                       18, 23, 0, 23, 18, mainwin_play_pushed, SKIN_CBUTTONS);
+    mainwin_pause =
+        create_pbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 62, 88, 23,
+                       18, 46, 0, 46, 18, bmp_playback_pause, SKIN_CBUTTONS);
+    mainwin_stop =
+        create_pbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 85, 88, 23,
+                       18, 69, 0, 69, 18, mainwin_stop_pushed, SKIN_CBUTTONS);
+#if 0
+    mainwin_fwd =
+        create_pbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 108, 88, 22,
+                       18, 92, 0, 92, 18, playlist_next, SKIN_CBUTTONS);
+#endif
+    mainwin_fwd =
+        create_pbutton_ex(&mainwin_wlist, mainwin_bg, mainwin_gc, 108, 88, 22,
+                       18, 92, 0, 92, 18, mainwin_fwd_pushed, mainwin_fwd_release,
+		       SKIN_CBUTTONS, SKIN_CBUTTONS);
+
+    mainwin_eject =
+        create_pbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 136, 89, 22,
+                       16, 114, 0, 114, 16, mainwin_eject_pushed,
+                       SKIN_CBUTTONS);
+
+    mainwin_srew =
+        create_sbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 169, 4, 8,
+                       7, playlist_prev);
+    mainwin_splay =
+        create_sbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 177, 4, 10,
+                       7, mainwin_play_pushed);
+    mainwin_spause =
+        create_sbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 187, 4, 10,
+                       7, bmp_playback_pause);
+    mainwin_sstop =
+        create_sbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 197, 4, 9,
+                       7, mainwin_stop_pushed);
+    mainwin_sfwd =
+        create_sbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 206, 4, 8,
+                       7, playlist_next);
+    mainwin_seject =
+        create_sbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 216, 4, 9,
+                       7, mainwin_eject_pushed);
+
+    mainwin_shuffle =
+        create_tbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 164, 89, 46,
+                       15, 28, 0, 28, 15, 28, 30, 28, 45,
+                       mainwin_shuffle_pushed, SKIN_SHUFREP);
+
+    mainwin_repeat =
+        create_tbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 210, 89, 28,
+                       15, 0, 0, 0, 15, 0, 30, 0, 45,
+                       mainwin_repeat_pushed, SKIN_SHUFREP);
+
+    mainwin_eq =
+        create_tbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 219, 58, 23,
+                       12, 0, 61, 46, 61, 0, 73, 46, 73, equalizerwin_show,
+                       SKIN_SHUFREP);
+    tbutton_set_toggled(mainwin_eq, cfg.equalizer_visible);
+    mainwin_pl =
+        create_tbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 242, 58, 23,
+                       12, 23, 61, 69, 61, 23, 73, 69, 73,
+                       mainwin_pl_pushed, SKIN_SHUFREP);
+    tbutton_set_toggled(mainwin_pl, cfg.playlist_visible);
+
+    mainwin_info =
+        create_textbox(&mainwin_wlist, mainwin_bg, mainwin_gc, 112, 27,
+                       153, 1, SKIN_TEXT);
+    textbox_set_scroll(mainwin_info, cfg.autoscroll);
+    textbox_set_xfont(mainwin_info, cfg.mainwin_use_xfont, cfg.mainwin_font);
+
+    mainwin_othertext =
+	create_textbox(&mainwin_wlist, mainwin_bg, mainwin_gc, 112, 43, 
+			153, 1, SKIN_TEXT);
+
+    mainwin_rate_text =
+        create_textbox(&mainwin_wlist, mainwin_bg, mainwin_gc, 111, 43, 15,
+                       0, SKIN_TEXT);
+    mainwin_freq_text =
+        create_textbox(&mainwin_wlist, mainwin_bg, mainwin_gc, 156, 43, 10,
+                       0, SKIN_TEXT);
+
+    mainwin_menurow =
+        create_menurow(&mainwin_wlist, mainwin_bg, mainwin_gc, 10, 22, 304,
+                       0, 304, 44, mainwin_mr_change, mainwin_mr_release,
+                       SKIN_TITLEBAR);
+    mainwin_menurow->mr_doublesize_selected = FALSE;
+    mainwin_menurow->mr_always_selected = cfg.always_on_top;
+
+    mainwin_volume =
+        create_hslider(&mainwin_wlist, mainwin_bg, mainwin_gc, 107, 57, 68,
+                       13, 15, 422, 0, 422, 14, 11, 15, 0, 0, 51,
+                       mainwin_volume_frame_cb, mainwin_volume_motion_cb,
+                       mainwin_volume_release_cb, SKIN_VOLUME);
+    mainwin_balance =
+        create_hslider(&mainwin_wlist, mainwin_bg, mainwin_gc, 177, 57, 38,
+                       13, 15, 422, 0, 422, 14, 11, 15, 9, 0, 24,
+                       mainwin_balance_frame_cb, mainwin_balance_motion_cb,
+                       mainwin_balance_release_cb, SKIN_BALANCE);
+
+    mainwin_monostereo =
+        create_monostereo(&mainwin_wlist, mainwin_bg, mainwin_gc, 212, 41,
+                          SKIN_MONOSTEREO);
+
+    mainwin_playstatus =
+        create_playstatus(&mainwin_wlist, mainwin_bg, mainwin_gc, 24, 28);
+
+    mainwin_minus_num =
+        create_number(&mainwin_wlist, mainwin_bg, mainwin_gc, 36, 26,
+                      SKIN_NUMBERS);
+    widget_hide(WIDGET(mainwin_minus_num));
+    mainwin_10min_num =
+        create_number(&mainwin_wlist, mainwin_bg, mainwin_gc, 48, 26,
+                      SKIN_NUMBERS);
+    widget_hide(WIDGET(mainwin_10min_num));
+
+    mainwin_min_num =
+        create_number(&mainwin_wlist, mainwin_bg, mainwin_gc, 60, 26,
+                      SKIN_NUMBERS);
+    widget_hide(WIDGET(mainwin_min_num));
+
+    mainwin_10sec_num =
+        create_number(&mainwin_wlist, mainwin_bg, mainwin_gc, 78, 26,
+                      SKIN_NUMBERS);
+    widget_hide(WIDGET(mainwin_10sec_num));
+
+    mainwin_sec_num =
+        create_number(&mainwin_wlist, mainwin_bg, mainwin_gc, 90, 26,
+                      SKIN_NUMBERS);
+    widget_hide(WIDGET(mainwin_sec_num));
+
+    mainwin_about =
+        create_sbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 247, 83, 20,
+                       25, show_about_window);
+
+    mainwin_vis =
+        create_vis(&mainwin_wlist, mainwin_bg, mainwin->window, mainwin_gc,
+                   24, 43, 76);
+    mainwin_svis = create_svis(&mainwin_wlist, mainwin_bg, mainwin_gc, 79, 5);
+    active_vis = mainwin_vis;
+
+    mainwin_position =
+        create_hslider(&mainwin_wlist, mainwin_bg, mainwin_gc, 16, 72, 248,
+                       10, 248, 0, 278, 0, 29, 10, 10, 0, 0, 219, NULL,
+                       mainwin_position_motion_cb,
+                       mainwin_position_release_cb, SKIN_POSBAR);
+    widget_hide(WIDGET(mainwin_position));
+
+    mainwin_sposition =
+        create_hslider(&mainwin_wlist, mainwin_bg, mainwin_gc, 226, 4, 17,
+                       7, 17, 36, 17, 36, 3, 7, 36, 0, 1, 13,
+                       mainwin_spos_frame_cb, mainwin_spos_motion_cb,
+                       mainwin_spos_release_cb, SKIN_TITLEBAR);
+    widget_hide(WIDGET(mainwin_sposition));
+
+    mainwin_stime_min =
+        create_textbox(&mainwin_wlist, mainwin_bg, mainwin_gc, 130, 4, 15,
+                       FALSE, SKIN_TEXT);
+    mainwin_stime_sec =
+        create_textbox(&mainwin_wlist, mainwin_bg, mainwin_gc, 147, 4, 10,
+                       FALSE, SKIN_TEXT);
+
+    if (!cfg.player_shaded) {
+        widget_hide(WIDGET(mainwin_svis));
+        widget_hide(WIDGET(mainwin_srew));
+        widget_hide(WIDGET(mainwin_splay));
+        widget_hide(WIDGET(mainwin_spause));
+        widget_hide(WIDGET(mainwin_sstop));
+        widget_hide(WIDGET(mainwin_sfwd));
+        widget_hide(WIDGET(mainwin_seject));
+        widget_hide(WIDGET(mainwin_stime_min));
+        widget_hide(WIDGET(mainwin_stime_sec));
+    }
+
+    err = gtk_message_dialog_new(GTK_WINDOW(mainwin), GTK_DIALOG_DESTROY_WITH_PARENT|GTK_DIALOG_MODAL,
+                                 GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,"Error in Audacious.");
+
+
+    gtk_window_set_position(GTK_WINDOW(err), GTK_WIN_POS_CENTER);
+    /* Dang well better set an error message or you'll see this */
+    gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(err),
+                                             "Boo! Bad stuff! Booga Booga!");
+
+}
+
+static void
+mainwin_create_window(void)
+{
+    gint width, height;
+
+    mainwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_title(GTK_WINDOW(mainwin), _("Audacious"));
+    gtk_window_set_role(GTK_WINDOW(mainwin), "player");
+    gtk_window_set_resizable(GTK_WINDOW(mainwin), FALSE);
+
+    width = MAINWIN_WIDTH;
+    height = cfg.player_shaded ? MAINWIN_SHADED_HEIGHT : MAINWIN_HEIGHT;
+
+    gtk_widget_set_size_request(mainwin, width, height);
+    gtk_widget_set_app_paintable(mainwin, TRUE);
+
+    dock_window_list = dock_window_set_decorated(dock_window_list,
+                                                 GTK_WINDOW(mainwin),
+                                                 cfg.show_wm_decorations);
+
+    if (cfg.player_x != -1 && cfg.save_window_position)
+        gtk_window_move(GTK_WINDOW(mainwin), cfg.player_x, cfg.player_y);
+
+    gtk_widget_add_events(mainwin,
+                          GDK_FOCUS_CHANGE_MASK | GDK_BUTTON_MOTION_MASK |
+                          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+                          GDK_SCROLL_MASK | GDK_KEY_PRESS_MASK |
+                          GDK_VISIBILITY_NOTIFY_MASK);
+    gtk_widget_realize(mainwin);
+
+    util_set_cursor(mainwin);
+
+    g_signal_connect(mainwin, "destroy", G_CALLBACK(mainwin_destroy), NULL);
+    g_signal_connect(mainwin, "button_press_event",
+                     G_CALLBACK(mainwin_mouse_button_press), NULL);
+    g_signal_connect(mainwin, "scroll_event",
+                     G_CALLBACK(mainwin_scrolled), NULL);
+    g_signal_connect(mainwin, "button_release_event",
+                     G_CALLBACK(mainwin_mouse_button_release), NULL);
+    g_signal_connect(mainwin, "motion_notify_event",
+                     G_CALLBACK(mainwin_motion), NULL);
+    g_signal_connect_after(mainwin, "focus_in_event",
+                           G_CALLBACK(mainwin_focus_in), NULL);
+    g_signal_connect_after(mainwin, "focus_out_event",
+                           G_CALLBACK(mainwin_focus_out), NULL);
+    g_signal_connect(mainwin, "configure_event",
+                     G_CALLBACK(mainwin_configure), NULL);
+    g_signal_connect(mainwin, "style_set",
+                     G_CALLBACK(mainwin_set_back_pixmap), NULL);
+
+    bmp_drag_dest_set(mainwin);
+
+    g_signal_connect(mainwin, "key_press_event",
+                     G_CALLBACK(mainwin_keypress), NULL);
+}
+
+static void
+mainwin_create_menus(void)
+{
+    mainwin_general_menu = create_menu(mainwin_general_menu_entries,
+                                       mainwin_general_menu_entries_num,
+                                       mainwin_accel);
+
+    mainwin_play_menu = create_menu(mainwin_playback_menu_entries,
+                                    mainwin_playback_menu_entries_num,
+                                    mainwin_accel);
+
+    mainwin_view_menu = create_menu(mainwin_view_menu_entries,
+                                    mainwin_view_menu_entries_num,
+                                    mainwin_accel);
+
+    mainwin_songname_menu = create_menu(mainwin_songname_menu_entries,
+                                        mainwin_songname_menu_entries_num,
+                                        mainwin_accel);
+
+    mainwin_add_menu = create_menu(mainwin_add_menu_entries,
+                                   mainwin_add_menu_entries_num,
+                                   mainwin_accel);
+
+    mainwin_vis_menu = create_menu(mainwin_vis_menu_entries,
+                                   mainwin_vis_menu_entries_num,
+                                   mainwin_accel);
+
+    make_submenu(mainwin_general_menu, "/View", mainwin_view_menu);
+    make_submenu(mainwin_general_menu, "/Playback", mainwin_play_menu);
+    make_submenu(mainwin_general_menu, "/Visualization", mainwin_vis_menu);
+
+    gtk_window_add_accel_group(GTK_WINDOW(mainwin), mainwin_accel);
+}
+
+void
+mainwin_create(void)
+{
+    mainwin_create_window();
+
+    mainwin_accel = gtk_accel_group_new();
+    mainwin_create_menus();
+
+    mainwin_gc = gdk_gc_new(mainwin->window);
+    mainwin_bg = gdk_pixmap_new(mainwin->window,
+                                MAINWIN_WIDTH, MAINWIN_HEIGHT, -1);
+    mainwin_set_back_pixmap();
+    mainwin_create_widgets();
+
+    vis_set_window(mainwin_vis, mainwin->window);
+}
+
+void
+mainwin_attach_idle_func(void)
+{
+    mainwin_timeout_id = g_timeout_add(MAINWIN_UPDATE_INTERVAL,
+                                       mainwin_idle_func, NULL);
+}
+
+static void
+idle_func_update_song_info(gint time)
+{
+    gint length, t;
+    gchar stime_prefix;
+
+    length = playlist_get_current_length();
+    if (bmp_playback_get_playing())
+        playlistwin_set_time(time, length, cfg.timer_mode);
+    else
+        playlistwin_hide_timer();
+    input_update_vis(time);
+
+    if (cfg.timer_mode == TIMER_REMAINING) {
+        if (length != -1) {
+            number_set_number(mainwin_minus_num, 11);
+            t = length - time;
+            stime_prefix = '-';
+        }
+        else {
+            number_set_number(mainwin_minus_num, 10);
+            t = time;
+            stime_prefix = ' ';
+        }
+    }
+    else {
+        number_set_number(mainwin_minus_num, 10);
+        t = time;
+        stime_prefix = ' ';
+    }
+    t /= 1000;
+
+    /* Show the time in the format HH:MM when we have more than 100
+     * minutes. */
+    if (t >= 100 * 60)
+        t /= 60;
+    number_set_number(mainwin_10min_num, t / 600);
+    number_set_number(mainwin_min_num, (t / 60) % 10);
+    number_set_number(mainwin_10sec_num, (t / 10) % 6);
+    number_set_number(mainwin_sec_num, t % 10);
+
+    if (!mainwin_sposition->hs_pressed) {
+        gchar *time_str;
+
+        time_str = g_strdup_printf("%c%2.2d", stime_prefix, t / 60);
+        textbox_set_text(mainwin_stime_min, time_str);
+        g_free(time_str);
+
+        time_str = g_strdup_printf("%2.2d", t % 60);
+        textbox_set_text(mainwin_stime_sec, time_str);
+        g_free(time_str);
+    }
+
+    time /= 1000;
+    length /= 1000;
+    if (length > 0) {
+        if (time > length) {
+            hslider_set_position(mainwin_position, 219);
+            hslider_set_position(mainwin_sposition, 13);
+        }
+        /* update the slider position ONLY if there is not a seek in progress */
+        else if (seek_state == MAINWIN_SEEK_NIL)  {
+            hslider_set_position(mainwin_position, (time * 219) / length);
+            hslider_set_position(mainwin_sposition,
+                                 ((time * 12) / length) + 1);
+        }
+    }
+    else {
+        hslider_set_position(mainwin_position, 0);
+        hslider_set_position(mainwin_sposition, 1);
+    }
+}
+
+static gboolean
+mainwin_idle_func(gpointer data)
+{
+    static gint count = 0;
+    gint time = 0;
+
+    /* run audcore events, then run our own. --nenolod */
+    switch((time = audcore_generic_events()))
+    {
+        case -2:
+            /* no usable output device */
+            GDK_THREADS_ENTER();
+            run_no_output_device_dialog();
+            mainwin_stop_pushed();
+            GDK_THREADS_LEAVE();
+            ev_waiting = FALSE;
+            break;
+
+        default:
+            idle_func_update_song_info(time);
+            /* nothing at this time */
+    }
+
+    GDK_THREADS_ENTER();
+
+    if (bmp_playback_get_playing())
+        vis_playback_start();
+    else
+        vis_playback_stop();
+
+    draw_main_window(mainwin_force_redraw);
+
+    if (!count) {
+        read_volume(VOLSET_UPDATE);
+        count = 10;
+    }
+    else
+        count--;
+
+    mainwin_force_redraw = FALSE;
+    draw_playlist_window(FALSE);
+    draw_equalizer_window(FALSE);
+
+    if (mainwin_title_text) {
+        G_LOCK(mainwin_title);
+        gtk_window_set_title(GTK_WINDOW(mainwin), mainwin_title_text);
+        g_free(mainwin_title_text);
+        mainwin_title_text = NULL;
+        G_UNLOCK(mainwin_title);
+
+        mainwin_set_info_text();
+        playlistwin_update_list();
+    }
+
+    /* tristate buttons seek */
+    if ( seek_state != MAINWIN_SEEK_NIL )
+    {
+      GTimeVal now_time;
+      GTimeVal delta_time;
+      gulong now_dur;
+      g_get_current_time(&now_time);
+
+      delta_time.tv_usec = now_time.tv_usec - cb_time.tv_usec;
+      delta_time.tv_sec = now_time.tv_sec - cb_time.tv_sec;
+
+      now_dur = labs((delta_time.tv_sec * 1000) + (glong) (delta_time.tv_usec / 1000));
+
+      if ( now_dur > TRISTATE_THRESHOLD )
+      {
+        gint np;
+        if (seek_state == MAINWIN_SEEK_REV)
+          np = seek_initial_pos - labs((gulong)(now_dur/100)); /* seek back */
+        else
+          np = seek_initial_pos + labs((gulong)(now_dur/100)); /* seek forward */
+
+        /* boundaries check */
+        if (np < 0 )
+          np = 0;
+        else if ( np > 219 )
+          np = 219;
+
+        hslider_set_position( mainwin_position , np );
+        mainwin_position_motion_cb( np );
+      }
+    }
+
+    GDK_THREADS_LEAVE();
+
+    /*
+    if (seek_state == MAINWIN_SEEK_REV)
+        bmp_playback_seek(CLAMP(bmp_playback_get_time() - 1000, 0,
+                                playlist_get_current_length()) / 1000);
+    else if (seek_state == MAINWIN_SEEK_FWD)
+        bmp_playback_seek(CLAMP(bmp_playback_get_time() + 1000, 0,
+                                playlist_get_current_length()) / 1000);
+    */
+
+    return TRUE;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/mainwin.h	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,193 @@
+/*  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; 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.
+ */
+
+#ifndef MAINWIN_H
+#define MAINWIN_H
+
+#include <gtk/gtk.h>
+
+#include "widgets/widgetcore.h"
+
+/* yes, main window size is fixed */
+#define MAINWIN_WIDTH            (gint)275
+#define MAINWIN_HEIGHT           (gint)116
+#define MAINWIN_TITLEBAR_HEIGHT  (gint)14
+#define MAINWIN_SHADED_HEIGHT    MAINWIN_TITLEBAR_HEIGHT
+
+#define MAINWIN_UPDATE_INTERVAL  10
+
+#define MAINWIN_DEFAULT_POS_X    20
+#define MAINWIN_DEFAULT_POS_Y    20
+
+#define MAINWIN_DEFAULT_FONT     "Sans Bold 9"
+
+
+typedef enum {
+    TIMER_ELAPSED,
+    TIMER_REMAINING
+} TimerMode;
+
+enum {
+    MAINWIN_GENERAL_ABOUT,
+    
+    MAINWIN_GENERAL_PLAYFILE,
+    MAINWIN_GENERAL_PLAYLOCATION,
+
+    MAINWIN_GENERAL_FILEINFO,
+    MAINWIN_GENERAL_PREFS,
+
+    MAINWIN_GENERAL_SHOWMWIN,
+    MAINWIN_GENERAL_SHOWPLWIN,
+
+    MAINWIN_GENERAL_FOCUSMWIN,
+    MAINWIN_GENERAL_FOCUSPLWIN,
+
+    MAINWIN_GENERAL_SHOWEQWIN,
+    MAINWIN_GENERAL_PLAYCD,
+    MAINWIN_GENERAL_EXIT,
+
+    MAINWIN_GENERAL_ADDCD,
+
+    MAINWIN_GENERAL_PREV,
+    MAINWIN_GENERAL_PLAY,
+    MAINWIN_GENERAL_PAUSE,
+    MAINWIN_GENERAL_STOP,
+    MAINWIN_GENERAL_NEXT,
+    MAINWIN_GENERAL_STOPFADE,
+    MAINWIN_GENERAL_BACK5SEC,
+    MAINWIN_GENERAL_FWD5SEC,
+    MAINWIN_GENERAL_START,
+    MAINWIN_GENERAL_BACK10,
+    MAINWIN_GENERAL_FWD10,
+    MAINWIN_GENERAL_JTT,
+    MAINWIN_GENERAL_JTF,
+    MAINWIN_GENERAL_QUEUE,
+    MAINWIN_GENERAL_CQUEUE,
+    MAINWIN_GENERAL_VOLUP,
+    MAINWIN_GENERAL_VOLDOWN
+};
+
+extern GtkWidget *mainwin;
+extern GtkWidget *err;
+extern GdkGC *mainwin_gc;
+
+extern GtkAccelGroup *mainwin_accel;
+
+extern gboolean mainwin_moving;
+extern gboolean mainwin_focus;
+
+extern GtkWidget *mainwin_jtf;
+
+extern GtkItemFactory *mainwin_general_menu; 
+extern GtkItemFactory *mainwin_vis_menu;
+extern GtkItemFactory *mainwin_play_menu, *mainwin_view_menu;
+
+extern TextBox *mainwin_stime_min, *mainwin_stime_sec;
+extern TextBox *mainwin_info;
+extern TButton *mainwin_shuffle, *mainwin_repeat, *mainwin_eq, *mainwin_pl;
+
+extern Vis *active_vis;
+extern Vis *mainwin_vis;
+extern SVis *mainwin_svis;
+
+extern PlayStatus *mainwin_playstatus;
+
+extern Number *mainwin_minus_num, *mainwin_10min_num, *mainwin_min_num;
+extern Number *mainwin_10sec_num, *mainwin_sec_num;
+
+extern HSlider *mainwin_sposition;
+
+void mainwin_create(void);
+void read_volume(gint when);
+void play_medium(void);
+void add_medium(void);
+
+void draw_main_window(gboolean);
+
+void mainwin_quit_cb(void);
+void mainwin_lock_info_text(const gchar * text);
+void mainwin_release_info_text(void);
+void mainwin_play_pushed(void);
+void mainwin_stop_pushed(void);
+void mainwin_eject_pushed(void);
+
+void mainwin_rev_pushed(void);
+void mainwin_rev_release(void);
+void mainwin_fwd_pushed(void);
+void mainwin_fwd_release(void);
+
+void mainwin_set_back_pixmap(void);
+
+void mainwin_adjust_volume_motion(gint v);
+void mainwin_adjust_volume_release(void);
+void mainwin_adjust_balance_motion(gint b);
+void mainwin_adjust_balance_release(void);
+void mainwin_set_volume_slider(gint percent);
+void mainwin_set_balance_slider(gint percent);
+
+void mainwin_vis_set_type(VisType mode);
+
+void mainwin_set_info_text(void);
+void mainwin_set_song_info(gint rate, gint freq, gint nch);
+void mainwin_clear_song_info(void);
+void mainwin_set_stopaftersong(gboolean stop);
+
+void mainwin_set_always_on_top(gboolean always);
+void mainwin_set_volume_diff(gint diff);
+void mainwin_set_balance_diff(gint diff);
+
+void mainwin_show(gboolean);
+void mainwin_real_show(void);
+void mainwin_real_hide(void);
+void mainwin_move(gint x, gint y);
+void mainwin_shuffle_pushed(gboolean toggled);
+void mainwin_repeat_pushed(gboolean toggled);
+void mainwin_disable_seekbar(void);
+void mainwin_set_title(const gchar * text);
+void mainwin_show_add_url_window(void);
+void mainwin_minimize_cb(void);
+void mainwin_general_menu_callback(gpointer cb_data,
+                                   guint action,
+                                   GtkWidget * widget);
+
+void mainwin_attach_idle_func(void);
+void mainwin_drag_data_received(GtkWidget * widget,
+                                GdkDragContext * context,
+                                gint x,
+                                gint y,
+                                GtkSelectionData * selection_data,
+                                guint info,
+                                guint time,
+                                gpointer user_data);
+
+void mainwin_setup_menus(void);
+
+void mainwin_jump_to_file(void);
+void mainwin_jump_to_time(void);
+
+void mainwin_ewmh_activate(void);
+
+/* FIXME: placed here for now */
+void playback_get_sample_params(gint * bitrate,
+                                gint * frequency,
+                                gint * numchannels);
+
+#endif
--- a/audacious/playback.c	Thu Sep 07 22:00:35 2006 -0700
+++ b/audacious/playback.c	Thu Sep 07 22:26:54 2006 -0700
@@ -40,15 +40,31 @@
 
 #include "input.h"
 #include "main.h"
+#include "mainwin.h"
+#include "equalizer.h"
 #include "output.h"
 #include "playlist.h"
+#include "ui_playlist.h"
 #include "skinwin.h"
 #include "urldecode.h"
 #include "util.h"
 
 
 #include "playback.h"
-#include "interface.h"
+
+
+/* FIXME: yuck!! this shouldn't be here... */
+void
+bmp_playback_set_random_skin(void)
+{
+    SkinNode *node;
+    guint32 randval;
+
+    /* Get a random value to select the skin to use */
+    randval = g_random_int_range(0, g_list_length(skinlist));
+    node = g_list_nth(skinlist, randval)->data;
+    bmp_active_skin_load(node->path);
+}
 
 gint
 bmp_playback_get_time(void)
@@ -73,11 +89,9 @@
     if (bmp_playback_get_playing())
         bmp_playback_stop();
 
-#if 0
     vis_clear_data(mainwin_vis);
     svis_clear_data(mainwin_svis);
     mainwin_disable_seekbar();
-#endif
 
     entry = playlist_get_entry_to_play();
 
@@ -88,9 +102,7 @@
         return;
 
     if (bmp_playback_get_time() != -1) {
-#if 0
         equalizerwin_load_auto_preset(entry->filename);
-#endif
         input_set_eq(cfg.equalizer_active, cfg.equalizer_preamp,
                      cfg.equalizer_bands);
         output_set_eq(cfg.equalizer_active, cfg.equalizer_preamp,
@@ -112,12 +124,10 @@
 
     ip_data.paused = !ip_data.paused;
 
-#if 0
     if (ip_data.paused)
         playstatus_set_status(mainwin_playstatus, STATUS_PAUSE);
     else
         playstatus_set_status(mainwin_playstatus, STATUS_PLAY);
-#endif
 
     if (get_current_input_plugin()->pause)
         get_current_input_plugin()->pause(ip_data.paused);
@@ -146,7 +156,7 @@
     }
 
     ip_data.buffering = FALSE;
-    current_interface->buffering_notify(FALSE);
+    playstatus_set_status_buffering(mainwin_playstatus, FALSE);
     ip_data.playing = FALSE;
 }
 
@@ -181,7 +191,7 @@
            "You have not selected an output plugin.");
 
     GtkWidget *dialog =
-        gtk_message_dialog_new_with_markup(GTK_WINDOW(current_interface->parentwin),
+        gtk_message_dialog_new_with_markup(GTK_WINDOW(mainwin),
                                            GTK_DIALOG_DESTROY_WITH_PARENT,
                                            GTK_MESSAGE_ERROR,
                                            GTK_BUTTONS_OK,
--- a/audacious/playlist.c	Thu Sep 07 22:00:35 2006 -0700
+++ b/audacious/playlist.c	Thu Sep 07 22:26:54 2006 -0700
@@ -42,16 +42,18 @@
 
 #include "input.h"
 #include "main.h"
+#include "mainwin.h"
 #include "libaudacious/util.h"
 #include "libaudacious/configdb.h"
 #include "libaudacious/vfs.h"
+#include "equalizer.h"
 #include "playback.h"
 #include "playlist.h"
 #include "playlist_container.h"
+#include "ui_playlist.h"
 #include "urldecode.h"
 #include "util.h"
 #include "ui_fileinfo.h"
-#include "interface.h"
 
 #include "debug.h"
 
@@ -835,11 +837,9 @@
 
     playlist_recalc_total_time();
 
-    current_interface->set_song_info(rate, freq, nch);
+    mainwin_set_song_info(rate, freq, nch);
 }
 
-/* *** TO WA2GUI *** */
-#if 0
 void
 playlist_check_pos_current(void)
 {
@@ -864,7 +864,6 @@
     PLAYLIST_UNLOCK();
     playlistwin_set_toprow(row);
 }
-#endif
 
 void
 playlist_next(void)
--- a/audacious/pluginenum.c	Thu Sep 07 22:00:35 2006 -0700
+++ b/audacious/pluginenum.c	Thu Sep 07 22:26:54 2006 -0700
@@ -36,6 +36,7 @@
 
 #include "controlsocket.h"
 #include "main.h"
+#include "mainwin.h"
 #include "playback.h"
 #include "playlist.h"
 #include "util.h"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/ui_playlist.c	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,2029 @@
+/*  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; 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 "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 "libaudacious/util.h"
+
+#include "dnd.h"
+#include "dock.h"
+#include "equalizer.h"
+#include "hints.h"
+#include "input.h"
+#include "main.h"
+#include "mainwin.h"
+#include "playback.h"
+#include "playlist.h"
+#include "playlist_container.h"
+#include "util.h"
+
+#include "pixmaps.h"
+#include "images/audacious_playlist.xpm"
+
+
+#define ITEM_SEPARATOR {"/-", NULL, NULL, 0, "<Separator>", NULL}
+
+
+enum {
+    ADD_URL, ADD_DIR, ADD_FILES,
+    SUB_MISC, SUB_ALL, SUB_CROP, SUB_SELECTED,
+    SUB_DUPLICATE_BYTITLE, SUB_DUPLICATE_BYFILENAME, SUB_DUPLICATE_BYPATH,
+    SEL_INV, SEL_ZERO, SEL_ALL,
+    MISC_SORT, MISC_FILEINFO, MISC_MISCOPTS,
+    PLIST_NEW, PLIST_SAVE_AS, PLIST_LOAD,
+    SEL_LOOKUP, CLOSE_PL_WINDOW, MOVE_UP, PLIST_SAVE,
+    MISC_QUEUE, PLIST_CQUEUE, PLIST_JTF, PLIST_JTT,
+    PLAYLISTWIN_REMOVE_DEAD_FILES,
+    PLAYLISTWIN_REFRESH, PLIST_DEFAULTSAVE, MISC_FILEPOPUP
+};
+
+enum {
+    PLAYLISTWIN_SORT_BYTITLE, PLAYLISTWIN_SORT_BYFILENAME,
+    PLAYLISTWIN_SORT_BYPATH, PLAYLISTWIN_SORT_BYDATE,
+    PLAYLISTWIN_SORT_BYARTIST, PLAYLISTWIN_SORT_SEL_BYARTIST,
+    PLAYLISTWIN_SORT_SEL_BYTITLE, PLAYLISTWIN_SORT_SEL_BYFILENAME,
+    PLAYLISTWIN_SORT_SEL_BYPATH, PLAYLISTWIN_SORT_SEL_BYDATE,
+    PLAYLISTWIN_SORT_RANDOMIZE, PLAYLISTWIN_SORT_REVERSE,
+    PLAYLISTWIN_SORT_BYTRACK, PLAYLISTWIN_SORT_SEL_BYTRACK,
+    PLAYLISTWIN_SORT_BYPLAYLIST, PLAYLISTWIN_SORT_SEL_BYPLAYLIST
+};
+
+GtkWidget *playlistwin;
+
+PlayList_List *playlistwin_list = NULL;
+PButton *playlistwin_shade, *playlistwin_close;
+
+static gboolean playlistwin_resizing = FALSE;
+
+static GtkItemFactory *playlistwin_popup_menu;
+static GtkItemFactory *pladd_menu, *pldel_menu;
+static GtkItemFactory *plsel_menu, *plsort_menu;
+static GtkItemFactory *pllist_menu;
+
+static GdkPixmap *playlistwin_bg;
+static GdkBitmap *playlistwin_mask = NULL;
+static GdkGC *playlistwin_gc;
+
+static GtkAccelGroup *playlistwin_accel;
+
+static gboolean playlistwin_hint_flag = FALSE;
+
+static PlaylistSlider *playlistwin_slider = NULL;
+static TextBox *playlistwin_time_min, *playlistwin_time_sec;
+static TextBox *playlistwin_info, *playlistwin_sinfo;
+static SButton *playlistwin_srew, *playlistwin_splay;
+static SButton *playlistwin_spause, *playlistwin_sstop;
+static SButton *playlistwin_sfwd, *playlistwin_seject;
+static SButton *playlistwin_sscroll_up, *playlistwin_sscroll_down;
+
+static GList *playlistwin_wlist = NULL;
+
+static void plsort_menu_callback(gpointer cb_data, guint action,
+                                           GtkWidget * w);
+static void playlistwin_sub_menu_callback(gpointer cb_data, guint action,
+                                          GtkWidget * w);
+static void playlistwin_popup_menu_callback(gpointer cb_data, guint action,
+                                            GtkWidget * w);
+
+static GtkItemFactoryEntry playlistwin_popup_menu_entries[] = {
+    {N_("/View Track Details"), NULL,
+     playlistwin_popup_menu_callback,
+     MISC_FILEINFO, "<ImageItem>", my_pixbuf},
+
+    {N_("/Show Popup Info"), NULL,
+     playlistwin_popup_menu_callback,
+     MISC_FILEPOPUP, "<ToggleItem>", NULL},
+
+    ITEM_SEPARATOR,
+
+    {N_("/Remove Selected"), "Delete",
+     playlistwin_sub_menu_callback,
+     SUB_SELECTED, "<StockItem>", GTK_STOCK_REMOVE},
+
+    {N_("/Remove Unselected"), NULL,
+     playlistwin_sub_menu_callback,
+     SUB_CROP, "<StockItem>", GTK_STOCK_REMOVE},
+
+    {N_("/Remove All"), NULL,
+     playlistwin_sub_menu_callback,
+     SUB_ALL, "<StockItem>", GTK_STOCK_CLEAR},
+
+    ITEM_SEPARATOR,
+
+    {N_("/Queue Toggle"), "q",
+     playlistwin_popup_menu_callback,
+     MISC_QUEUE, "<ImageItem>", queuetoggle_pixbuf},
+};
+
+static GtkItemFactoryEntry pladd_menu_entries[] = {
+    {N_("/Add CD..."), "<shift>c",
+     mainwin_general_menu_callback,
+     MAINWIN_GENERAL_ADDCD, "<StockItem>", GTK_STOCK_CDROM},
+
+    {N_("/Add Internet Address..."), "<control>h",
+     mainwin_general_menu_callback,
+     MAINWIN_GENERAL_PLAYLOCATION, "<StockItem>", GTK_STOCK_NETWORK},
+
+    {N_("/Add Files..."), "f",
+     mainwin_general_menu_callback,
+     MAINWIN_GENERAL_PLAYFILE, "<StockItem>", GTK_STOCK_ADD},
+};
+
+static GtkItemFactoryEntry pldel_menu_entries[] = {
+    {N_("/Clear Queue"), "<shift>Q",
+     playlistwin_popup_menu_callback,
+     PLIST_CQUEUE, "<StockItem>", GTK_STOCK_CANCEL},
+
+    ITEM_SEPARATOR,
+
+    {N_("/Remove Unavailable Files"), NULL,
+     playlistwin_sub_menu_callback,
+     PLAYLISTWIN_REMOVE_DEAD_FILES, "<ImageItem>", removeunavail_pixbuf},
+
+    {N_("/Remove Duplicates"), NULL, NULL, 0, "<Branch>", NULL},
+    {N_("/Remove Duplicates/By Title"), NULL,
+     playlistwin_sub_menu_callback,
+     SUB_DUPLICATE_BYTITLE, "<ImageItem>", removedups_pixbuf},
+    {N_("/Remove Duplicates/By Filename"), NULL,
+     playlistwin_sub_menu_callback,
+     SUB_DUPLICATE_BYFILENAME, "<ImageItem>", removedups_pixbuf},
+    {N_("/Remove Duplicates/By Path + Filename"), NULL,
+     playlistwin_sub_menu_callback,
+     SUB_DUPLICATE_BYPATH, "<ImageItem>", removedups_pixbuf},
+
+    ITEM_SEPARATOR,
+
+    {N_("/Remove All"), NULL,
+     playlistwin_sub_menu_callback,
+     SUB_ALL, "<StockItem>", GTK_STOCK_CLEAR},
+
+    {N_("/Remove Unselected"), NULL,
+     playlistwin_sub_menu_callback,
+     SUB_CROP, "<StockItem>", GTK_STOCK_REMOVE},
+
+    {N_("/Remove Selected"), "Delete",
+     playlistwin_sub_menu_callback,
+     SUB_SELECTED, "<StockItem>", GTK_STOCK_REMOVE}
+};
+
+static GtkItemFactoryEntry pllist_menu_entries[] = {
+    {N_("/New List"), "<shift>N",
+     playlistwin_sub_menu_callback,
+     PLIST_NEW, "<StockItem>", GTK_STOCK_NEW},
+
+    ITEM_SEPARATOR,
+
+    {N_("/Load List"), "o",
+     playlistwin_sub_menu_callback,
+     PLIST_LOAD, "<StockItem>", GTK_STOCK_OPEN},
+
+    {N_("/Save List"), "<shift>S",
+     playlistwin_sub_menu_callback,
+     PLIST_SAVE, "<StockItem>", GTK_STOCK_SAVE},
+
+    {N_("/Save Default List"), "<alt>S",
+     playlistwin_sub_menu_callback,
+     PLIST_DEFAULTSAVE, "<StockItem>", GTK_STOCK_SAVE},
+
+    ITEM_SEPARATOR,
+
+    {N_("/Update View"), "F5",
+     playlistwin_sub_menu_callback,
+     PLAYLISTWIN_REFRESH, "<StockItem>", GTK_STOCK_REFRESH}
+};
+
+static GtkItemFactoryEntry plsel_menu_entries[] = {
+    {N_("/Invert Selection"), NULL,
+     playlistwin_sub_menu_callback,
+     SEL_INV, "<ImageItem>", selectinvert_pixbuf},
+    
+    ITEM_SEPARATOR,
+
+    {N_("/Select None"),"<Ctrl><Shift>A",
+     playlistwin_sub_menu_callback,
+     SEL_ZERO, "<ImageItem>", selectnone_pixbuf},
+
+    {N_("/Select All"), "<Ctrl>A",
+     playlistwin_sub_menu_callback,
+     SEL_ALL, "<ImageItem>", selectall_pixbuf},
+};
+
+static GtkItemFactoryEntry plsort_menu_entries[] = {
+    {N_("/Randomize List"), NULL, plsort_menu_callback,
+     PLAYLISTWIN_SORT_RANDOMIZE, "<ImageItem>", randomizepl_pixbuf},
+    {N_("/Reverse List"), NULL, plsort_menu_callback,
+     PLAYLISTWIN_SORT_REVERSE, "<ImageItem>", invertpl_pixbuf},
+    ITEM_SEPARATOR,
+    {N_("/Sort List"), NULL, NULL, 0, "<Branch>", NULL},
+    {N_("/Sort List/By Title"), NULL, plsort_menu_callback,
+     PLAYLISTWIN_SORT_BYTITLE, "<ImageItem>", sortbytitle_pixbuf},
+    {N_("/Sort List/By Artist"), NULL, plsort_menu_callback,
+     PLAYLISTWIN_SORT_BYARTIST, "<ImageItem>", sortbytitle_pixbuf},
+    {N_("/Sort List/By Filename"), NULL, plsort_menu_callback,
+     PLAYLISTWIN_SORT_BYFILENAME, "<ImageItem>", sortbyfilename_pixbuf},
+    {N_("/Sort List/By Path + Filename"), NULL, plsort_menu_callback,
+     PLAYLISTWIN_SORT_BYPATH, "<ImageItem>", sortbypathfile_pixbuf},
+    {N_("/Sort List/By Date"), NULL, plsort_menu_callback,
+     PLAYLISTWIN_SORT_BYDATE, "<ImageItem>", sortbydate_pixbuf},
+    {N_("/Sort List/By Track Number"), NULL, plsort_menu_callback,
+     PLAYLISTWIN_SORT_BYTRACK, "<ImageItem>", sortbytitle_pixbuf},
+    {N_("/Sort List/By Playlist Entry"), NULL, plsort_menu_callback,
+     PLAYLISTWIN_SORT_BYPLAYLIST, "<ImageItem>", sortbytitle_pixbuf},
+    {N_("/Sort Selection"), NULL, NULL, 0, "<Branch>", NULL},
+    {N_("/Sort Selection/By Title"), NULL, plsort_menu_callback,
+     PLAYLISTWIN_SORT_SEL_BYTITLE, "<ImageItem>", sortbytitle_pixbuf},
+    {N_("/Sort Selection/By Artist"), NULL, plsort_menu_callback,
+     PLAYLISTWIN_SORT_SEL_BYARTIST, "<ImageItem>", sortbytitle_pixbuf},
+    {N_("/Sort Selection/By Filename"), NULL, plsort_menu_callback,
+     PLAYLISTWIN_SORT_SEL_BYFILENAME, "<ImageItem>", sortbyfilename_pixbuf},
+    {N_("/Sort Selection/By Path + Filename"), NULL, plsort_menu_callback,
+     PLAYLISTWIN_SORT_SEL_BYPATH, "<ImageItem>", sortbypathfile_pixbuf},
+    {N_("/Sort Selection/By Date"), NULL, plsort_menu_callback,
+     PLAYLISTWIN_SORT_SEL_BYDATE, "<ImageItem>", sortbydate_pixbuf},
+    {N_("/Sort Selection/By Track Number"), NULL, plsort_menu_callback,
+     PLAYLISTWIN_SORT_SEL_BYTRACK, "<ImageItem>", sortbytitle_pixbuf},
+    {N_("/Sort Selection/By Playlist Entry"), NULL, plsort_menu_callback,
+     PLAYLISTWIN_SORT_SEL_BYPLAYLIST, "<ImageItem>", sortbytitle_pixbuf}
+};
+
+
+static void playlistwin_draw_frame(void);
+
+
+gboolean
+playlistwin_is_shaded(void)
+{
+    return cfg.playlist_shaded;
+}
+
+gint
+playlistwin_get_width(void)
+{
+    cfg.playlist_width /= PLAYLISTWIN_WIDTH_SNAP;
+    cfg.playlist_width *= PLAYLISTWIN_WIDTH_SNAP;
+    return cfg.playlist_width;
+}
+
+gint
+playlistwin_get_height_unshaded(void)
+{
+    cfg.playlist_height /= PLAYLISTWIN_HEIGHT_SNAP;
+    cfg.playlist_height *= PLAYLISTWIN_HEIGHT_SNAP;
+    gint height = cfg.playlist_height;
+    return 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();
+}
+
+void
+playlistwin_get_size(gint * width, gint * height)
+{
+    if (width)
+        *width = playlistwin_get_width();
+
+    if (height)
+        *height = playlistwin_get_height();
+}
+
+static void
+playlistwin_update_info(void)
+{
+    gchar *text, *sel_text, *tot_text;
+    gulong selection, total;
+    gboolean selection_more, total_more;
+
+    playlist_get_total_time(&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);
+    textbox_set_text(playlistwin_info, text);
+    g_free(text);
+    g_free(tot_text);
+    g_free(sel_text);
+}
+
+static void
+playlistwin_update_sinfo(void)
+{
+    gchar *posstr, *timestr, *title, *info, *dots;
+    gint pos, time;
+    guint max_len;
+
+    pos = playlist_get_position();
+    title = playlist_get_songtitle(pos);
+    time = playlist_get_songtime(pos);
+
+    if (!title) {
+        textbox_set_text(playlistwin_sinfo, "");
+        return;
+    }
+
+    if (cfg.show_numbers_in_pl)
+        posstr = g_strdup_printf("%d. ", pos + 1);
+    else
+        posstr = g_strdup("");
+
+    max_len = (playlistwin_get_width() - 35) / 5 - strlen(posstr);
+
+    if (time != -1) {
+        timestr = g_strdup_printf(" %d:%-2.2d", time / 60000,
+                                  (time / 1000) % 60);
+        max_len -= strlen(timestr);
+    }
+    else
+        timestr = g_strdup("");
+
+    convert_title_text(title);
+
+    if (strlen(title) > max_len) {
+        max_len -= 1;
+        dots = "\r";
+        /* textbox.c interprets \r as the ellipsis character, as there 
+           is none in ASCII. */
+    }
+    else
+        dots = "";
+
+    info = g_strdup_printf("%s%-*.*s%s%s", posstr, max_len, max_len,
+                           title, dots, timestr);
+    g_free(posstr);
+    g_free(title);
+    g_free(timestr);
+
+    textbox_set_text(playlistwin_sinfo, info);
+    g_free(info);
+}
+
+gboolean
+playlistwin_item_visible(gint index)
+{
+    if (index >= playlistwin_list->pl_first
+        && index <
+        (playlistwin_list->pl_first + playlistwin_list->pl_num_visible))
+        return TRUE;
+    return FALSE;
+}
+
+gint
+playlistwin_get_toprow(void)
+{
+    if (playlistwin_list)
+        return (playlistwin_list->pl_first);
+    return (-1);
+}
+
+void
+playlistwin_set_toprow(gint toprow)
+{
+    if (playlistwin_list)
+        playlistwin_list->pl_first = toprow;
+    playlistwin_update_list();
+}
+
+void
+playlistwin_update_list(void)
+{
+    g_return_if_fail(playlistwin_list != NULL);
+
+    widget_draw(WIDGET(playlistwin_list));
+    widget_draw(WIDGET(playlistwin_slider));
+    playlistwin_update_info();
+    playlistwin_update_sinfo();
+    /*     mainwin_update_jtf(); */
+}
+
+#if 0
+static void
+playlistwin_redraw_list(void)
+{
+    g_return_if_fail(playlistwin_list != NULL);
+
+    draw_widget(playlistwin_list);
+    draw_widget(playlistwin_slider);
+}
+#endif
+
+static void
+playlistwin_set_mask(void)
+{
+    GdkGC *gc;
+    GdkColor pattern;
+
+    if (playlistwin_mask)
+        g_object_unref(playlistwin_mask);
+
+    playlistwin_mask =
+        gdk_pixmap_new(playlistwin->window, playlistwin_get_width(),
+                       playlistwin_get_height(), 1);
+    gc = gdk_gc_new(playlistwin_mask);
+    pattern.pixel = 1;
+    gdk_gc_set_foreground(gc, &pattern);
+    gdk_draw_rectangle(playlistwin_mask, gc, TRUE, 0, 0,
+                       playlistwin_get_width(), playlistwin_get_height());
+    gdk_gc_destroy(gc);
+
+    gtk_widget_shape_combine_mask(playlistwin, playlistwin_mask, 0, 0);
+}
+
+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_shade(gboolean shaded)
+{
+    cfg.playlist_shaded = shaded;
+
+    if (shaded) {
+        widget_show(WIDGET(playlistwin_sinfo));
+        playlistwin_shade->pb_nx = 128;
+        playlistwin_shade->pb_ny = 45;
+        playlistwin_shade->pb_px = 150;
+        playlistwin_shade->pb_py = 42;
+        playlistwin_close->pb_nx = 138;
+        playlistwin_close->pb_ny = 45;
+    }
+    else {
+        widget_hide(WIDGET(playlistwin_sinfo));
+        playlistwin_shade->pb_nx = 157;
+        playlistwin_shade->pb_ny = 3;
+        playlistwin_shade->pb_px = 62;
+        playlistwin_shade->pb_py = 42;
+        playlistwin_close->pb_nx = 167;
+        playlistwin_close->pb_ny = 3;
+    }
+
+    dock_shade(dock_window_list, GTK_WINDOW(playlistwin),
+               playlistwin_get_height());
+
+    playlistwin_set_geometry_hints(cfg.playlist_shaded);
+
+    gtk_window_resize(GTK_WINDOW(playlistwin),
+                      playlistwin_get_width(),
+                      playlistwin_get_height());
+
+    playlistwin_set_mask();
+
+    widget_draw(WIDGET(playlistwin_list));
+    widget_draw(WIDGET(playlistwin_slider));
+
+    draw_playlist_window(TRUE);
+}
+
+static void
+playlistwin_set_shade_menu(gboolean shaded)
+{
+    GtkWidget *item;
+
+    item = gtk_item_factory_get_widget(mainwin_view_menu,
+                                       "/Roll up Playlist Editor");
+    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), shaded);
+
+    playlistwin_set_shade(shaded);
+}
+
+void
+playlistwin_shade_toggle(void)
+{
+    playlistwin_set_shade_menu(!cfg.playlist_shaded);
+}
+
+static void
+playlistwin_release(GtkWidget * widget,
+                    GdkEventButton * event,
+                    gpointer callback_data)
+{
+    if (event->button == 3)
+        return;
+
+    gdk_pointer_ungrab(GDK_CURRENT_TIME);
+    playlistwin_resizing = FALSE;
+    gdk_flush();
+ 
+    if (dock_is_moving(GTK_WINDOW(playlistwin)))
+    {
+       dock_move_release(GTK_WINDOW(playlistwin));
+
+       if (cfg.playlist_transparent)
+           playlistwin_update_list();
+    }
+    else
+    {
+       handle_release_cb(playlistwin_wlist, widget, event);
+       draw_playlist_window(FALSE);
+    }
+}
+
+void
+playlistwin_scroll(gint num)
+{
+    playlistwin_list->pl_first += num;
+    playlistwin_update_list();
+}
+
+void
+playlistwin_scroll_up_pushed(void)
+{
+    playlistwin_list->pl_first -= 3;
+    playlistwin_update_list();
+}
+
+void
+playlistwin_scroll_down_pushed(void)
+{
+    playlistwin_list->pl_first += 3;
+    playlistwin_update_list();
+}
+
+static void
+playlistwin_select_all(void)
+{
+    playlist_select_all(TRUE);
+    playlistwin_list->pl_prev_selected = 0;
+    playlistwin_list->pl_prev_min = 0;
+    playlistwin_list->pl_prev_max = playlist_get_length() - 1;
+    playlistwin_update_list();
+}
+
+static void
+playlistwin_select_none(void)
+{
+    playlist_select_all(FALSE);
+    playlistwin_list->pl_prev_selected = -1;
+    playlistwin_list->pl_prev_min = -1;
+    playlistwin_update_list();
+}
+
+static void
+playlistwin_inverse_selection(void)
+{
+    playlist_select_invert_all();
+    playlistwin_list->pl_prev_selected = -1;
+    playlistwin_list->pl_prev_min = -1;
+    playlistwin_update_list();
+}
+
+static void
+playlistwin_resize(gint width, gint height)
+{
+    gboolean redraw;
+
+    g_return_if_fail(width > 0 && height > 0);
+
+    cfg.playlist_width = width;
+
+    if (!cfg.playlist_shaded)
+        cfg.playlist_height = height;
+    else
+        height = cfg.playlist_height;
+
+    /* FIXME: why the fsck are we doing this manually? */
+    /* adjust widget positions and sizes */
+
+    widget_resize(WIDGET(playlistwin_list), width - 31, height - 58);
+
+    widget_move(WIDGET(playlistwin_slider), width - 15, 20);
+    widget_resize(WIDGET(playlistwin_slider), 8, height - 58);
+
+    widget_resize(WIDGET(playlistwin_sinfo), width - 35, 14);
+    playlistwin_update_sinfo();
+
+    widget_move(WIDGET(playlistwin_shade), width - 21, 3);
+    widget_move(WIDGET(playlistwin_close), width - 11, 3);
+    widget_move(WIDGET(playlistwin_time_min), width - 82, height - 15);
+    widget_move(WIDGET(playlistwin_time_sec), width - 64, height - 15);
+    widget_move(WIDGET(playlistwin_info), width - 143, height - 28);
+    widget_move(WIDGET(playlistwin_srew), width - 144, height - 16);
+    widget_move(WIDGET(playlistwin_splay), width - 138, height - 16);
+    widget_move(WIDGET(playlistwin_spause), width - 128, height - 16);
+    widget_move(WIDGET(playlistwin_sstop), width - 118, height - 16);
+    widget_move(WIDGET(playlistwin_sfwd), width - 109, height - 16);
+    widget_move(WIDGET(playlistwin_seject), width - 100, height - 16);
+    widget_move(WIDGET(playlistwin_sscroll_up), width - 14, height - 35);
+    widget_move(WIDGET(playlistwin_sscroll_down), width - 14, height - 30);
+
+    g_object_unref(playlistwin_bg);
+    playlistwin_bg = gdk_pixmap_new(playlistwin->window, width, height, -1);
+    playlistwin_set_mask();
+
+    widget_list_lock(playlistwin_wlist);
+
+    widget_list_change_pixmap(playlistwin_wlist, playlistwin_bg);
+    playlistwin_draw_frame();
+    widget_list_draw(playlistwin_wlist, &redraw, TRUE);
+    widget_list_clear_redraw(playlistwin_wlist);
+
+    widget_list_unlock(playlistwin_wlist);
+
+    gdk_window_set_back_pixmap(playlistwin->window, playlistwin_bg, 0);
+    gdk_window_clear(playlistwin->window);
+}
+
+
+
+static void
+playlistwin_motion(GtkWidget * widget,
+                   GdkEventMotion * event,
+                   gpointer callback_data)
+{
+    GdkEvent *gevent;
+
+    if (dock_is_moving(GTK_WINDOW(playlistwin))) {
+        dock_move_motion(GTK_WINDOW(playlistwin), event);
+    }
+    else {
+        handle_motion_cb(playlistwin_wlist, widget, event);
+        draw_playlist_window(FALSE);
+    }
+    gdk_flush();
+
+    while ((gevent = gdk_event_get()) != NULL) gdk_event_free(gevent);
+}
+
+static void
+playlistwin_enter(GtkWidget * widget,
+                   GdkEventMotion * event,
+                   gpointer callback_data)
+{
+    playlistwin_list->pl_tooltips = TRUE;
+}
+
+static void
+playlistwin_leave(GtkWidget * widget,
+                   GdkEventMotion * event,
+                   gpointer callback_data)
+{
+    playlistwin_list->pl_tooltips = FALSE;
+}
+
+static void
+playlistwin_show_filebrowser(void)
+{
+    util_run_filebrowser(NO_PLAY_BUTTON);
+}
+
+#if 0
+static void
+playlistwin_add_dir_handler(const gchar * dir)
+{
+    g_free(cfg.filesel_path);
+    cfg.filesel_path = g_strdup(dir);
+    playlist_add_dir(dir);
+}
+#endif
+
+static void
+playlistwin_fileinfo(void)
+{
+    /* Show the first selected file, or the current file if nothing is
+     * selected */
+    GList *list = playlist_get_selected();
+    if (list) {
+        playlist_fileinfo(GPOINTER_TO_INT(list->data));
+        g_list_free(list);
+    }
+    else
+        playlist_fileinfo_current();
+}
+
+static void
+menu_set_item_sensitive(GtkItemFactory * item_factory,
+                        const gchar * path,
+                        gboolean sensitive)
+{
+    GtkWidget *item = gtk_item_factory_get_widget(item_factory, path);
+    gtk_widget_set_sensitive(item, sensitive);
+}
+
+/* FIXME: broken */
+static void
+playlistwin_set_sensitive_sortmenu(void)
+{
+    gboolean set = playlist_get_num_selected() > 1;
+    menu_set_item_sensitive(plsort_menu, "/Sort Selection/By Title", set);
+    menu_set_item_sensitive(plsort_menu, "/Sort Selection/By Filename", set);
+    menu_set_item_sensitive(plsort_menu, "/Sort Selection/By Path + Filename", set);
+    menu_set_item_sensitive(plsort_menu, "/Sort Selection/By Date", set);
+}
+
+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 != NULL);
+
+    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_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);
+
+    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_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 = playlist_container_find(ext);
+    if (plc == NULL) {
+        show_playlist_save_format_error(GTK_WINDOW(playlistwin), filename);
+        return;
+    }
+
+    str_replace_in(&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 (!playlist_save(filename))
+        show_playlist_save_error(GTK_WINDOW(playlistwin), filename);
+}
+
+#if 0
+static void
+playlistwin_save_current(void)
+{
+    const gchar *filename;
+
+    if (!(filename = playlist_get_current_name()))
+        return;
+
+    playlistwin_save_playlist(filename);
+}
+#endif
+
+static void
+playlistwin_load_playlist(const gchar * filename)
+{
+    g_return_if_fail(filename != NULL);
+
+    str_replace_in(&cfg.playlist_path, g_strdup(filename));
+
+    playlist_clear();
+    mainwin_clear_song_info();
+    mainwin_set_info_text();
+
+    playlist_load(filename);
+    playlist_set_current_name(filename);
+}
+
+static gchar *
+playlist_file_selection(const gchar * title,
+                        gboolean save,
+                        const gchar * default_filename)
+{
+    GtkWidget *dialog, *button;
+    gchar *filename;
+
+    g_return_val_if_fail(title != NULL, NULL);
+
+    dialog = gtk_file_chooser_dialog_new(title, GTK_WINDOW(mainwin),
+                                         save ? GTK_FILE_CHOOSER_ACTION_SAVE :
+                                         GTK_FILE_CHOOSER_ACTION_OPEN, NULL, NULL);
+    if (default_filename)
+        gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog),
+                                      default_filename);
+
+    button = gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL,
+                                   GTK_RESPONSE_REJECT);
+    gtk_button_set_use_stock(GTK_BUTTON(button), TRUE);
+    GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+
+    button = gtk_dialog_add_button(GTK_DIALOG(dialog),
+                                   save ? GTK_STOCK_SAVE : GTK_STOCK_OPEN,
+                                   GTK_RESPONSE_ACCEPT);
+    gtk_button_set_use_stock(GTK_BUTTON(button), TRUE);
+    gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
+
+    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 Playlist"), FALSE, default_filename);
+
+    if (filename) {
+        playlistwin_load_playlist(filename);
+        g_free(filename);
+    }
+}
+
+static void
+playlistwin_select_playlist_to_save(const gchar * default_filename)
+{
+    gchar *filename =
+        playlist_file_selection(_("Save Playlist"), TRUE, default_filename);
+
+    if (filename) {
+        /* Default to M3U if no filename has extension */
+
+        /* NOTE: This doesn't work correctly for hidden files
+           - descender */
+        if (!strchr(filename, '.')) {
+            gchar *tmpstr = filename;
+            filename = g_strconcat(filename, ".m3u", NULL);
+            g_free(tmpstr);
+        }
+
+        playlistwin_save_playlist(filename);
+        g_free(filename);
+    }
+}
+
+static gboolean
+inside_sensitive_widgets(gint x, gint y)
+{
+    return (widget_contains(WIDGET(playlistwin_list), x, y) ||
+            widget_contains(WIDGET(playlistwin_slider), x, y) ||
+            widget_contains(WIDGET(playlistwin_close), x, y) ||
+            widget_contains(WIDGET(playlistwin_shade), x, y) ||
+            widget_contains(WIDGET(playlistwin_time_min), x, y) ||
+            widget_contains(WIDGET(playlistwin_time_sec), x, y) ||
+            widget_contains(WIDGET(playlistwin_info), x, y) ||
+            widget_contains(WIDGET(playlistwin_srew), x, y) ||
+            widget_contains(WIDGET(playlistwin_splay), x, y) ||
+            widget_contains(WIDGET(playlistwin_spause), x, y) ||
+            widget_contains(WIDGET(playlistwin_sstop), x, y) ||
+            widget_contains(WIDGET(playlistwin_sfwd), x, y) ||
+            widget_contains(WIDGET(playlistwin_seject), x, y) ||
+            widget_contains(WIDGET(playlistwin_sscroll_up), x, y) ||
+            widget_contains(WIDGET(playlistwin_sscroll_down), x, y));
+}
+
+#define REGION_L(x1,x2,y1,y2)                   \
+    (event->x >= (x1) && event->x < (x2) &&     \
+     event->y >= cfg.playlist_height - (y1) &&  \
+     event->y < cfg.playlist_height - (y2))
+
+#define REGION_R(x1,x2,y1,y2)                      \
+    (event->x >= playlistwin_get_width() - (x1) && \
+     event->x < playlistwin_get_width() - (x2) &&  \
+     event->y >= cfg.playlist_height - (y1) &&     \
+     event->y < cfg.playlist_height - (y2))
+
+static void
+playlistwin_scrolled(GtkWidget * widget,
+                     GdkEventScroll * event,
+                     gpointer callback_data)
+{
+
+    if (event->direction == GDK_SCROLL_DOWN)
+        playlistwin_scroll(cfg.scroll_pl_by);
+
+    if (event->direction == GDK_SCROLL_UP)
+        playlistwin_scroll(-cfg.scroll_pl_by);
+
+}
+
+
+
+
+static gboolean
+playlistwin_press(GtkWidget * widget,
+                  GdkEventButton * event,
+                  gpointer callback_data)
+{
+    gboolean grab = TRUE;
+    gint xpos, ypos;
+    GtkWidget *_menu;
+    GtkRequisition req;
+
+    gtk_window_get_position(GTK_WINDOW(playlistwin), &xpos, &ypos);
+
+    if (event->button == 1 && !cfg.show_wm_decorations &&
+        ((!cfg.playlist_shaded &&
+          event->x > playlistwin_get_width() - 20 &&
+          event->y > cfg.playlist_height - 20) ||
+         (cfg.playlist_shaded &&
+          event->x >= playlistwin_get_width() - 31 &&
+          event->x < playlistwin_get_width() - 22))) {
+
+        /* NOTE: Workaround for bug #214 */
+        if (event->type != GDK_2BUTTON_PRESS && 
+            event->type != GDK_3BUTTON_PRESS) {
+            /* resize area */
+            playlistwin_resizing = TRUE;
+            gtk_window_begin_resize_drag(GTK_WINDOW(widget),
+                                         GDK_WINDOW_EDGE_SOUTH_EAST,
+                                         event->button,
+                                         event->x + xpos, event->y + ypos,
+                                         event->time);
+        }
+        grab = FALSE;
+    }
+    else if (event->button == 1 && REGION_L(12, 37, 29, 11)) {
+        /* ADD button menu */
+
+        _menu = GTK_WIDGET(pladd_menu->widget);
+        if (!GTK_WIDGET_REALIZED(_menu)) gtk_widget_realize(_menu);
+        gtk_widget_size_request(_menu, &req);
+        gtk_item_factory_popup_with_data(pladd_menu,
+                                         NULL, NULL,
+                                         xpos+12,
+                                         (ypos + playlistwin_get_height()) - 8 - req.height, 1, event->time);
+        grab = FALSE;
+    }
+    else if (event->button == 1 && REGION_L(41, 66, 29, 11)) {
+        /* SUB button menu */
+        _menu = GTK_WIDGET(pldel_menu->widget);
+        if (!GTK_WIDGET_REALIZED(_menu)) gtk_widget_realize(_menu);
+        gtk_widget_size_request(_menu, &req);
+        gtk_item_factory_popup_with_data(pldel_menu,
+                                         NULL, NULL,
+                                         xpos+40,
+                                         (ypos + playlistwin_get_height()) - 8 - req.height, 1, event->time);
+        grab = FALSE;
+    }
+    else if (event->button == 1 && REGION_L(70, 95, 29, 11)) {
+        /* SEL button menu */
+        _menu = GTK_WIDGET(plsel_menu->widget);
+        if (!GTK_WIDGET_REALIZED(_menu)) gtk_widget_realize(_menu);
+        gtk_widget_size_request(_menu, &req);
+        gtk_item_factory_popup_with_data(plsel_menu,
+                                         NULL, NULL,
+                                         xpos+68,
+                                         (ypos + playlistwin_get_height()) - 8 - req.height, 1, event->time);
+
+        grab = FALSE;
+    }
+    else if (event->button == 1 && REGION_L(99, 124, 29, 11)) {
+        /* MISC button menu */
+        _menu = GTK_WIDGET(plsort_menu->widget);
+        if (!GTK_WIDGET_REALIZED(_menu)) gtk_widget_realize(_menu);
+        gtk_widget_size_request(_menu, &req);
+        gtk_item_factory_popup_with_data(plsort_menu,
+                                         NULL, NULL,
+                                         xpos+100,
+                                         (ypos + playlistwin_get_height()) - 8 - req.height, 1, event->time);
+        grab = FALSE;
+    }
+    else if (event->button == 1 && REGION_R(46, 23, 29, 11)) {
+        /* LIST button menu */
+        _menu = GTK_WIDGET(pllist_menu->widget);
+        if (!GTK_WIDGET_REALIZED(_menu)) gtk_widget_realize(_menu);
+        gtk_widget_size_request(_menu, &req);
+        gtk_item_factory_popup_with_data(pllist_menu,
+                                         NULL, NULL,
+                                         xpos + playlistwin_get_width() - req.width - 12,
+                                         (ypos + playlistwin_get_height()) - 8 - req.height, 1, event->time);
+        grab = FALSE;
+    }
+    else if (event->button == 1 && REGION_R(82, 54, 15, 9)) {
+        if (cfg.timer_mode == TIMER_ELAPSED)
+            cfg.timer_mode = TIMER_REMAINING;
+        else
+            cfg.timer_mode = TIMER_ELAPSED;
+    }
+    else if (event->button == 2 && (event->type == GDK_BUTTON_PRESS) &&
+             widget_contains(WIDGET(playlistwin_list), event->x, event->y)) {
+        gtk_selection_convert(widget, GDK_SELECTION_PRIMARY,
+                              GDK_TARGET_STRING, event->time);
+    }
+    else if (event->button == 1 && event->type == GDK_BUTTON_PRESS &&
+             !inside_sensitive_widgets(event->x, event->y) && event->y < 14)
+    {
+        dock_move_press(dock_window_list, GTK_WINDOW(playlistwin), event,
+                        FALSE);
+        gtk_window_present(GTK_WINDOW(playlistwin));
+    }
+    else if (event->button == 1 && event->type == GDK_2BUTTON_PRESS &&
+             !inside_sensitive_widgets(event->x, event->y)
+             && 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 &&
+             !(widget_contains(WIDGET(playlistwin_list), event->x, event->y) ||
+               (event->y >= cfg.playlist_height - 29 &&
+                event->y < cfg.playlist_height - 11 &&
+                ((event->x >= 12 && event->x < 37) ||
+                 (event->x >= 41 && event->x < 66) ||
+                 (event->x >= 70 && event->x < 95) ||
+                 (event->x >= 99 && event->x < 124) ||
+                 (event->x >= playlistwin_get_width() - 46 &&
+                  event->x < playlistwin_get_width() - 23))))) {
+        /*
+         * Pop up the main menu a few pixels down to avoid
+         * anything to be selected initially.
+         */
+        util_item_factory_popup(mainwin_general_menu, event->x_root,
+                                event->y_root + 2, 3, event->time);
+        grab = FALSE;
+    }
+    else if (event->button == 3 &&
+             widget_contains(WIDGET(playlistwin_list), event->x, event->y)) {
+        /* popup menu */
+        playlistwin_set_sensitive_sortmenu();
+        {
+            GtkWidget *item = gtk_item_factory_get_widget(playlistwin_popup_menu, "/Show Popup Info");
+            gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), cfg.show_filepopup_for_tuple);
+        }
+        gtk_item_factory_popup(playlistwin_popup_menu,
+                               event->x_root, event->y_root + 5,
+                               3, event->time);
+        grab = FALSE;
+    }
+    else {
+        handle_press_cb(playlistwin_wlist, widget, event);
+        draw_playlist_window(FALSE);
+    }
+
+    if (grab)
+        gdk_pointer_grab(playlistwin->window, FALSE,
+                         GDK_BUTTON_MOTION_MASK | GDK_BUTTON_RELEASE_MASK |
+                         GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
+                         GDK_BUTTON1_MOTION_MASK, NULL, NULL,
+                         GDK_CURRENT_TIME);
+
+    return FALSE;
+}
+
+static gboolean
+playlistwin_focus_in(GtkWidget * widget, GdkEvent * event, gpointer data)
+{
+    playlistwin_close->pb_allow_draw = TRUE;
+    playlistwin_shade->pb_allow_draw = TRUE;
+    draw_playlist_window(TRUE);
+    return FALSE;
+}
+
+static gboolean
+playlistwin_focus_out(GtkWidget * widget,
+                      GdkEventButton * event, gpointer data)
+{
+    playlistwin_close->pb_allow_draw = FALSE;
+    playlistwin_shade->pb_allow_draw = FALSE;
+    draw_playlist_window(TRUE);
+    return FALSE;
+}
+
+static gboolean
+playlistwin_configure(GtkWidget * window,
+                      GdkEventConfigure * event, gpointer data)
+{
+    if (!GTK_WIDGET_VISIBLE(window))
+        return FALSE;
+
+    cfg.playlist_x = event->x;
+    cfg.playlist_y = event->y;
+
+    if (playlistwin_resizing) {
+        if (event->width != playlistwin_get_width() ||
+            event->height != playlistwin_get_height())
+            playlistwin_resize(event->width, event->height);
+    }
+    return TRUE;
+}
+
+void
+playlistwin_set_back_pixmap(void)
+{
+    gdk_window_set_back_pixmap(playlistwin->window, playlistwin_bg, 0);
+    gdk_window_clear(playlistwin->window);
+}
+
+static gboolean
+playlistwin_delete(GtkWidget * w, gpointer data)
+{
+    playlistwin_hide();
+    return TRUE;
+}
+
+static void
+playlistwin_keypress_up_down_handler(PlayList_List * pl,
+                                     gboolean up, guint state)
+{
+    if ((state & GDK_MOD1_MASK) && (state & GDK_SHIFT_MASK))
+        return;
+    if (!(state & GDK_MOD1_MASK))
+        playlist_select_all(FALSE);
+
+    if (pl->pl_prev_selected == -1 ||
+        (!playlistwin_item_visible(pl->pl_prev_selected) &&
+         !(state & GDK_SHIFT_MASK && pl->pl_prev_min != -1))) {
+        pl->pl_prev_selected = pl->pl_first;
+    }
+    else if (state & GDK_SHIFT_MASK) {
+        if (pl->pl_prev_min == -1) {
+            pl->pl_prev_max = pl->pl_prev_selected;
+            pl->pl_prev_min = pl->pl_prev_selected;
+        }
+        pl->pl_prev_max += (up ? -1 : 1);
+        pl->pl_prev_max =
+            CLAMP(pl->pl_prev_max, 0, playlist_get_length() - 1);
+
+        pl->pl_first = MIN(pl->pl_first, pl->pl_prev_max);
+        pl->pl_first = MAX(pl->pl_first, pl->pl_prev_max -
+                           pl->pl_num_visible + 1);
+        playlist_select_range(pl->pl_prev_min, pl->pl_prev_max, TRUE);
+        return;
+    }
+    else if (state & GDK_MOD1_MASK) {
+        if (up)
+            playlist_list_move_up(pl);
+        else
+            playlist_list_move_down(pl);
+        if (pl->pl_prev_min < pl->pl_first)
+            pl->pl_first = pl->pl_prev_min;
+        else if (pl->pl_prev_max >= (pl->pl_first + pl->pl_num_visible))
+            pl->pl_first = pl->pl_prev_max - pl->pl_num_visible + 1;
+        return;
+    }
+    else if (up)
+        pl->pl_prev_selected--;
+    else
+        pl->pl_prev_selected++;
+
+    pl->pl_prev_selected =
+        CLAMP(pl->pl_prev_selected, 0, playlist_get_length() - 1);
+
+    if (pl->pl_prev_selected < pl->pl_first)
+        pl->pl_first--;
+    else if (pl->pl_prev_selected >= (pl->pl_first + pl->pl_num_visible))
+        pl->pl_first++;
+
+    playlist_select_range(pl->pl_prev_selected, pl->pl_prev_selected, TRUE);
+    pl->pl_prev_min = -1;
+
+}
+
+/* FIXME: Handle the keys through menu */
+
+static gboolean
+playlistwin_keypress(GtkWidget * w, GdkEventKey * event, gpointer data)
+{
+    guint keyval;
+    gboolean refresh = FALSE;
+
+    if (cfg.playlist_shaded)
+        return FALSE;
+
+    switch (keyval = event->keyval) {
+    case GDK_KP_Up:
+    case GDK_KP_Down:
+    case GDK_Up:
+    case GDK_Down:
+        playlistwin_keypress_up_down_handler(playlistwin_list,
+                                             keyval == GDK_Up
+                                             || keyval == GDK_KP_Up,
+                                             event->state);
+        refresh = TRUE;
+        break;
+    case GDK_Page_Up:
+        playlistwin_scroll(-playlistwin_list->pl_num_visible);
+        refresh = TRUE;
+        break;
+    case GDK_Page_Down:
+        playlistwin_scroll(playlistwin_list->pl_num_visible);
+        refresh = TRUE;
+        break;
+    case GDK_Home:
+        playlistwin_list->pl_first = 0;
+        refresh = TRUE;
+        break;
+    case GDK_End:
+        playlistwin_list->pl_first =
+            playlist_get_length() - playlistwin_list->pl_num_visible;
+        refresh = TRUE;
+        break;
+    case GDK_Return:
+        if (playlistwin_list->pl_prev_selected > -1
+            && playlistwin_item_visible(playlistwin_list->pl_prev_selected)) {
+            playlist_set_position(playlistwin_list->pl_prev_selected);
+            if (!bmp_playback_get_playing())
+                bmp_playback_initiate();
+        }
+        break;
+    case GDK_3:
+        if (event->state & GDK_CONTROL_MASK)
+            playlistwin_fileinfo();
+        break;
+    case GDK_Delete:
+        if (event->state & GDK_CONTROL_MASK)
+            playlist_delete(TRUE);
+        else
+            playlist_delete(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 (playlist_get_current_length() != -1)
+            bmp_playback_seek(CLAMP
+                              (bmp_playback_get_time() - 1000, 0,
+                              playlist_get_current_length()) / 1000);
+        break;
+    case GDK_Right:
+    case GDK_KP_Right:
+    case GDK_KP_9:
+        if (playlist_get_current_length() != -1)
+            bmp_playback_seek(CLAMP
+                              (bmp_playback_get_time() + 1000, 0,
+                              playlist_get_current_length()) / 1000);
+        break;
+
+    case GDK_Escape:
+        mainwin_minimize_cb();
+        break;
+    default:
+        return FALSE;
+    }
+
+    if (refresh)
+        playlistwin_update_list();
+
+    return TRUE;
+}
+
+static void
+playlistwin_draw_frame(void)
+{
+    gboolean focus =
+        gtk_window_has_toplevel_focus(GTK_WINDOW(playlistwin)) ||
+        !cfg.dim_titlebar;
+
+    if (cfg.playlist_shaded) {
+        skin_draw_playlistwin_shaded(bmp_active_skin,
+                                     playlistwin_bg, playlistwin_gc,
+                                     playlistwin_get_width(), focus);
+    }
+    else {
+        skin_draw_playlistwin_frame(bmp_active_skin,
+                                    playlistwin_bg, playlistwin_gc,
+                                    playlistwin_get_width(),
+                                    cfg.playlist_height, focus);
+    }
+}
+
+void
+draw_playlist_window(gboolean force)
+{
+    gboolean redraw;
+    GList *wl;
+    Widget *w;
+
+    if (force)
+        playlistwin_draw_frame();
+
+    widget_list_lock(playlistwin_wlist);
+    widget_list_draw(playlistwin_wlist, &redraw, force);
+
+    if (redraw || force) {
+        if (force) {
+            gdk_window_clear(playlistwin->window);
+        }
+        else {
+            for (wl = playlistwin_wlist; wl; wl = g_list_next(wl)) {
+                w = WIDGET(wl->data);
+                if (w->redraw && w->visible) {
+                    gdk_window_clear_area(playlistwin->window, w->x, w->y,
+                                          w->width, w->height);
+                    w->redraw = FALSE;
+                }
+            }
+        }
+
+        gdk_flush();
+    }
+
+    widget_list_unlock(playlistwin_wlist);
+}
+
+
+void
+playlistwin_hide_timer(void)
+{
+    textbox_set_text(playlistwin_time_min, "   ");
+    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);
+    textbox_set_text(playlistwin_time_min, text);
+    g_free(text);
+
+    text = g_strdup_printf("%-2.2d", time % 60);
+    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)
+{
+    playlistwin_list->pl_drag_motion = TRUE;
+    playlistwin_list->drag_motion_x = x;
+    playlistwin_list->drag_motion_y = y;
+    playlistwin_update_list();
+    playlistwin_hint_flag = TRUE;
+}
+
+static void
+playlistwin_drag_end(GtkWidget * widget,
+                     GdkDragContext * context, gpointer user_data)
+{
+    playlistwin_list->pl_drag_motion = FALSE;
+    playlistwin_hint_flag = FALSE;
+    playlistwin_update_list();
+}
+
+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;
+
+    g_return_if_fail(selection_data != NULL);
+
+    if (!selection_data->data) {
+        g_message("Received no DND data!");
+        return;
+    }
+
+    if (widget_contains(WIDGET(playlistwin_list), x, y)) {
+        pos = (y - WIDGET(playlistwin_list)->y) /
+            playlistwin_list->pl_fheight + playlistwin_list->pl_first;
+
+        pos = MIN(pos, playlist_get_length());
+        playlist_ins_url((gchar *) selection_data->data, pos);
+    }
+    else
+        playlist_add_url((gchar *) selection_data->data);
+}
+
+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 =
+        create_textbox(&playlistwin_wlist, playlistwin_bg, playlistwin_gc,
+                       4, 4, playlistwin_get_width() - 35, FALSE, SKIN_TEXT);
+
+    if (!cfg.playlist_shaded)
+        widget_hide(WIDGET(playlistwin_sinfo));
+
+    /* shade/unshade window push button */
+    if (cfg.playlist_shaded)
+        playlistwin_shade =
+            create_pbutton(&playlistwin_wlist, playlistwin_bg,
+                           playlistwin_gc, playlistwin_get_width() - 21, 3,
+                           9, 9, 128, 45, 150, 42,
+                           playlistwin_shade_toggle, SKIN_PLEDIT);
+    else
+        playlistwin_shade =
+            create_pbutton(&playlistwin_wlist, playlistwin_bg,
+                           playlistwin_gc, playlistwin_get_width() - 21, 3,
+                           9, 9, 157, 3, 62, 42, playlistwin_shade_toggle,
+                           SKIN_PLEDIT);
+
+    playlistwin_shade->pb_allow_draw = FALSE;
+
+    /* close window push button */
+    playlistwin_close =
+        create_pbutton(&playlistwin_wlist, playlistwin_bg, playlistwin_gc,
+                       playlistwin_get_width() - 11, 3, 9, 9,
+                       cfg.playlist_shaded ? 138 : 167,
+                       cfg.playlist_shaded ? 45 : 3, 52, 42,
+                       playlistwin_hide, SKIN_PLEDIT);
+    playlistwin_close->pb_allow_draw = FALSE;
+
+    /* playlist list box */
+    playlistwin_list =
+        create_playlist_list(&playlistwin_wlist, playlistwin_bg,
+                             playlistwin_gc, 12, 20,
+                             playlistwin_get_width() - 31,
+                             cfg.playlist_height - 58);
+    playlist_list_set_font(cfg.playlist_font);
+
+    /* playlist list box slider */
+    playlistwin_slider =
+        create_playlistslider(&playlistwin_wlist, playlistwin_bg,
+                              playlistwin_gc, playlistwin_get_width() - 15,
+                              20, cfg.playlist_height - 58, playlistwin_list);
+    /* track time (minute) */
+    playlistwin_time_min =
+        create_textbox(&playlistwin_wlist, playlistwin_bg, playlistwin_gc,
+                       playlistwin_get_width() - 82,
+                       cfg.playlist_height - 15, 15, FALSE, SKIN_TEXT);
+
+    /* track time (second) */
+    playlistwin_time_sec =
+        create_textbox(&playlistwin_wlist, playlistwin_bg, playlistwin_gc,
+                       playlistwin_get_width() - 64,
+                       cfg.playlist_height - 15, 10, FALSE, SKIN_TEXT);
+
+    /* playlist information (current track length / total track length) */
+    playlistwin_info =
+        create_textbox(&playlistwin_wlist, playlistwin_bg, playlistwin_gc,
+                       playlistwin_get_width() - 143,
+                       cfg.playlist_height - 28, 90, FALSE, SKIN_TEXT);
+
+    /* mini play control buttons at right bottom corner */
+
+    /* rewind button */
+    playlistwin_srew =
+        create_sbutton(&playlistwin_wlist, playlistwin_bg, playlistwin_gc,
+                       playlistwin_get_width() - 144,
+                       cfg.playlist_height - 16, 8, 7, playlist_prev);
+
+    /* play button */
+    playlistwin_splay =
+        create_sbutton(&playlistwin_wlist, playlistwin_bg, playlistwin_gc,
+                       playlistwin_get_width() - 138,
+                       cfg.playlist_height - 16, 10, 7, mainwin_play_pushed);
+
+    /* pause button */
+    playlistwin_spause =
+        create_sbutton(&playlistwin_wlist, playlistwin_bg, playlistwin_gc,
+                       playlistwin_get_width() - 128,
+                       cfg.playlist_height - 16, 10, 7, bmp_playback_pause);
+
+    /* stop button */
+    playlistwin_sstop =
+        create_sbutton(&playlistwin_wlist, playlistwin_bg, playlistwin_gc,
+                       playlistwin_get_width() - 118,
+                       cfg.playlist_height - 16, 9, 7, mainwin_stop_pushed);
+
+    /* forward button */
+    playlistwin_sfwd =
+        create_sbutton(&playlistwin_wlist, playlistwin_bg, playlistwin_gc,
+                       playlistwin_get_width() - 109,
+                       cfg.playlist_height - 16, 8, 7, playlist_next);
+
+    /* eject button */
+    playlistwin_seject =
+        create_sbutton(&playlistwin_wlist, playlistwin_bg, playlistwin_gc,
+                       playlistwin_get_width() - 100,
+                       cfg.playlist_height - 16, 9, 7, mainwin_eject_pushed);
+
+
+    playlistwin_sscroll_up =
+        create_sbutton(&playlistwin_wlist, playlistwin_bg, playlistwin_gc,
+                       playlistwin_get_width() - 14,
+                       cfg.playlist_height - 35, 8, 5,
+                       playlistwin_scroll_up_pushed);
+    playlistwin_sscroll_down =
+        create_sbutton(&playlistwin_wlist, playlistwin_bg, playlistwin_gc,
+                       playlistwin_get_width() - 14,
+                       cfg.playlist_height - 30, 8, 5,
+                       playlistwin_scroll_down_pushed);
+
+}
+
+static void
+selection_received(GtkWidget * widget,
+                   GtkSelectionData * selection_data, gpointer data)
+{
+    if (selection_data->type == GDK_SELECTION_TYPE_STRING &&
+        selection_data->length > 0)
+        playlist_add_url((gchar *) selection_data->data);
+}
+
+static void
+playlistwin_create_window(void)
+{
+    GdkPixbuf *icon;
+
+    playlistwin = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_title(GTK_WINDOW(playlistwin), _("Audacious Playlist Editor"));
+    gtk_window_set_wmclass(GTK_WINDOW(playlistwin), "playlist", "Audacious");
+    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(cfg.playlist_shaded);
+    dock_window_list = dock_window_set_decorated(dock_window_list,
+                                                 GTK_WINDOW(playlistwin),
+                                                 cfg.show_wm_decorations);
+
+    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 **) bmp_playlist_icon);
+    gtk_window_set_icon(GTK_WINDOW(playlistwin), icon);
+    g_object_unref(icon);
+
+    gtk_widget_set_app_paintable(playlistwin, TRUE);
+
+    if (cfg.playlist_x != -1 && cfg.save_window_position)
+        gtk_window_move(GTK_WINDOW(playlistwin),
+                        cfg.playlist_x, cfg.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);
+
+    util_set_cursor(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);
+    g_signal_connect(playlistwin, "enter_notify_event",
+                     G_CALLBACK(playlistwin_enter), NULL);
+    g_signal_connect(playlistwin, "leave_notify_event",
+                     G_CALLBACK(playlistwin_leave), NULL);
+    g_signal_connect_after(playlistwin, "focus_in_event",
+                           G_CALLBACK(playlistwin_focus_in), NULL);
+    g_signal_connect_after(playlistwin, "focus_out_event",
+                           G_CALLBACK(playlistwin_focus_out), NULL);
+    g_signal_connect(playlistwin, "configure_event",
+                     G_CALLBACK(playlistwin_configure), NULL);
+    g_signal_connect(playlistwin, "style_set",
+                     G_CALLBACK(playlistwin_set_back_pixmap), NULL);
+
+    bmp_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);
+
+    playlistwin_set_mask();
+}
+
+void
+playlistwin_create_popup_menus(void)
+{
+    playlistwin_accel = gtk_accel_group_new();
+
+    /* playlist window popup menu */
+    playlistwin_popup_menu = create_menu(playlistwin_popup_menu_entries,
+                                         G_N_ELEMENTS(playlistwin_popup_menu_entries),
+                                         playlistwin_accel);
+
+    pladd_menu  = create_menu(pladd_menu_entries, G_N_ELEMENTS(pladd_menu_entries),
+                              playlistwin_accel);
+    pldel_menu  = create_menu(pldel_menu_entries, G_N_ELEMENTS(pldel_menu_entries),
+                              playlistwin_accel);
+    plsel_menu  = create_menu(plsel_menu_entries, G_N_ELEMENTS(plsel_menu_entries),
+                              playlistwin_accel);
+    plsort_menu = create_menu(plsort_menu_entries,
+                              G_N_ELEMENTS(plsort_menu_entries),
+                              playlistwin_accel);
+    pllist_menu = create_menu(pllist_menu_entries, G_N_ELEMENTS(pllist_menu_entries),
+                              playlistwin_accel);
+
+#if 0
+    make_submenu(playlistwin_popup_menu, "/Playlist",
+                 playlistwin_playlist_menu);
+    make_submenu(playlistwin_popup_menu, "/Playback",
+                 playlistwin_playback_menu);
+    make_submenu(playlistwin_popup_menu, "/Add",
+                 pladd_menu);
+#endif
+}
+
+void
+playlistwin_create(void)
+{
+    playlistwin_create_window();
+    playlistwin_create_popup_menus();
+
+    /* create GC and back pixmap for custom widget to draw on */
+    playlistwin_gc = gdk_gc_new(playlistwin->window);
+    playlistwin_bg = gdk_pixmap_new(playlistwin->window,
+                                    playlistwin_get_width(),
+                                    playlistwin_get_height_unshaded(), -1);
+    gdk_window_set_back_pixmap(playlistwin->window, playlistwin_bg, 0);
+
+    playlistwin_create_widgets();
+    playlistwin_update_info();
+
+    gtk_window_add_accel_group(GTK_WINDOW(playlistwin), playlistwin_accel);
+    gtk_window_add_accel_group(GTK_WINDOW(playlistwin), mainwin_accel);
+}
+
+
+void
+playlistwin_show(void)
+{
+    GtkWidget *item;
+
+    item = gtk_item_factory_get_widget(mainwin_view_menu,
+                                       "/Show Playlist Editor");
+    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE);
+
+    tbutton_set_toggled(mainwin_pl, TRUE);
+    cfg.playlist_visible = TRUE;
+
+    playlistwin_set_toprow(0);
+    playlist_check_pos_current();
+
+    gtk_widget_show(playlistwin);
+}
+
+void
+playlistwin_hide(void)
+{
+    GtkWidget *item;
+
+    item = gtk_item_factory_get_widget(mainwin_view_menu,
+                                       "/Show Playlist Editor");
+    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE);
+
+    gtk_widget_hide(playlistwin);
+    tbutton_set_toggled(mainwin_pl, FALSE);
+    cfg.playlist_visible = FALSE;
+
+    gtk_window_present(GTK_WINDOW(mainwin));
+    gtk_widget_grab_focus(mainwin);
+}
+
+
+static void
+plsort_menu_callback(gpointer data,
+                     guint action,
+                     GtkWidget * widget)
+{
+    switch (action) {
+    case PLAYLISTWIN_SORT_BYPLAYLIST:
+        playlist_sort(PLAYLIST_SORT_PLAYLIST);
+        playlistwin_update_list();
+        break;
+    case PLAYLISTWIN_SORT_BYTRACK:
+        playlist_sort(PLAYLIST_SORT_TRACK);
+        playlistwin_update_list();
+        break;
+    case PLAYLISTWIN_SORT_BYTITLE:
+        playlist_sort(PLAYLIST_SORT_TITLE);
+        playlistwin_update_list();
+        break;
+    case PLAYLISTWIN_SORT_BYARTIST:
+        playlist_sort(PLAYLIST_SORT_ARTIST);
+        playlistwin_update_list();
+        break;
+    case PLAYLISTWIN_SORT_BYPATH:
+        playlist_sort(PLAYLIST_SORT_PATH);
+        playlistwin_update_list();
+        break;
+    case PLAYLISTWIN_SORT_BYDATE:
+        playlist_sort(PLAYLIST_SORT_DATE);
+        playlistwin_update_list();
+        break;
+    case PLAYLISTWIN_SORT_BYFILENAME:
+        playlist_sort(PLAYLIST_SORT_FILENAME);
+        playlistwin_update_list();
+        break;
+    case PLAYLISTWIN_SORT_SEL_BYPLAYLIST:
+        playlist_sort_selected(PLAYLIST_SORT_PLAYLIST);
+        playlistwin_update_list();
+        break;
+    case PLAYLISTWIN_SORT_SEL_BYTRACK:
+        playlist_sort_selected(PLAYLIST_SORT_TRACK);
+        playlistwin_update_list();
+        break;
+    case PLAYLISTWIN_SORT_SEL_BYTITLE:
+        playlist_sort_selected(PLAYLIST_SORT_TITLE);
+        playlistwin_update_list();
+        break;
+    case PLAYLISTWIN_SORT_SEL_BYARTIST:
+        playlist_sort_selected(PLAYLIST_SORT_ARTIST);
+        playlistwin_update_list();
+        break;
+    case PLAYLISTWIN_SORT_SEL_BYFILENAME:
+        playlist_sort_selected(PLAYLIST_SORT_FILENAME);
+        playlistwin_update_list();
+        break;
+    case PLAYLISTWIN_SORT_SEL_BYPATH:
+        playlist_sort_selected(PLAYLIST_SORT_PATH);
+        playlistwin_update_list();
+        break;
+    case PLAYLISTWIN_SORT_SEL_BYDATE:
+        playlist_sort_selected(PLAYLIST_SORT_DATE);
+        playlistwin_update_list();
+        break;
+    case PLAYLISTWIN_SORT_REVERSE:
+        playlist_reverse();
+        playlistwin_update_list();
+        break;
+    case PLAYLISTWIN_SORT_RANDOMIZE:
+        playlist_random();
+        playlistwin_update_list();
+        break;
+    }
+}
+
+static void
+playlistwin_sub_menu_callback(gpointer data,
+                              guint action,
+                              GtkWidget * widget)
+{
+    switch (action) {
+    case PLIST_NEW:
+        playlist_set_current_name(NULL);
+        playlist_clear();
+        mainwin_clear_song_info();
+        mainwin_set_info_text();
+        break;
+    case PLIST_SAVE:
+        playlistwin_select_playlist_to_save(playlist_get_current_name());
+        break;
+    case PLIST_DEFAULTSAVE:
+        playlist_save(bmp_paths[BMP_PATH_PLAYLIST_FILE]);
+        break;
+    case PLIST_SAVE_AS:
+        playlistwin_select_playlist_to_save(playlist_get_current_name());
+        break;
+    case PLIST_LOAD:
+        playlistwin_select_playlist_to_load(playlist_get_current_name());
+        break;
+    case SEL_INV:
+        playlistwin_inverse_selection();
+        break;
+    case SEL_ZERO:
+        playlistwin_select_none();
+        break;
+    case SEL_ALL:
+        playlistwin_select_all();
+        break;
+    case SUB_ALL:
+        playlist_clear();
+        mainwin_clear_song_info();
+        mainwin_set_info_text();
+        break;
+    case SUB_CROP:
+        playlist_delete(TRUE);
+        break;
+    case SUB_SELECTED:
+        playlist_delete(FALSE);
+        break;
+    case SUB_DUPLICATE_BYTITLE:
+        playlist_remove_duplicates(PLAYLIST_DUPS_TITLE);
+        break;
+    case SUB_DUPLICATE_BYFILENAME:
+        playlist_remove_duplicates(PLAYLIST_DUPS_FILENAME);
+        break;
+    case SUB_DUPLICATE_BYPATH:
+        playlist_remove_duplicates(PLAYLIST_DUPS_PATH);
+        break;
+    case PLAYLISTWIN_REMOVE_DEAD_FILES:
+        playlist_remove_dead_files();
+        break;
+    case PLAYLISTWIN_REFRESH:
+        playlist_read_info_selection();
+        playlistwin_update_list();
+        break;
+    }
+}
+
+static void
+playlistwin_popup_menu_callback(gpointer data,
+                                guint action,
+                                GtkWidget * widget)
+{
+    extern GtkWidget *filepopupbutton;
+
+    switch (action) {
+    case ADD_FILES:
+        playlistwin_show_filebrowser();
+        break;
+    case CLOSE_PL_WINDOW:
+        playlistwin_hide();
+        break;
+    case MISC_FILEINFO:
+        playlistwin_fileinfo();
+        break;
+    case SEL_LOOKUP:
+        playlist_read_info_selection();
+        break;
+    case MISC_QUEUE:
+        playlist_queue();
+        break;
+    case PLIST_CQUEUE:
+        playlist_clear_queue();
+        break;
+    case PLIST_JTT:
+        mainwin_jump_to_time();
+        break;
+    case PLIST_JTF:
+        mainwin_jump_to_file();
+        break;
+    case MISC_FILEPOPUP:
+        cfg.show_filepopup_for_tuple = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
+        if(filepopupbutton != NULL){
+            gtk_signal_emit_by_name(GTK_OBJECT(filepopupbutton), "realize");
+        }
+        break;
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/ui_playlist.h	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,78 @@
+/*  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; 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.
+ */
+
+#ifndef PLAYLISTWIN_H
+#define PLAYLISTWIN_H
+
+#include <glib.h>
+
+#include "mainwin.h"
+#include "widgets/widgetcore.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               cfg.playlist_width
+#define PLAYLISTWIN_HEIGHT \
+    (cfg.playlist_shaded ? PLAYLISTWIN_SHADED_HEIGHT : cfg.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);
+void playlistwin_update_list(void);
+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 draw_playlist_window(gboolean force);
+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_set_back_pixmap(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);
+
+extern GtkWidget *playlistwin;
+extern PlayList_List *playlistwin_list;
+
+extern PButton *playlistwin_shade, *playlistwin_close;
+
+extern gboolean playlistwin_focus;
+
+#endif
--- a/audacious/util.c	Thu Sep 07 22:00:35 2006 -0700
+++ b/audacious/util.c	Thu Sep 07 22:26:54 2006 -0700
@@ -53,6 +53,7 @@
 #include "main.h"
 #include "playback.h"
 #include "playlist.h"
+#include "ui_playlist.h"
 
 #ifdef USE_CHARDET
     #include "../libguess/libguess.h"
@@ -763,57 +764,6 @@
     return win;
 }
 
-/* *** TO WA2GUI *** */
-
-#if 0
-/* text_get_extents() taken from The GIMP (C) Spencer Kimball, Peter
- * Mattis et al */
-gboolean
-text_get_extents(const gchar * fontname,
-                 const gchar * text,
-                 gint * width, gint * height, gint * ascent, gint * descent)
-{
-    PangoFontDescription *font_desc;
-    PangoLayout *layout;
-    PangoRectangle rect;
-
-    g_return_val_if_fail(fontname != NULL, FALSE);
-    g_return_val_if_fail(text != NULL, FALSE);
-
-    /* FIXME: resolution */
-    layout = gtk_widget_create_pango_layout(GTK_WIDGET(mainwin), text);
-
-    font_desc = pango_font_description_from_string(fontname);
-    pango_layout_set_font_description(layout, font_desc);
-    pango_font_description_free(font_desc);
-    pango_layout_get_pixel_extents(layout, NULL, &rect);
-
-    if (width)
-        *width = rect.width;
-    if (height)
-        *height = rect.height;
-
-    if (ascent || descent) {
-        PangoLayoutIter *iter;
-        PangoLayoutLine *line;
-
-        iter = pango_layout_get_iter(layout);
-        line = pango_layout_iter_get_line(iter);
-        pango_layout_iter_free(iter);
-
-        pango_layout_line_get_pixel_extents(line, NULL, &rect);
-
-        if (ascent)
-            *ascent = PANGO_ASCENT(rect);
-        if (descent)
-            *descent = -PANGO_DESCENT(rect);
-    }
-
-    g_object_unref(layout);
-
-    return TRUE;
-}
-
 static void
 filebrowser_add_files(GtkFileChooser * browser,
                       GSList * files)
@@ -1095,56 +1045,6 @@
     gtk_window_present(GTK_WINDOW(dialog));
 }
 
-#endif
-
-/******************************************************************** keep in util.c */
-
-GtkWidget *
-make_filebrowser(const gchar * title,
-		 GtkWidget *parent,
-                 gboolean save)
-{
-    GtkWidget *dialog;
-    GtkWidget *button;
-    GtkWidget *button_close;
-
-    g_return_val_if_fail(title != NULL, NULL);
-
-    dialog = gtk_file_chooser_dialog_new(title, GTK_WINDOW(parent),
-                                         GTK_FILE_CHOOSER_ACTION_OPEN, NULL, NULL);
-    if (save)
-        gtk_file_chooser_set_action(GTK_FILE_CHOOSER(dialog),
-                                    GTK_FILE_CHOOSER_ACTION_SAVE);
-
-    if (!save)
-        gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
-
-    g_signal_connect(dialog, "destroy",
-                     G_CALLBACK(gtk_widget_destroyed), &dialog);
-
-#ifdef HAVE_GNOME_VFS
-    gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dialog), FALSE);
-#endif
-
-    button_close = gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL,
-                                         GTK_RESPONSE_REJECT);
-    gtk_button_set_use_stock(GTK_BUTTON(button_close), TRUE);
-    GTK_WIDGET_SET_FLAGS(button_close, GTK_CAN_DEFAULT);
-    g_signal_connect_swapped(button_close, "clicked",
-                             G_CALLBACK(gtk_widget_destroy), dialog);
-
-    button = gtk_dialog_add_button(GTK_DIALOG(dialog), save ?
-                                   GTK_STOCK_SAVE : GTK_STOCK_OPEN,
-                                   GTK_RESPONSE_ACCEPT);
-    gtk_button_set_use_stock(GTK_BUTTON(button), TRUE);
-    GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
-    gtk_window_set_default(GTK_WINDOW(dialog), button);
-
-    gtk_widget_show(dialog);
-
-    return dialog;
-}
-
 GdkFont *
 util_font_load(const gchar * name)
 {
@@ -1192,6 +1092,54 @@
     gdk_window_set_cursor(window->window, cursor);
 }
 
+/* text_get_extents() taken from The GIMP (C) Spencer Kimball, Peter
+ * Mattis et al */
+gboolean
+text_get_extents(const gchar * fontname,
+                 const gchar * text,
+                 gint * width, gint * height, gint * ascent, gint * descent)
+{
+    PangoFontDescription *font_desc;
+    PangoLayout *layout;
+    PangoRectangle rect;
+
+    g_return_val_if_fail(fontname != NULL, FALSE);
+    g_return_val_if_fail(text != NULL, FALSE);
+
+    /* FIXME: resolution */
+    layout = gtk_widget_create_pango_layout(GTK_WIDGET(mainwin), text);
+
+    font_desc = pango_font_description_from_string(fontname);
+    pango_layout_set_font_description(layout, font_desc);
+    pango_font_description_free(font_desc);
+    pango_layout_get_pixel_extents(layout, NULL, &rect);
+
+    if (width)
+        *width = rect.width;
+    if (height)
+        *height = rect.height;
+
+    if (ascent || descent) {
+        PangoLayoutIter *iter;
+        PangoLayoutLine *line;
+
+        iter = pango_layout_get_iter(layout);
+        line = pango_layout_iter_get_line(iter);
+        pango_layout_iter_free(iter);
+
+        pango_layout_line_get_pixel_extents(line, NULL, &rect);
+
+        if (ascent)
+            *ascent = PANGO_ASCENT(rect);
+        if (descent)
+            *descent = -PANGO_DESCENT(rect);
+    }
+
+    g_object_unref(layout);
+
+    return TRUE;
+}
+
 /* counts number of digits in a gint */
 guint
 gint_count_digits(gint n)
@@ -1410,6 +1358,52 @@
     return TRUE;
 }
 
+GtkWidget *
+make_filebrowser(const gchar * title,
+                 gboolean save)
+{
+    GtkWidget *dialog;
+    GtkWidget *button;
+    GtkWidget *button_close;
+
+    g_return_val_if_fail(title != NULL, NULL);
+
+    dialog = gtk_file_chooser_dialog_new(title, GTK_WINDOW(mainwin),
+                                         GTK_FILE_CHOOSER_ACTION_OPEN, NULL, NULL);
+    if (save)
+        gtk_file_chooser_set_action(GTK_FILE_CHOOSER(dialog),
+                                    GTK_FILE_CHOOSER_ACTION_SAVE);
+
+    if (!save)
+        gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
+
+    g_signal_connect(dialog, "destroy",
+                     G_CALLBACK(gtk_widget_destroyed), &dialog);
+
+#ifdef HAVE_GNOME_VFS
+    gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dialog), FALSE);
+#endif
+
+    button_close = gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL,
+                                         GTK_RESPONSE_REJECT);
+    gtk_button_set_use_stock(GTK_BUTTON(button_close), TRUE);
+    GTK_WIDGET_SET_FLAGS(button_close, GTK_CAN_DEFAULT);
+    g_signal_connect_swapped(button_close, "clicked",
+                             G_CALLBACK(gtk_widget_destroy), dialog);
+
+    button = gtk_dialog_add_button(GTK_DIALOG(dialog), save ?
+                                   GTK_STOCK_SAVE : GTK_STOCK_OPEN,
+                                   GTK_RESPONSE_ACCEPT);
+    gtk_button_set_use_stock(GTK_BUTTON(button), TRUE);
+    GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
+    gtk_window_set_default(GTK_WINDOW(dialog), button);
+
+    gtk_widget_show(dialog);
+
+    return dialog;
+}
+
+
 GtkItemFactory *
 create_menu(GtkItemFactoryEntry *entries,
             guint n_entries,
--- a/audacious/util.h	Thu Sep 07 22:00:35 2006 -0700
+++ b/audacious/util.h	Thu Sep 07 22:26:54 2006 -0700
@@ -114,7 +114,7 @@
                   const gchar *item_path,
                   GtkItemFactory *submenu);
 
-GtkWidget *make_filebrowser(const gchar * title, GtkWidget *parent,
+GtkWidget *make_filebrowser(const gchar * title,
                             gboolean save);
 
 /* Not sure this should be here? */
--- a/audacious/visualization.c	Thu Sep 07 22:00:35 2006 -0700
+++ b/audacious/visualization.c	Thu Sep 07 22:26:54 2006 -0700
@@ -32,7 +32,7 @@
 #include "playback.h"
 #include "plugin.h"
 #include "prefswin.h"
-#include "interface.h"
+#include "widgets/widgetcore.h"
 
 VisPluginData vp_data = {
     NULL,
@@ -280,15 +280,12 @@
     gint i;
 
     if (!pcm_data || nch < 1) {
-	current_interface->send_pcm_data(pcm_data, nch, length);
-#if 0
         if (cfg.vis_type != VIS_OFF) {
             if (cfg.player_shaded && cfg.player_visible)
                 svis_timeout_func(mainwin_svis, NULL);
             else
                 vis_timeout_func(active_vis, NULL);
         }
-#endif
         return;
     }
 
@@ -329,8 +326,6 @@
         node = g_list_next(node);
     }
 
-    current_interface->send_pcm_data(pcm_data, nch, length);
-#if 0
     if (cfg.vis_type == VIS_OFF)
         return;
 
@@ -439,5 +434,4 @@
         svis_timeout_func(mainwin_svis, intern_vis_data);
     else
         vis_timeout_func(active_vis, intern_vis_data);
-#endif
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/Makefile	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,38 @@
+include ../../mk/rules.mk
+include ../../mk/init.mk
+
+OBJECTIVE_LIBS_NOINST = libwidgets.a
+
+LDFLAGS += -Wl,-export-dynamic
+
+CFLAGS += \
+	$(GTK_CFLAGS)      \
+	$(LIBGLADE_CFLAGS) \
+	$(BEEP_DEFINES)    \
+	$(ARCH_DEFINES)    \
+	-I../..    \
+	-I..	   \
+	-I../../intl \
+
+SOURCES = \
+	widget.c \
+	sbutton.c \
+	pbutton.c \
+	tbutton.c \
+	textbox.c \
+	hslider.c \
+	menurow.c \
+	monostereo.c \
+	vis.c \
+	svis.c \
+	number.c \
+	playstatus.c \
+	playlist_list.c \
+	playlist_slider.c \
+	eq_graph.c \
+	eq_slider.c \
+	skin.c
+
+OBJECTS = ${SOURCES:.c=.o}
+
+include ../../mk/objective.mk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/eq_graph.c	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,149 @@
+/*  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; 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 "widgetcore.h"
+
+#include <glib.h>
+
+#include "main.h"
+#include "skin.h"
+
+void
+init_spline(gfloat * x, gfloat * y, gint n, gfloat * y2)
+{
+    gint i, k;
+    gfloat p, qn, sig, un, *u;
+
+    u = (gfloat *) g_malloc(n * sizeof(gfloat));
+
+    y2[0] = u[0] = 0.0;
+
+    for (i = 1; i < n - 1; i++) {
+        sig = ((gfloat) x[i] - x[i - 1]) / ((gfloat) x[i + 1] - x[i - 1]);
+        p = sig * y2[i - 1] + 2.0;
+        y2[i] = (sig - 1.0) / p;
+        u[i] =
+            (((gfloat) y[i + 1] - y[i]) / (x[i + 1] - x[i])) -
+            (((gfloat) y[i] - y[i - 1]) / (x[i] - x[i - 1]));
+        u[i] = (6.0 * u[i] / (x[i + 1] - x[i - 1]) - sig * u[i - 1]) / p;
+    }
+    qn = un = 0.0;
+
+    y2[n - 1] = (un - qn * u[n - 2]) / (qn * y2[n - 2] + 1.0);
+    for (k = n - 2; k >= 0; k--)
+        y2[k] = y2[k] * y2[k + 1] + u[k];
+    g_free(u);
+}
+
+gfloat
+eval_spline(gfloat xa[], gfloat ya[], gfloat y2a[], gint n, gfloat x)
+{
+    gint klo, khi, k;
+    gfloat h, b, a;
+
+    klo = 0;
+    khi = n - 1;
+    while (khi - klo > 1) {
+        k = (khi + klo) >> 1;
+        if (xa[k] > x)
+            khi = k;
+        else
+            klo = k;
+    }
+    h = xa[khi] - xa[klo];
+    a = (xa[khi] - x) / h;
+    b = (x - xa[klo]) / h;
+    return (a * ya[klo] + b * ya[khi] +
+            ((a * a * a - a) * y2a[klo] +
+             (b * b * b - b) * y2a[khi]) * (h * h) / 6.0);
+}
+
+void
+eqgraph_draw(Widget * w)
+{
+    EqGraph *eg = (EqGraph *) w;
+    GdkPixmap *obj;
+    GdkColor col;
+    guint32 cols[19];
+    gint i, y, ymin, ymax, py = 0;
+    gfloat x[] = { 0, 11, 23, 35, 47, 59, 71, 83, 97, 109 }, yf[10];
+
+    /*
+     * This avoids the init_spline() function to be inlined.
+     * Inlining the function caused troubles when compiling with
+     * `-O' (at least on FreeBSD).
+     */
+    void (*__init_spline) (gfloat *, gfloat *, gint, gfloat *) = init_spline;
+
+    obj = eg->eg_widget.parent;
+    skin_draw_pixmap(bmp_active_skin, obj, eg->eg_widget.gc, SKIN_EQMAIN,
+                     0, 294, eg->eg_widget.x, eg->eg_widget.y,
+                     eg->eg_widget.width, eg->eg_widget.height);
+    skin_draw_pixmap(bmp_active_skin, obj, eg->eg_widget.gc, SKIN_EQMAIN,
+                     0, 314, eg->eg_widget.x,
+                     eg->eg_widget.y + 9 +
+                     ((cfg.equalizer_preamp * 9) / 20),
+                     eg->eg_widget.width, 1);
+
+    skin_get_eq_spline_colors(bmp_active_skin, cols);
+
+    __init_spline(x, cfg.equalizer_bands, 10, yf);
+    for (i = 0; i < 109; i++) {
+        y = 9 -
+            (gint) ((eval_spline(x, cfg.equalizer_bands, yf, 10, i) *
+                     9.0) / 20.0);
+        if (y < 0)
+            y = 0;
+        if (y > 18)
+            y = 18;
+        if (!i)
+            py = y;
+        if (y < py) {
+            ymin = y;
+            ymax = py;
+        }
+        else {
+            ymin = py;
+            ymax = y;
+        }
+        py = y;
+        for (y = ymin; y <= ymax; y++) {
+            col.pixel = cols[y];
+            gdk_gc_set_foreground(eg->eg_widget.gc, &col);
+            gdk_draw_point(obj, eg->eg_widget.gc, eg->eg_widget.x + i + 2,
+                           eg->eg_widget.y + y);
+        }
+    }
+}
+
+EqGraph *
+create_eqgraph(GList ** wlist, GdkPixmap * parent, GdkGC * gc, gint x, gint y)
+{
+    EqGraph *eg;
+
+    eg = g_new0(EqGraph, 1);
+    widget_init(&eg->eg_widget, parent, gc, x, y, 113, 19, TRUE);
+    eg->eg_widget.draw = eqgraph_draw;
+
+    widget_list_add(wlist, WIDGET(eg));
+
+    return eg;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/eq_graph.h	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,44 @@
+/*  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; 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.
+ */
+
+#ifndef _WIDGETCORE_H_
+#error Please do not include me directly! Use widgetcore.h instead!
+#endif
+
+#ifndef EQ_GRAPH_H
+#define EQ_GRAPH_H
+
+#include <glib.h>
+#include <gdk/gdk.h>
+
+#include "widget.h"
+
+#define EQ_GRAPH(x)  ((EqGraph *)(x))
+struct _EqGraph {
+    Widget eg_widget;
+};
+
+typedef struct _EqGraph EqGraph;
+
+EqGraph *create_eqgraph(GList ** wlist, GdkPixmap * parent, GdkGC * gc,
+                        gint x, gint y);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/eq_slider.c	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,235 @@
+/*  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; 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 "widgetcore.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+#include "equalizer.h"
+#include "mainwin.h"
+#include "skin.h"
+
+void
+eqslider_set_position(EqSlider * es,
+                      gfloat pos)
+{
+    es->es_position = 25 - (gint) ((pos * 25.0) / 20.0);
+
+    if (es->es_position < 0)
+        es->es_position = 0;
+
+    if (es->es_position > 50)
+        es->es_position = 50;
+
+    if (es->es_position >= 24 && es->es_position <= 26)
+        es->es_position = 25;
+
+    widget_draw(WIDGET(es));
+}
+
+gfloat
+eqslider_get_position(EqSlider * es)
+{
+    return 20.0 - (((gfloat) es->es_position * 20.0) / 25.0);
+}
+
+void
+eqslider_draw(Widget * w)
+{
+    EqSlider *es = (EqSlider *) w;
+    GdkPixmap *obj;
+    SkinPixmapId src;
+    gint frame;
+
+    src = SKIN_EQMAIN;
+    obj = es->es_widget.parent;
+
+    frame = 27 - ((es->es_position * 27) / 50);
+    if (frame < 14)
+        skin_draw_pixmap(bmp_active_skin, obj, es->es_widget.gc, src,
+                         (frame * 15) + 13, 164, es->es_widget.x,
+                         es->es_widget.y, es->es_widget.width,
+                         es->es_widget.height);
+    else
+        skin_draw_pixmap(bmp_active_skin, obj, es->es_widget.gc, src,
+                         ((frame - 14) * 15) + 13, 229, es->es_widget.x,
+                         es->es_widget.y, es->es_widget.width,
+                         es->es_widget.height);
+    if (es->es_isdragging)
+        skin_draw_pixmap(bmp_active_skin, obj, es->es_widget.gc, src, 0,
+                         176, es->es_widget.x + 1,
+                         es->es_widget.y + es->es_position, 11, 11);
+    else
+        skin_draw_pixmap(bmp_active_skin, obj, es->es_widget.gc, src, 0,
+                         164, es->es_widget.x + 1,
+                         es->es_widget.y + es->es_position, 11, 11);
+}
+
+void
+eqslider_set_mainwin_text(EqSlider * es)
+{
+    gint band = 0;
+    const gchar *bandname[11] = { N_("PREAMP"), N_("60HZ"), N_("170HZ"),
+        N_("310HZ"), N_("600HZ"), N_("1KHZ"),
+        N_("3KHZ"), N_("6KHZ"), N_("12KHZ"),
+        N_("14KHZ"), N_("16KHZ")
+    };
+    gchar *tmp;
+
+    if (es->es_widget.x > 21)
+        band = ((es->es_widget.x - 78) / 18) + 1;
+
+    tmp =
+        g_strdup_printf("EQ: %s: %+.1f DB", _(bandname[band]),
+                        eqslider_get_position(es));
+    mainwin_lock_info_text(tmp);
+    g_free(tmp);
+}
+
+void
+eqslider_button_press_cb(GtkWidget * w,
+                         GdkEventButton * event,
+                         gpointer data)
+{
+    EqSlider *es = EQ_SLIDER(data);
+    gint y;
+
+    if (widget_contains(&es->es_widget, event->x, event->y)) {
+        if (event->button == 1) {
+            y = event->y - es->es_widget.y;
+            es->es_isdragging = TRUE;
+            if (y >= es->es_position && y < es->es_position + 11)
+                es->es_drag_y = y - es->es_position;
+            else {
+                es->es_position = y - 5;
+                es->es_drag_y = 5;
+                if (es->es_position < 0)
+                    es->es_position = 0;
+                if (es->es_position > 50)
+                    es->es_position = 50;
+                if (es->es_position >= 24 && es->es_position <= 26)
+                    es->es_position = 25;
+                equalizerwin_eq_changed();
+            }
+
+            eqslider_set_mainwin_text(es);
+            widget_draw(WIDGET(es));
+        }
+        if (event->button == 4) {
+            es->es_position -= 2;
+            if (es->es_position < 0)
+                es->es_position = 0;
+            equalizerwin_eq_changed();
+            widget_draw(WIDGET(es));
+        }
+    }
+}
+
+void
+eqslider_mouse_scroll_cb(GtkWidget * w,
+                         GdkEventScroll * event,
+                         gpointer data)
+{
+    EqSlider *es = EQ_SLIDER(data);
+
+    if (!widget_contains(&es->es_widget, event->x, event->y))
+        return;
+
+    if (event->direction == GDK_SCROLL_UP) {
+        es->es_position -= 2;
+
+        if (es->es_position < 0)
+            es->es_position = 0;
+
+        equalizerwin_eq_changed();
+        widget_draw(WIDGET(es));
+    }
+    else {
+        es->es_position += 2;
+
+        if (es->es_position > 50)
+            es->es_position = 50;
+
+        equalizerwin_eq_changed();
+        widget_draw(WIDGET(es));
+    }
+}
+
+void
+eqslider_motion_cb(GtkWidget * w,
+                   GdkEventMotion * event,
+                   gpointer data)
+{
+    EqSlider *es = EQ_SLIDER(data);
+    gint y;
+
+    y = event->y - es->es_widget.y;
+    if (es->es_isdragging) {
+        es->es_position = y - es->es_drag_y;
+        if (es->es_position < 0)
+            es->es_position = 0;
+        if (es->es_position > 50)
+            es->es_position = 50;
+        if (es->es_position >= 24 && es->es_position <= 26)
+            es->es_position = 25;
+        equalizerwin_eq_changed();
+        eqslider_set_mainwin_text(es);
+        widget_draw(WIDGET(es));
+    }
+}
+
+void
+eqslider_button_release_cb(GtkWidget * w,
+                           GdkEventButton * event,
+                           gpointer data)
+{
+    EqSlider *es = EQ_SLIDER(data);
+
+    if (es->es_isdragging) {
+        es->es_isdragging = FALSE;
+        mainwin_release_info_text();
+        widget_draw(WIDGET(es));
+    }
+}
+
+EqSlider *
+create_eqslider(GList ** wlist,
+                GdkPixmap * parent,
+                GdkGC * gc,
+                gint x, gint y)
+{
+    EqSlider *es;
+
+    es = g_new0(EqSlider, 1);
+    widget_init(&es->es_widget, parent, gc, x, y, 14, 63, TRUE);
+    es->es_widget.button_press_cb = eqslider_button_press_cb;
+    es->es_widget.button_release_cb = eqslider_button_release_cb;
+    es->es_widget.motion_cb = eqslider_motion_cb;
+    es->es_widget.draw = eqslider_draw;
+    es->es_widget.mouse_scroll_cb = eqslider_mouse_scroll_cb;
+
+    widget_list_add(wlist, WIDGET(es));
+
+    return es;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/eq_slider.h	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,49 @@
+/*  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; 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.
+ */
+
+#ifndef _WIDGETCORE_H_
+#error Please do not include me directly! Use widgetcore.h instead!
+#endif
+
+#ifndef EQ_SLIDER_H
+#define EQ_SLIDER_H
+
+#include <glib.h>
+#include <gdk/gdk.h>
+
+#include "widget.h"
+
+#define EQ_SLIDER(x)  ((EqSlider *)(x))
+struct _EqSlider {
+    Widget es_widget;
+    gint es_position;
+    gboolean es_isdragging;
+    gint es_drag_y;
+};
+
+typedef struct _EqSlider EqSlider;
+
+EqSlider *create_eqslider(GList ** wlist, GdkPixmap * parent, GdkGC * gc,
+                          gint x, gint y);
+void eqslider_set_position(EqSlider * es, gfloat pos);
+gfloat eqslider_get_position(EqSlider * es);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/hslider.c	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,203 @@
+/*  XMMS - Cross-platform multimedia player
+ *  Copyright (C) 1998-2000  Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "widgetcore.h"
+
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+#include "skin.h"
+
+void
+hslider_set_position(HSlider * hs,
+                     gint pos)
+{
+    if (pos == hs->hs_position || hs->hs_pressed)
+        return;
+
+    hs->hs_position = pos;
+
+    if (hs->hs_frame_cb)
+        hs->hs_frame = hs->hs_frame_cb(hs->hs_position);
+
+    widget_draw(WIDGET(hs));
+}
+
+gint
+hslider_get_position(HSlider * hs)
+{
+    return hs->hs_position;
+}
+
+void
+hslider_draw(Widget * w)
+{
+    HSlider *hs = (HSlider *) w;
+    GdkPixmap *obj;
+
+    obj = hs->hs_widget.parent;
+
+    skin_draw_pixmap(bmp_active_skin, obj, hs->hs_widget.gc,
+                     hs->hs_skin_index, hs->hs_frame_offset,
+                     hs->hs_frame * hs->hs_frame_height, hs->hs_widget.x,
+                     hs->hs_widget.y, hs->hs_widget.width,
+                     hs->hs_widget.height);
+    if (hs->hs_pressed)
+        skin_draw_pixmap(bmp_active_skin, obj, hs->hs_widget.gc,
+                         hs->hs_skin_index, hs->hs_knob_px,
+                         hs->hs_knob_py, hs->hs_widget.x + hs->hs_position,
+                         hs->hs_widget.y +
+                         ((hs->hs_widget.height - hs->hs_knob_height) / 2),
+                         hs->hs_knob_width, hs->hs_knob_height);
+    else
+        skin_draw_pixmap(bmp_active_skin, obj, hs->hs_widget.gc,
+                         hs->hs_skin_index, hs->hs_knob_nx, hs->hs_knob_ny,
+                         hs->hs_widget.x + hs->hs_position,
+                         hs->hs_widget.y +
+                         ((hs->hs_widget.height - hs->hs_knob_height) / 2),
+                         hs->hs_knob_width, hs->hs_knob_height);
+}
+
+void
+hslider_button_press_cb(GtkWidget * w,
+                        GdkEventButton * event,
+                        gpointer data)
+{
+    HSlider *hs = HSLIDER(data);
+    gint x;
+
+    if (event->button != 1)
+        return;
+
+    if (widget_contains(&hs->hs_widget, event->x, event->y)) {
+        x = event->x - hs->hs_widget.x;
+        hs->hs_pressed = TRUE;
+
+        if (x >= hs->hs_position && x < hs->hs_position + hs->hs_knob_width)
+            hs->hs_pressed_x = x - hs->hs_position;
+        else {
+            hs->hs_position = x - (hs->hs_knob_width / 2);
+            hs->hs_pressed_x = hs->hs_knob_width / 2;
+            if (hs->hs_position < hs->hs_min)
+                hs->hs_position = hs->hs_min;
+            if (hs->hs_position > hs->hs_max)
+                hs->hs_position = hs->hs_max;
+            if (hs->hs_frame_cb)
+                hs->hs_frame = hs->hs_frame_cb(hs->hs_position);
+
+        }
+
+        if (hs->hs_motion_cb)
+            hs->hs_motion_cb(hs->hs_position);
+
+        widget_draw(WIDGET(hs));
+    }
+}
+
+void
+hslider_motion_cb(GtkWidget * w, GdkEventMotion * event, gpointer data)
+{
+    HSlider *hs = (HSlider *) data;
+    gint x;
+
+    if (hs->hs_pressed) {
+        if (!hs->hs_widget.visible) {
+            hs->hs_pressed = FALSE;
+            return;
+        }
+
+        x = event->x - hs->hs_widget.x;
+        hs->hs_position = x - hs->hs_pressed_x;
+
+        if (hs->hs_position < hs->hs_min)
+            hs->hs_position = hs->hs_min;
+
+        if (hs->hs_position > hs->hs_max)
+            hs->hs_position = hs->hs_max;
+
+        if (hs->hs_frame_cb)
+            hs->hs_frame = hs->hs_frame_cb(hs->hs_position);
+
+        if (hs->hs_motion_cb)
+            hs->hs_motion_cb(hs->hs_position);
+
+        widget_draw(WIDGET(hs));
+    }
+}
+
+void
+hslider_button_release_cb(GtkWidget * w,
+                          GdkEventButton * event,
+                          gpointer data)
+{
+    HSlider *hs = HSLIDER(data);
+
+    if (hs->hs_pressed) {
+        hs->hs_pressed = FALSE;
+
+        if (hs->hs_release_cb)
+            hs->hs_release_cb(hs->hs_position);
+
+        widget_draw(WIDGET(hs));
+    }
+}
+
+HSlider *
+create_hslider(GList ** wlist, GdkPixmap * parent, GdkGC * gc,
+               gint x, gint y, gint w, gint h, gint knx, gint kny,
+               gint kpx, gint kpy, gint kw, gint kh, gint fh,
+               gint fo, gint min, gint max, gint(*fcb) (gint),
+               void (*mcb) (gint), void (*rcb) (gint), SkinPixmapId si)
+{
+    HSlider *hs;
+
+    hs = g_new0(HSlider, 1);
+    widget_init(&hs->hs_widget, parent, gc, x, y, w, h, 1);
+    hs->hs_widget.button_press_cb =
+        (void (*)(GtkWidget *, GdkEventButton *, gpointer))
+        hslider_button_press_cb;
+    hs->hs_widget.button_release_cb =
+        (void (*)(GtkWidget *, GdkEventButton *, gpointer))
+        hslider_button_release_cb;
+    hs->hs_widget.motion_cb =
+        (void (*)(GtkWidget *, GdkEventMotion *, gpointer))
+        hslider_motion_cb;
+    hs->hs_widget.draw = hslider_draw;
+    hs->hs_knob_nx = knx;
+    hs->hs_knob_ny = kny;
+    hs->hs_knob_px = kpx;
+    hs->hs_knob_py = kpy;
+    hs->hs_knob_width = kw;
+    hs->hs_knob_height = kh;
+    hs->hs_frame_height = fh;
+    hs->hs_frame_offset = fo;
+    hs->hs_min = min;
+    hs->hs_position = min;
+    hs->hs_max = max;
+    hs->hs_frame_cb = fcb;
+    hs->hs_motion_cb = mcb;
+    hs->hs_release_cb = rcb;
+    if (hs->hs_frame_cb)
+        hs->hs_frame = hs->hs_frame_cb(0);
+    hs->hs_skin_index = si;
+
+    widget_list_add(wlist, WIDGET(hs));
+
+    return hs;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/hslider.h	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,59 @@
+/*  XMMS - Cross-platform multimedia player
+ *  Copyright (C) 1998-2000  Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _WIDGETCORE_H_
+#error Please do not include me directly! Use widgetcore.h instead!
+#endif
+
+#ifndef HSLIDER_H
+#define HSLIDER_H
+
+#include <glib.h>
+#include <gdk/gdk.h>
+
+#include "skin.h"
+#include "widget.h"
+
+#define HSLIDER(x)  ((HSlider *)(x))
+struct _HSlider {
+    Widget hs_widget;
+    gint hs_frame, hs_frame_offset, hs_frame_height, hs_min, hs_max;
+    gint hs_knob_nx, hs_knob_ny, hs_knob_px, hs_knob_py;
+    gint hs_knob_width, hs_knob_height;
+    gint hs_position;
+    gboolean hs_pressed;
+    gint hs_pressed_x, hs_pressed_y;
+     gint(*hs_frame_cb) (gint);
+    void (*hs_motion_cb) (gint);
+    void (*hs_release_cb) (gint);
+    SkinPixmapId hs_skin_index;
+};
+
+typedef struct _HSlider HSlider;
+
+HSlider *create_hslider(GList ** wlist, GdkPixmap * parent, GdkGC * gc,
+                        gint x, gint y, gint w, gint h, gint knx, gint kny,
+                        gint kpx, gint kpy, gint kw, gint kh, gint fh,
+                        gint fo, gint min, gint max, gint(*fcb) (gint),
+                        void (*mcb) (gint), void (*rcb) (gint),
+                        SkinPixmapId si);
+
+void hslider_set_position(HSlider * hs, gint pos);
+gint hslider_get_position(HSlider * hs);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/menurow.c	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,186 @@
+/*  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; 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 "widgetcore.h"
+
+#include <glib.h>
+#include <gdk/gdk.h>
+
+#include "main.h"
+#include "menurow.h"
+#include "widget.h"
+
+void
+menurow_draw(Widget * widget)
+{
+    MenuRow *mr = MENU_ROW(widget);
+
+    GdkPixmap *obj = mr->mr_widget.parent;
+
+    if (mr->mr_selected == MENUROW_NONE) {
+        if (cfg.always_show_cb || mr->mr_bpushed)
+            skin_draw_pixmap(bmp_active_skin, obj,
+                             mr->mr_widget.gc,
+                             mr->mr_skin_index,
+                             mr->mr_nx, mr->mr_ny,
+                             mr->mr_widget.x, mr->mr_widget.y, 8, 43);
+        else
+            skin_draw_pixmap(bmp_active_skin, obj,
+                             mr->mr_widget.gc,
+                             mr->mr_skin_index,
+                             mr->mr_nx + 8, mr->mr_ny,
+                             mr->mr_widget.x, mr->mr_widget.y, 8, 43);
+    }
+    else {
+        skin_draw_pixmap(bmp_active_skin, obj,
+                         mr->mr_widget.gc,
+                         mr->mr_skin_index,
+                         mr->mr_sx + ((mr->mr_selected - 1) * 8),
+                         mr->mr_sy, mr->mr_widget.x, mr->mr_widget.y, 8, 43);
+    }
+    if (cfg.always_show_cb || mr->mr_bpushed) {
+        if (mr->mr_always_selected)
+            skin_draw_pixmap(bmp_active_skin, obj,
+                             mr->mr_widget.gc,
+                             mr->mr_skin_index,
+                             mr->mr_sx + 8, mr->mr_sy + 10,
+                             mr->mr_widget.x, mr->mr_widget.y + 10, 8, 8);
+        if (mr->mr_doublesize_selected)
+            skin_draw_pixmap(bmp_active_skin, obj,
+                             mr->mr_widget.gc,
+                             mr->mr_skin_index,
+                             mr->mr_sx + 24, mr->mr_sy + 26,
+                             mr->mr_widget.x, mr->mr_widget.y + 26, 8, 8);
+    }
+
+}
+
+MenuRowItem
+menurow_find_selected(MenuRow * mr, gint x, gint y)
+{
+    MenuRowItem ret = MENUROW_NONE;
+
+    x -= mr->mr_widget.x;
+    y -= mr->mr_widget.y;
+    if (x > 0 && x < 8) {
+        if (y >= 0 && y <= 10)
+            ret = MENUROW_OPTIONS;
+        if (y >= 10 && y <= 17)
+            ret = MENUROW_ALWAYS;
+        if (y >= 18 && y <= 25)
+            ret = MENUROW_FILEINFOBOX;
+        if (y >= 26 && y <= 33)
+            ret = MENUROW_DOUBLESIZE;
+        if (y >= 34 && y <= 42)
+            ret = MENUROW_VISUALIZATION;
+    }
+    return ret;
+}
+
+void
+menurow_button_press(GtkWidget * widget,
+                     GdkEventButton * event,
+                     gpointer data)
+{
+    MenuRow *mr = MENU_ROW(data);
+
+    if (event->button != 1)
+        return;
+
+    if (widget_contains(&mr->mr_widget, event->x, event->y)) {
+        mr->mr_bpushed = TRUE;
+        mr->mr_selected = menurow_find_selected(mr, event->x, event->y);
+
+        widget_draw(WIDGET(mr));
+
+        if (mr->mr_change_callback)
+            mr->mr_change_callback(mr->mr_selected);
+    }
+}
+
+void
+menurow_motion(GtkWidget * widget,
+               GdkEventMotion * event,
+               gpointer data)
+{
+    MenuRow *mr = MENU_ROW(data);
+
+    if (mr->mr_bpushed) {
+        mr->mr_selected = menurow_find_selected(mr, event->x, event->y);
+
+        widget_draw(WIDGET(mr));
+
+        if (mr->mr_change_callback)
+            mr->mr_change_callback(mr->mr_selected);
+    }
+}
+
+void
+menurow_button_release(GtkWidget * widget,
+                       GdkEventButton * event,
+                       gpointer data)
+{
+    MenuRow *mr = MENU_ROW(data);
+
+    if (mr->mr_bpushed) {
+        mr->mr_bpushed = FALSE;
+
+        if (mr->mr_selected == MENUROW_ALWAYS)
+            mr->mr_always_selected = !mr->mr_always_selected;
+
+        if (mr->mr_selected == MENUROW_DOUBLESIZE)
+            mr->mr_doublesize_selected = !mr->mr_doublesize_selected;
+
+        if ((int)(mr->mr_selected) != -1 && mr->mr_release_callback)
+            mr->mr_release_callback(mr->mr_selected);
+
+        mr->mr_selected = MENUROW_NONE;
+
+        widget_draw(WIDGET(mr));
+    }
+}
+
+MenuRow *
+create_menurow(GList ** wlist, GdkPixmap * parent, GdkGC * gc,
+               gint x, gint y, gint nx, gint ny, gint sx, gint sy,
+               void (*ccb) (MenuRowItem),
+               void (*rcb) (MenuRowItem), SkinPixmapId si)
+{
+    MenuRow *mr;
+
+    mr = g_new0(MenuRow, 1);
+    widget_init(&mr->mr_widget, parent, gc, x, y, 8, 43, 1);
+    mr->mr_widget.draw = menurow_draw;
+    mr->mr_widget.button_press_cb = menurow_button_press;
+    mr->mr_widget.motion_cb = menurow_motion;
+    mr->mr_widget.button_release_cb = menurow_button_release;
+    mr->mr_nx = nx;
+    mr->mr_ny = ny;
+    mr->mr_sx = sx;
+    mr->mr_sy = sy;
+    mr->mr_selected = MENUROW_NONE;
+    mr->mr_change_callback = ccb;
+    mr->mr_release_callback = rcb;
+    mr->mr_skin_index = si;
+
+    widget_list_add(wlist, WIDGET(mr));
+    return mr;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/menurow.h	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,61 @@
+/*  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; 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.
+ */
+
+#ifndef _WIDGETCORE_H_
+#error Please do not include me directly! Use widgetcore.h instead!
+#endif
+
+#ifndef MENUROW_H
+#define	MENUROW_H
+
+#include <glib.h>
+#include <gdk/gdk.h>
+
+#include "skin.h"
+#include "widget.h"
+
+typedef enum {
+    MENUROW_NONE, MENUROW_OPTIONS, MENUROW_ALWAYS, MENUROW_FILEINFOBOX,
+    MENUROW_DOUBLESIZE, MENUROW_VISUALIZATION
+} MenuRowItem;
+
+#define MENU_ROW(x)  ((MenuRow *)(x))
+struct _MenuRow {
+    Widget mr_widget;
+    gint mr_nx, mr_ny;
+    gint mr_sx, mr_sy;
+    MenuRowItem mr_selected;
+    gboolean mr_bpushed;
+    gboolean mr_always_selected;
+    gboolean mr_doublesize_selected;
+    void (*mr_change_callback) (MenuRowItem);
+    void (*mr_release_callback) (MenuRowItem);
+    SkinPixmapId mr_skin_index;
+};
+
+typedef struct _MenuRow MenuRow;
+
+MenuRow *create_menurow(GList ** wlist, GdkPixmap * parent, GdkGC * gc,
+                        gint x, gint y, gint nx, gint ny, gint sx, gint sy,
+                        void (*ccb) (MenuRowItem),
+                        void (*rcb) (MenuRowItem), SkinPixmapId si);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/monostereo.c	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,93 @@
+/*  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; 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 "widgetcore.h"
+
+#include <glib.h>
+#include <gdk/gdk.h>
+
+#include "skin.h"
+#include "widget.h"
+
+void
+monostereo_draw(Widget * widget)
+{
+    MonoStereo *ms = (MonoStereo *) widget;
+    GdkPixmap *obj;
+
+    obj = ms->ms_widget.parent;
+
+    switch (ms->ms_num_channels) {
+    case 0:
+        skin_draw_pixmap(bmp_active_skin, obj, ms->ms_widget.gc,
+                         ms->ms_skin_index, 29, 12,
+                         ms->ms_widget.x, ms->ms_widget.y, 27, 12);
+        skin_draw_pixmap(bmp_active_skin, obj, ms->ms_widget.gc,
+                         ms->ms_skin_index, 0, 12,
+                         ms->ms_widget.x + 27, ms->ms_widget.y, 29, 12);
+        break;
+    case 1:
+        skin_draw_pixmap(bmp_active_skin, obj, ms->ms_widget.gc,
+                         ms->ms_skin_index, 29, 0,
+                         ms->ms_widget.x, ms->ms_widget.y, 27, 12);
+        skin_draw_pixmap(bmp_active_skin, obj, ms->ms_widget.gc,
+                         ms->ms_skin_index, 0, 12,
+                         ms->ms_widget.x + 27, ms->ms_widget.y, 29, 12);
+        break;
+    case 2:
+        skin_draw_pixmap(bmp_active_skin, obj, ms->ms_widget.gc,
+                         ms->ms_skin_index, 29, 12,
+                         ms->ms_widget.x, ms->ms_widget.y, 27, 12);
+        skin_draw_pixmap(bmp_active_skin, obj, ms->ms_widget.gc,
+                         ms->ms_skin_index, 0, 0,
+                         ms->ms_widget.x + 27, ms->ms_widget.y, 29, 12);
+        break;
+    }
+}
+
+void
+monostereo_set_num_channels(MonoStereo * ms,
+                            gint nch)
+{
+    if (!ms)
+        return;
+
+    ms->ms_num_channels = nch;
+    widget_draw(WIDGET(ms));
+}
+
+MonoStereo *
+create_monostereo(GList ** wlist,
+                  GdkPixmap * parent,
+                  GdkGC * gc,
+                  gint x, gint y, 
+                  SkinPixmapId si)
+{
+    MonoStereo *ms;
+
+    ms = g_new0(MonoStereo, 1);
+    widget_init(&ms->ms_widget, parent, gc, x, y, 56, 12, 1);
+    ms->ms_widget.draw = monostereo_draw;
+    ms->ms_skin_index = si;
+
+    widget_list_add(wlist, WIDGET(ms));
+    return ms;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/monostereo.h	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,48 @@
+/*  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; 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.
+ */
+
+#ifndef _WIDGETCORE_H_
+#error Please do not include me directly! Use widgetcore.h instead!
+#endif
+
+#ifndef MONOSTEREO_H
+#define MONOSTEREO_H
+
+#include <glib.h>
+#include <gdk/gdk.h>
+
+#include "skin.h"
+#include "widget.h"
+
+#define MONO_STEREO(x)  ((MonoStereo *)(x))
+struct _MonoStereo {
+    Widget ms_widget;
+    gint ms_num_channels;
+    SkinPixmapId ms_skin_index;
+};
+
+typedef struct _MonoStereo MonoStereo;
+
+MonoStereo *create_monostereo(GList ** wlist, GdkPixmap * parent,
+                              GdkGC * gc, gint x, gint y, SkinPixmapId si);
+void monostereo_set_num_channels(MonoStereo * ms, gint nch);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/number.c	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,75 @@
+/*  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; 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 "widgetcore.h"
+
+#include <glib.h>
+#include <gdk/gdk.h>
+
+#include "skin.h"
+
+void
+number_set_number(Number * nu,
+                  gint number)
+{
+    if (number == nu->nu_number)
+        return;
+
+    nu->nu_number = number;
+    widget_draw(WIDGET(nu));
+}
+
+void
+number_draw(Widget * w)
+{
+    Number *nu = NUMBER(w);
+    GdkPixmap *obj;
+
+    obj = nu->nu_widget.parent;
+
+    if (nu->nu_number <= 11)
+        skin_draw_pixmap(bmp_active_skin, obj, nu->nu_widget.gc,
+                         nu->nu_skin_index, nu->nu_number * 9, 0,
+                         nu->nu_widget.x, nu->nu_widget.y, 9, 13);
+    else
+        skin_draw_pixmap(bmp_active_skin, obj, nu->nu_widget.gc,
+                         nu->nu_skin_index, 90, 0, nu->nu_widget.x,
+                         nu->nu_widget.y, 9, 13);
+}
+
+Number *
+create_number(GList ** wlist,
+              GdkPixmap * parent,
+              GdkGC * gc,
+              gint x, gint y,
+              SkinPixmapId si)
+{
+    Number *nu;
+
+    nu = g_new0(Number, 1);
+    widget_init(&nu->nu_widget, parent, gc, x, y, 9, 13, 1);
+    nu->nu_widget.draw = number_draw;
+    nu->nu_number = 10;
+    nu->nu_skin_index = si;
+
+    widget_list_add(wlist, WIDGET(nu));
+    return nu;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/number.h	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,48 @@
+/*  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; 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.
+ */
+
+#ifndef _WIDGETCORE_H_
+#error Please do not include me directly! Use widgetcore.h instead!
+#endif
+
+#ifndef NUMBER_H
+#define NUMBER_H
+
+#include <glib.h>
+#include <gdk/gdk.h>
+
+#include "widget.h"
+#include "skin.h"
+
+#define NUMBER(x) ((Number *)(x))
+struct _Number {
+    Widget nu_widget;
+    gint nu_number;
+    SkinPixmapId nu_skin_index;
+};
+
+typedef struct _Number Number;
+
+void number_set_number(Number * nu, gint number);
+Number *create_number(GList ** wlist, GdkPixmap * parent, GdkGC * gc,
+                      gint x, gint y, SkinPixmapId si);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/pbutton.c	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,194 @@
+/*  BMP - Cross-platform multimedia player
+ *  Copyright (C) 2003-2004  BMP development team.
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; 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 "widgetcore.h"
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+#include "skin.h"
+#include "widget.h"
+
+void
+pbutton_draw(PButton * button)
+{
+    GdkPixmap *obj;
+
+    if (button->pb_allow_draw) {
+        obj = button->pb_widget.parent;
+
+        if (button->pb_pressed && button->pb_inside) {
+            skin_draw_pixmap(bmp_active_skin, obj,
+                             button->pb_widget.gc,
+                             button->pb_skin_index2, button->pb_px,
+                             button->pb_py, button->pb_widget.x,
+                             button->pb_widget.y,
+                             button->pb_widget.width,
+                             button->pb_widget.height);
+        }
+        else {
+            skin_draw_pixmap(bmp_active_skin, obj,
+                             button->pb_widget.gc,
+                             button->pb_skin_index1,
+                             button->pb_nx, button->pb_ny,
+                             button->pb_widget.x, button->pb_widget.y,
+                             button->pb_widget.width,
+                             button->pb_widget.height);
+        }
+    }
+}
+
+void
+pbutton_button_press_cb(GtkWidget * widget,
+                        GdkEventButton * event,
+                        PButton * button)
+{
+    if (event->button != 1)
+        return;
+
+    if (widget_contains(&button->pb_widget, event->x, event->y)) {
+        button->pb_pressed = 1;
+        button->pb_inside = 1;
+        widget_draw(WIDGET(button));
+        if (button->pb_push_cb)
+            button->pb_push_cb();
+    }
+}
+
+void
+pbutton_button_release_cb(GtkWidget * widget,
+                          GdkEventButton * event,
+                          PButton * button)
+{
+    if (event->button != 1)
+        return;
+    if (button->pb_inside && button->pb_pressed) {
+        button->pb_inside = 0;
+        widget_draw(WIDGET(button));
+	if (button->pb_release_cb)
+	    button->pb_release_cb();
+    }
+    if (button->pb_pressed)
+        button->pb_pressed = 0;
+}
+
+void
+pbutton_motion_cb(GtkWidget * widget, GdkEventMotion * event,
+                  PButton * button)
+{
+    gint inside;
+
+    if (!button->pb_pressed)
+        return;
+
+    inside = widget_contains(&button->pb_widget, event->x, event->y);
+
+    if (inside != button->pb_inside) {
+        button->pb_inside = inside;
+        widget_draw(WIDGET(button));
+    }
+}
+
+void
+pbutton_set_skin_index(PButton * b, SkinPixmapId si)
+{
+    b->pb_skin_index1 = b->pb_skin_index2 = si;
+}
+
+void
+pbutton_set_skin_index1(PButton * b, SkinPixmapId si)
+{
+    b->pb_skin_index1 = si;
+}
+
+void
+pbutton_set_skin_index2(PButton * b, SkinPixmapId si)
+{
+    b->pb_skin_index2 = si;
+}
+
+void
+pbutton_set_button_data(PButton * b, gint nx, gint ny, gint px, gint py)
+{
+    if (nx > -1)
+        b->pb_nx = nx;
+    if (ny > -1)
+        b->pb_ny = ny;
+    if (px > -1)
+        b->pb_px = px;
+    if (py > -1)
+        b->pb_py = py;
+}
+
+
+PButton *
+create_pbutton_ex(GList ** wlist, GdkPixmap * parent, GdkGC * gc,
+                  gint x, gint y, gint w, gint h, gint nx,
+                  gint ny, gint px, gint py, void (*push_cb) (void),
+		  void (*release_cb) (void),
+                  SkinPixmapId si1, SkinPixmapId si2)
+{
+    PButton *b;
+
+    b = g_new0(PButton, 1);
+    widget_init(&b->pb_widget, parent, gc, x, y, w, h, 1);
+    b->pb_widget.button_press_cb =
+        (void (*)(GtkWidget *, GdkEventButton *, gpointer))
+        pbutton_button_press_cb;
+    b->pb_widget.button_release_cb =
+        (void (*)(GtkWidget *, GdkEventButton *, gpointer))
+        pbutton_button_release_cb;
+    b->pb_widget.motion_cb =
+        (void (*)(GtkWidget *, GdkEventMotion *, gpointer))
+        pbutton_motion_cb;
+
+    b->pb_widget.draw = (void (*)(Widget *)) pbutton_draw;
+    b->pb_nx = nx;
+    b->pb_ny = ny;
+    b->pb_px = px;
+    b->pb_py = py;
+    b->pb_push_cb = push_cb;
+    b->pb_release_cb = release_cb;
+    b->pb_skin_index1 = si1;
+    b->pb_skin_index2 = si2;
+    b->pb_allow_draw = TRUE;
+    b->pb_inside = 0;
+    b->pb_pressed = 0;
+    widget_list_add(wlist, WIDGET(b));
+
+    return b;
+}
+
+PButton *
+create_pbutton(GList ** wlist, GdkPixmap * parent, GdkGC * gc,
+               gint x, gint y, gint w, gint h, gint nx, gint ny,
+               gint px, gint py, void (*cb) (void), SkinPixmapId si)
+{
+    return create_pbutton_ex(wlist, parent, gc, x, y, w, h, nx, ny, px, py,
+                             NULL, cb, si, si);
+}
+
+void
+free_pbutton(PButton * b)
+{
+    g_free(b);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/pbutton.h	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,64 @@
+/*  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; 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.
+ */
+
+#ifndef _WIDGETCORE_H_
+#error Please do not include me directly! Use widgetcore.h instead!
+#endif
+
+#ifndef PBUTTON_H
+#define PBUTTON_H
+
+#include <glib.h>
+#include <gdk/gdk.h>
+
+#include "widget.h"
+#include "skin.h"
+
+#define PBUTTON(x)  ((PButton *)(x))
+struct _PButton {
+    Widget pb_widget;
+    gint pb_nx, pb_ny;
+    gint pb_px, pb_py;
+    gboolean pb_pressed;
+    gboolean pb_inside;
+    gboolean pb_allow_draw;
+    void (*pb_push_cb) (void);
+    void (*pb_release_cb) (void);
+    SkinPixmapId pb_skin_index1, pb_skin_index2;
+};
+
+typedef struct _PButton PButton;
+
+PButton *create_pbutton(GList ** wlist, GdkPixmap * parent, GdkGC * gc,
+                        gint x, gint y, gint w, gint h, gint nx, gint ny,
+                        gint px, gint py, void (*push_cb) (void), SkinPixmapId si);
+PButton *create_pbutton_ex(GList ** wlist, GdkPixmap * parent, GdkGC * gc,
+                           gint x, gint y, gint w, gint h, gint nx,
+                           gint ny, gint px, gint py, void (*push_cb) (void),
+			   void (*release_cb) (void), SkinPixmapId si1,
+			   SkinPixmapId si2);
+void free_pbutton(PButton * b);
+void pbutton_set_skin_index(PButton * b, SkinPixmapId si);
+void pbutton_set_skin_index1(PButton * b, SkinPixmapId si);
+void pbutton_set_skin_index2(PButton * b, SkinPixmapId si);
+void pbutton_set_button_data(PButton * b, gint nx, gint ny, gint px, gint py);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/playlist_list.c	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,966 @@
+/*  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; 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.
+ */
+
+/*
+ *  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 "widgetcore.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "main.h"
+#include "input.h"
+#include "playback.h"
+#include "playlist.h"
+#include "ui_playlist.h"
+#include "util.h"
+
+#include "debug.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;
+
+GdkPixmap *rootpix;
+
+void playlist_list_draw(Widget * w);
+
+/* Sort of stolen from XChat, but not really, as theres uses Xlib */
+static void
+shade_gdkimage_generic (GdkVisual *visual, GdkImage *ximg, int bpl, int w, int h, int rm, int gm, int bm, int bg)
+{
+	int x, y;
+	int bgr = (256 - rm) * (bg & visual->red_mask);
+	int bgg = (256 - gm) * (bg & visual->green_mask);
+	int bgb = (256 - bm) * (bg & visual->blue_mask);
+
+	for (x = 0; x < w; x++)
+	{
+		for (y = 0; y < h; y++)
+		{
+			unsigned long pixel = gdk_image_get_pixel (ximg, x, y);
+			int r, g, b;
+
+			r = rm * (pixel & visual->red_mask) + bgr;
+			g = gm * (pixel & visual->green_mask) + bgg;
+			b = bm * (pixel & visual->blue_mask) + bgb;
+
+			gdk_image_put_pixel (ximg, x, y,
+				((r >> 8) & visual->red_mask) |
+				((g >> 8) & visual->green_mask) |
+				((b >> 8) & visual->blue_mask));
+		}
+	}
+}
+
+/* and this is definately mine... -nenolod */
+GdkPixmap *
+shade_pixmap(GdkPixmap *in, gint x, gint y, gint x_offset, gint y_offset, gint w, gint h, GdkColor *shade_color)
+{
+	GdkImage *ximg;
+	GdkPixmap *p = gdk_pixmap_new(in, w, h, -1);
+	GdkGC *gc = gdk_gc_new(p);
+
+        gdk_draw_pixmap(p, gc, in, x, y, 0, 0, w, h);
+
+	gdk_error_trap_push();
+
+	ximg = gdk_drawable_copy_to_image(in, NULL, x, y, 0, 0, w, h);	/* copy */
+
+	gdk_error_trap_pop();
+
+	if (GDK_IS_IMAGE(ximg))
+	{
+		shade_gdkimage_generic(gdk_drawable_get_visual(GDK_WINDOW(playlistwin->window)),
+			ximg, ximg->bpl, w, h, 60, 60, 60, shade_color->pixel);
+
+		gdk_draw_image(p, gc, ximg, 0, 0, x, y, w, h);
+	}
+	else {
+		cfg.playlist_transparent = FALSE;
+	}
+
+	g_object_unref(gc);
+
+	return p;
+}
+
+#ifdef GDK_WINDOWING_X11
+
+#include <gdk/gdkx.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+
+GdkDrawable *get_transparency_pixmap(void)
+{
+	GdkDrawable *root;
+	XID *pixmaps;
+	GdkAtom prop_type;
+	gint prop_size;
+	GdkPixmap *pixmap;
+	gboolean ret;
+
+	root = gdk_get_default_root_window();
+
+	pixmap = NULL;
+	pixmaps = NULL;
+
+	gdk_error_trap_push();
+
+	ret = gdk_property_get(root, gdk_atom_intern("_XROOTPMAP_ID", TRUE),
+			0, 0, INT_MAX - 3,
+			FALSE,
+			&prop_type, NULL, &prop_size,
+			(guchar **) &pixmaps);
+
+	gdk_error_trap_pop();
+
+	if ((ret == TRUE) && (prop_type == GDK_TARGET_PIXMAP) && (prop_size >= sizeof(XID)) && (pixmaps != NULL))
+	{
+		pixmap = gdk_pixmap_foreign_new_for_display(gdk_drawable_get_display(root),
+			pixmaps[0]);
+
+		if (pixmaps != NULL)
+			g_free(pixmaps);
+	}
+
+	return GDK_DRAWABLE(pixmap);
+}
+
+static GdkFilterReturn
+root_event_cb (GdkXEvent *xev, GdkEventProperty *event, gpointer data)
+{
+        static Atom at = None;
+        XEvent *xevent = (XEvent *)xev;
+
+        if (xevent->type == PropertyNotify)
+        {
+                if (at == None)
+                        at = XInternAtom (xevent->xproperty.display, "_XROOTPMAP_ID", True);
+
+                if (at == xevent->xproperty.atom)
+		{
+                        rootpix = shade_pixmap(get_transparency_pixmap(), 0, 0, 0, 0, gdk_screen_width(), gdk_screen_height(),
+                            skin_get_color(bmp_active_skin, SKIN_PLEDIT_NORMALBG));
+
+			if (cfg.playlist_transparent)
+			{
+				playlistwin_update_list();
+				draw_playlist_window(TRUE);
+			}
+		}
+        }
+
+        return GDK_FILTER_CONTINUE;
+}
+
+#else
+
+GdkPixmap *get_transparency_pixmap(void)
+{
+    return NULL;
+}
+
+#endif
+
+static gboolean
+playlist_list_auto_drag_down_func(gpointer data)
+{
+    PlayList_List *pl = data;
+
+    if (pl->pl_auto_drag_down) {
+        playlist_list_move_down(pl);
+        pl->pl_first++;
+        playlistwin_update_list();
+        return TRUE;
+    }
+    return FALSE;
+}
+
+static gboolean
+playlist_list_auto_drag_up_func(gpointer data)
+{
+    PlayList_List *pl = data;
+
+    if (pl->pl_auto_drag_up) {
+        playlist_list_move_up(pl);
+        pl->pl_first--;
+        playlistwin_update_list();
+        return TRUE;
+
+    }
+    return FALSE;
+}
+
+void
+playlist_list_move_up(PlayList_List * pl)
+{
+    GList *list;
+
+    PLAYLIST_LOCK();
+    if ((list = playlist_get()) == NULL) {
+        PLAYLIST_UNLOCK();
+        return;
+    }
+    if (PLAYLIST_ENTRY(list->data)->selected) {
+        /* We are at the top */
+        PLAYLIST_UNLOCK();
+        return;
+    }
+    while (list) {
+        if (PLAYLIST_ENTRY(list->data)->selected)
+            glist_moveup(list);
+        list = g_list_next(list);
+    }
+    PLAYLIST_UNLOCK();
+    if (pl->pl_prev_selected != -1)
+        pl->pl_prev_selected--;
+    if (pl->pl_prev_min != -1)
+        pl->pl_prev_min--;
+    if (pl->pl_prev_max != -1)
+        pl->pl_prev_max--;
+}
+
+void
+playlist_list_move_down(PlayList_List * pl)
+{
+    GList *list;
+
+    PLAYLIST_LOCK();
+
+    if (!(list = g_list_last(playlist_get()))) {
+        PLAYLIST_UNLOCK();
+        return;
+    }
+
+    if (PLAYLIST_ENTRY(list->data)->selected) {
+        /* We are at the bottom */
+        PLAYLIST_UNLOCK();
+        return;
+    }
+
+    while (list) {
+        if (PLAYLIST_ENTRY(list->data)->selected)
+            glist_movedown(list);
+        list = g_list_previous(list);
+    }
+
+    PLAYLIST_UNLOCK();
+
+    if (pl->pl_prev_selected != -1)
+        pl->pl_prev_selected++;
+    if (pl->pl_prev_min != -1)
+        pl->pl_prev_min++;
+    if (pl->pl_prev_max != -1)
+        pl->pl_prev_max++;
+}
+
+static void
+playlist_list_button_press_cb(GtkWidget * widget,
+                              GdkEventButton * event,
+                              PlayList_List * pl)
+{
+    gint nr, y;
+
+    if (event->button == 1 && pl->pl_fheight &&
+        widget_contains(&pl->pl_widget, event->x, event->y)) {
+
+        y = event->y - pl->pl_widget.y;
+        nr = (y / pl->pl_fheight) + pl->pl_first;
+
+        if (nr >= playlist_get_length())
+            nr = playlist_get_length() - 1;
+
+        if (!(event->state & GDK_CONTROL_MASK))
+            playlist_select_all(FALSE);
+
+        if (event->state & GDK_SHIFT_MASK && pl->pl_prev_selected != -1) {
+            playlist_select_range(pl->pl_prev_selected, nr, TRUE);
+            pl->pl_prev_min = pl->pl_prev_selected;
+            pl->pl_prev_max = nr;
+            pl->pl_drag_pos = nr - pl->pl_first;
+        }
+        else {
+            if (playlist_select_invert(nr)) {
+                if (event->state & GDK_CONTROL_MASK) {
+                    if (pl->pl_prev_min == -1) {
+                        pl->pl_prev_min = pl->pl_prev_selected;
+                        pl->pl_prev_max = pl->pl_prev_selected;
+                    }
+                    if (nr < pl->pl_prev_min)
+                        pl->pl_prev_min = nr;
+                    else if (nr > pl->pl_prev_max)
+                        pl->pl_prev_max = nr;
+                }
+                else
+                    pl->pl_prev_min = -1;
+                pl->pl_prev_selected = nr;
+                pl->pl_drag_pos = nr - pl->pl_first;
+            }
+        }
+        if (event->type == GDK_2BUTTON_PRESS) {
+            /*
+             * Ungrab the pointer to prevent us from
+             * hanging on to it during the sometimes slow
+             * bmp_playback_initiate().
+             */
+            gdk_pointer_ungrab(GDK_CURRENT_TIME);
+            gdk_flush();
+            playlist_set_position(nr);
+            if (!bmp_playback_get_playing())
+                bmp_playback_initiate();
+        }
+
+        pl->pl_dragging = TRUE;
+        playlistwin_update_list();
+    }
+}
+
+gint
+playlist_list_get_playlist_position(PlayList_List * pl,
+                                    gint x,
+                                    gint y)
+{
+    gint iy, length;
+    gint ret;
+
+    if (!widget_contains(WIDGET(pl), x, y) || !pl->pl_fheight)
+        return -1;
+
+    if ((length = playlist_get_length()) == 0)
+        return -1;
+    iy = y - pl->pl_widget.y;
+
+    ret = (iy / pl->pl_fheight) + pl->pl_first;
+
+    if(ret > length-1)
+	    ret = -1;
+
+    return ret;
+}
+
+static void
+playlist_list_motion_cb(GtkWidget * widget,
+                        GdkEventMotion * event,
+                        PlayList_List * pl)
+{
+    gint nr, y, off, i;
+
+    if (pl->pl_dragging) {
+        y = event->y - pl->pl_widget.y;
+        nr = (y / pl->pl_fheight);
+        if (nr < 0) {
+            nr = 0;
+            if (!pl->pl_auto_drag_up) {
+                pl->pl_auto_drag_up = TRUE;
+                pl->pl_auto_drag_up_tag =
+                    gtk_timeout_add(100, playlist_list_auto_drag_up_func, pl);
+            }
+        }
+        else if (pl->pl_auto_drag_up)
+            pl->pl_auto_drag_up = FALSE;
+
+        if (nr >= pl->pl_num_visible) {
+            nr = pl->pl_num_visible - 1;
+            if (!pl->pl_auto_drag_down) {
+                pl->pl_auto_drag_down = TRUE;
+                pl->pl_auto_drag_down_tag =
+                    gtk_timeout_add(100, playlist_list_auto_drag_down_func,
+                                    pl);
+            }
+        }
+        else if (pl->pl_auto_drag_down)
+            pl->pl_auto_drag_down = FALSE;
+
+        off = nr - pl->pl_drag_pos;
+        if (off) {
+            for (i = 0; i < abs(off); i++) {
+                if (off < 0)
+                    playlist_list_move_up(pl);
+                else
+                    playlist_list_move_down(pl);
+
+            }
+            playlistwin_update_list();
+        }
+        pl->pl_drag_pos = nr;
+    }
+}
+
+static void
+playlist_list_button_release_cb(GtkWidget * widget,
+                                GdkEventButton * event,
+                                PlayList_List * pl)
+{
+    pl->pl_dragging = FALSE;
+    pl->pl_auto_drag_down = FALSE;
+    pl->pl_auto_drag_up = FALSE;
+}
+
+static void
+playlist_list_draw_string(PlayList_List * pl,
+                          PangoFontDescription * font,
+                          gint line,
+                          gint width,
+                          const gchar * text,
+                          guint ppos)
+{
+    guint plist_length_int;
+
+    PangoLayout *layout;
+
+    REQUIRE_STATIC_LOCK(playlist);
+
+    if (cfg.show_numbers_in_pl) {
+        gchar *pos_string = g_strdup_printf(cfg.show_separator_in_pl == TRUE ? "%d" : "%d.", ppos);
+        plist_length_int =
+            gint_count_digits(playlist_get_length_nolock()) + !cfg.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);
+        gdk_draw_layout(pl->pl_widget.parent, pl->pl_widget.gc,
+                        pl->pl_widget.x +
+                        (width_approx_digits *
+                         (-1 + plist_length_int - strlen(pos_string))) +
+                        (width_approx_digits / 4),
+                        pl->pl_widget.y + (line - 1) * pl->pl_fheight +
+                        ascent + abs(descent), layout);
+        g_free(pos_string);
+        g_object_unref(layout);
+
+        if (!cfg.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);
+    gdk_draw_layout(pl->pl_widget.parent, pl->pl_widget.gc,
+                    pl->pl_widget.x + padding + (width_approx_letters / 4),
+                    pl->pl_widget.y + (line - 1) * pl->pl_fheight +
+                    ascent + abs(descent), layout);
+
+    g_object_unref(layout);
+}
+
+void
+playlist_list_draw(Widget * w)
+{
+    PlayList_List *pl = PLAYLIST_LIST(w);
+    GList *list;
+    GdkGC *gc;
+    GdkPixmap *obj;
+    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;
+
+    gchar tail[100];
+    gchar queuepos[255];
+    gchar length[40];
+
+    gchar **frags;
+    gchar *frag0;
+
+    gint plw_w, plw_h;
+
+    GdkRectangle *playlist_rect;
+
+    gc = pl->pl_widget.gc;
+
+    width = pl->pl_widget.width;
+    height = pl->pl_widget.height;
+
+    obj = pl->pl_widget.parent;
+
+    gtk_window_get_size(GTK_WINDOW(playlistwin), &plw_w, &plw_h);
+
+    playlist_rect = g_new0(GdkRectangle, 1);
+
+    playlist_rect->x = 0;
+    playlist_rect->y = 0;
+    playlist_rect->width = plw_w - 17;
+    playlist_rect->height = plw_h - 36;
+
+    gdk_gc_set_clip_origin(gc, 31, 58);
+    gdk_gc_set_clip_rectangle(gc, playlist_rect);
+
+    if (cfg.playlist_transparent == FALSE)
+    {
+        gdk_gc_set_foreground(gc,
+                              skin_get_color(bmp_active_skin,
+                                             SKIN_PLEDIT_NORMALBG));
+        gdk_draw_rectangle(obj, gc, TRUE, pl->pl_widget.x, pl->pl_widget.y,
+                              width, height);
+    }
+    else
+    {
+	if (!rootpix)
+           rootpix = shade_pixmap(get_transparency_pixmap(), 0, 0, 0, 0, gdk_screen_width(), gdk_screen_height(), 
+    			    skin_get_color(bmp_active_skin, SKIN_PLEDIT_NORMALBG));
+        gdk_draw_pixmap(obj, gc, rootpix, cfg.playlist_x + pl->pl_widget.x,
+                    cfg.playlist_y + pl->pl_widget.y, pl->pl_widget.x, pl->pl_widget.y,
+                    width, height);
+    }
+
+    if (!playlist_list_font) {
+        g_critical("Couldn't open playlist font");
+        return;
+    }
+
+    pl->pl_fheight = (ascent + abs(descent));
+    pl->pl_num_visible = height / pl->pl_fheight;
+
+    max_first = playlist_get_length() - pl->pl_num_visible;
+    max_first = MAX(max_first, 0);
+
+    pl->pl_first = CLAMP(pl->pl_first, 0, max_first);
+
+    PLAYLIST_LOCK();
+    list = playlist_get();
+    list = g_list_nth(list, pl->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->pl_first;
+         list && i < pl->pl_first + pl->pl_num_visible;
+         list = g_list_next(list), i++) {
+        PlaylistEntry *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_get();
+    list = g_list_nth(list, pl->pl_first);
+
+    for (i = pl->pl_first;
+         list && i < pl->pl_first + pl->pl_num_visible;
+         list = g_list_next(list), i++) {
+        gint pos;
+        PlaylistEntry *entry = list->data;
+
+        if (entry->selected) {
+            gdk_gc_set_foreground(gc,
+                                  skin_get_color(bmp_active_skin,
+                                                 SKIN_PLEDIT_SELECTEDBG));
+            gdk_draw_rectangle(obj, gc, TRUE, pl->pl_widget.x,
+                               pl->pl_widget.y +
+                               ((i - pl->pl_first) * pl->pl_fheight),
+                               width, pl->pl_fheight);
+        }
+
+        /* 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 *basename = g_path_get_basename(entry->filename);
+            title = filename_to_utf8(basename);
+            g_free(basename);
+        }
+        else
+            title = str_to_utf8(entry->title);
+
+        pos = playlist_get_queue_position(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));
+        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 == playlist_get_position_nolock())
+            gdk_gc_set_foreground(gc,
+                                  skin_get_color(bmp_active_skin,
+                                                 SKIN_PLEDIT_CURRENT));
+        else
+            gdk_gc_set_foreground(gc,
+                                  skin_get_color(bmp_active_skin,
+                                                 SKIN_PLEDIT_NORMAL));
+        playlist_list_draw_string(pl, playlist_list_font,
+                                  i - pl->pl_first, tail_width, title,
+                                  i + 1);
+
+        x = pl->pl_widget.x + width - width_approx_digits * 2;
+        y = pl->pl_widget.y + ((i - pl->pl_first) -
+                               1) * pl->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);
+            gdk_draw_layout(obj, gc, x - (0.5 * width_approx_digits),
+                            y + abs(descent), 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);
+            gdk_draw_layout(obj, gc, x - (0.75 * width_approx_digits),
+                            y + abs(descent), layout);
+            g_object_unref(layout);
+
+            g_free(frag0);
+            g_strfreev(frags);
+        }
+
+        if (pos != -1) {
+
+            /* DON'T remove the commented code yet please     -- Milosz */
+
+            if (tpadding_dwidth > 0)
+                queue_tailpadding = tpadding_dwidth + 1;
+            else
+                queue_tailpadding = -0.75;
+
+            gdk_draw_rectangle(obj, gc, FALSE,
+                               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->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);
+
+            gdk_draw_layout(obj, gc,
+                            x -
+                            ((queue_tailpadding +
+                              strlen(queuepos)) * width_approx_digits) +
+                            (width_approx_digits / 4),
+                            y + abs(descent), layout);
+            g_object_unref(layout);
+        }
+
+        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->pl_drag_motion) {
+        guint pos, plength, lpadding;
+	gint x, y, plx, ply;
+
+        if (cfg.show_numbers_in_pl) {
+            lpadding = gint_count_digits(playlist_get_length_nolock()) + 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 = playlist_get_length_nolock();
+
+        x = pl->drag_motion_x;
+        y = pl->drag_motion_y;
+
+        plx = pl->pl_widget.x;
+        ply = pl->pl_widget.y;
+
+        if ((x > pl->pl_widget.x) && !(x > pl->pl_widget.width)) {
+
+            if ((y > pl->pl_widget.y)
+                && !(y > (pl->pl_widget.height + ply))) {
+
+                pos = ((y - ((Widget *) pl)->y) / pl->pl_fheight) +
+                    pl->pl_first;
+
+                if (pos > (plength)) {
+                    pos = plength;
+                }
+
+                gdk_gc_set_foreground(gc,
+                                      skin_get_color(bmp_active_skin,
+                                                     SKIN_PLEDIT_CURRENT));
+
+                gdk_draw_line(obj, gc, pl->pl_widget.x,
+			      pl->pl_widget.y + ((pos - pl->pl_first) * pl->pl_fheight),
+                              pl->pl_widget.width + pl->pl_widget.x - 1,
+                              pl->pl_widget.y +
+                              ((pos - pl->pl_first) * pl->pl_fheight));
+            }
+
+        }
+
+        /* 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 < ply) || (y > pl->pl_widget.height + ply)) {
+            if ((y >= 0) || (y <= (pl->pl_widget.height + ply))) {
+                pos = plength;
+                gdk_gc_set_foreground(gc,
+                                      skin_get_color(bmp_active_skin,
+                                                     SKIN_PLEDIT_CURRENT));
+
+                gdk_draw_line(obj, gc, pl->pl_widget.x,
+                              pl->pl_widget.y +
+                              ((pos - pl->pl_first) * pl->pl_fheight),
+                              pl->pl_widget.width + pl->pl_widget.x - 1,
+                              pl->pl_widget.y +
+                              ((pos - pl->pl_first) * pl->pl_fheight));
+
+            }
+        }
+    }
+
+    gdk_gc_set_foreground(gc,
+                          skin_get_color(bmp_active_skin,
+                                         SKIN_PLEDIT_NORMAL));
+
+    if (cfg.show_numbers_in_pl) {
+
+        padding_plength = playlist_get_length_nolock();
+
+        if (padding_plength == 0) {
+            padding_dwidth = 0;
+        }
+        else {
+            padding_dwidth = gint_count_digits(playlist_get_length_nolock());
+        }
+
+        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 (cfg.show_separator_in_pl) {
+            gdk_draw_line(obj, gc,
+                          pl->pl_widget.x + padding,
+                          pl->pl_widget.y,
+                          pl->pl_widget.x + padding,
+                          pl->pl_widget.y + pl->pl_widget.height - 1);
+        }
+    }
+
+    if (tpadding_dwidth != 0)
+    {
+        tpadding = (tpadding_dwidth * width_approx_digits) + (width_approx_digits * 1.5);
+
+        if (has_slant)
+            tpadding += width_approx_digits_half;
+
+        if (cfg.show_separator_in_pl) {
+            gdk_draw_line(obj, gc,
+                          pl->pl_widget.x + pl->pl_widget.width - tpadding,
+                          pl->pl_widget.y,
+                          pl->pl_widget.x + pl->pl_widget.width - tpadding,
+                          pl->pl_widget.y + pl->pl_widget.height - 1);
+        }
+    }
+
+    gdk_gc_set_clip_origin(gc, 0, 0);
+    gdk_gc_set_clip_rectangle(gc, NULL);
+
+    PLAYLIST_UNLOCK();
+
+    gdk_flush();
+
+    g_free(playlist_rect);
+}
+
+
+PlayList_List *
+create_playlist_list(GList ** wlist,
+                     GdkPixmap * parent,
+                     GdkGC * gc,
+                     gint x, gint y,
+                     gint w, gint h)
+{
+    PlayList_List *pl;
+
+    pl = g_new0(PlayList_List, 1);
+    widget_init(&pl->pl_widget, parent, gc, x, y, w, h, TRUE);
+
+    pl->pl_widget.button_press_cb =
+        (WidgetButtonPressFunc) playlist_list_button_press_cb;
+    pl->pl_widget.button_release_cb =
+        (WidgetButtonReleaseFunc) playlist_list_button_release_cb;
+    pl->pl_widget.motion_cb = (WidgetMotionFunc) playlist_list_motion_cb;
+    pl->pl_widget.draw = playlist_list_draw;
+
+    pl->pl_prev_selected = -1;
+    pl->pl_prev_min = -1;
+    pl->pl_prev_max = -1;
+
+    widget_list_add(wlist, WIDGET(pl));
+
+#ifdef GDK_WINDOWING_X11
+    gdk_window_set_events (gdk_get_default_root_window(), GDK_PROPERTY_CHANGE_MASK);
+    gdk_window_add_filter (gdk_get_default_root_window(), (GdkFilterFunc)root_event_cb, pl);
+#endif
+
+    return pl;
+}
+
+void
+playlist_list_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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/playlist_list.h	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,60 @@
+/*  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; 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.
+ */
+
+#ifndef _WIDGETCORE_H_
+#error Please do not include me directly! Use widgetcore.h instead!
+#endif
+
+#ifndef PLAYLIST_LIST_H
+#define PLAYLIST_LIST_H
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+#include "widget.h"
+
+#define PLAYLIST_LIST(x)    ((PlayList_List *)(x))
+struct _PlayList_List {
+    Widget pl_widget;
+    gint pl_first, pl_fheight, pl_prev_selected, pl_prev_min, pl_prev_max;
+    gint pl_num_visible, pl_drag_pos;
+    gboolean pl_dragging, pl_auto_drag_down, pl_auto_drag_up;
+    gint pl_auto_drag_up_tag, pl_auto_drag_down_tag;
+    gboolean pl_drag_motion;
+    gint drag_motion_x, drag_motion_y;
+    gboolean pl_tooltips;
+};
+
+typedef struct _PlayList_List PlayList_List;
+
+PlayList_List *create_playlist_list(GList ** wlist, GdkPixmap * parent,
+                                    GdkGC * gc, gint x, gint y, gint w,
+                                    gint h);
+void playlist_list_move_up(PlayList_List * pl);
+void playlist_list_move_down(PlayList_List * pl);
+int playlist_list_get_playlist_position(PlayList_List * pl, gint x, gint y);
+void playlist_list_set_font(const gchar * font);
+GdkPixmap *rootpix;
+GdkPixmap *shade_pixmap(GdkDrawable *in, gint x, gint y, gint x_offset, gint y_offset, gint w, gint h, GdkColor *shade_color);
+GdkDrawable *get_transparency_pixmap(void);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/playlist_slider.c	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,168 @@
+/*  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; 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 "widgetcore.h"
+
+#include <glib.h>
+
+#include "playlist.h"
+#include "ui_playlist.h"
+#include "skin.h"
+#include "widget.h"
+
+void
+playlistslider_draw(Widget * w)
+{
+    PlaylistSlider *ps = (PlaylistSlider *) w;
+    GdkPixmap *obj;
+    gint y, skinx;
+
+    g_return_if_fail(ps != NULL);
+    g_return_if_fail(ps->ps_list != NULL);
+
+    if (playlist_get_length() > ps->ps_list->pl_num_visible)
+        y = (ps->ps_list->pl_first * (ps->ps_widget.height - 19)) /
+            (playlist_get_length() - ps->ps_list->pl_num_visible);
+    else
+        y = 0;
+
+    obj = ps->ps_widget.parent;
+
+    if (ps->ps_back_image) {
+        if (skin_get_id() != ps->ps_skin_id)
+            ps->ps_skin_id = skin_get_id();
+        else if (ps->ps_widget.height == ps->ps_prev_height)
+            gdk_draw_image(obj, ps->ps_widget.gc,
+                           ps->ps_back_image, 0, 0,
+                           ps->ps_widget.x,
+                           ps->ps_widget.y + ps->ps_prev_y, 8, 18);
+        gdk_image_destroy(ps->ps_back_image);
+    }
+
+    ps->ps_prev_y = y;
+    ps->ps_prev_height = ps->ps_widget.height;
+    ps->ps_back_image = gdk_drawable_get_image(obj, ps->ps_widget.x,
+                                               ps->ps_widget.y + y, 8, 18);
+    if (ps->ps_is_draging)
+        skinx = 61;
+    else
+        skinx = 52;
+
+    skin_draw_pixmap(bmp_active_skin, obj, ps->ps_widget.gc, SKIN_PLEDIT,
+                     skinx, 53, ps->ps_widget.x, ps->ps_widget.y + y, 8, 18);
+}
+
+static void
+playlistslider_set_pos(PlaylistSlider * ps, gint y)
+{
+    gint pos;
+
+    y = CLAMP(y, 0, ps->ps_widget.height - 19);
+
+    pos = (y * (playlist_get_length() - ps->ps_list->pl_num_visible)) /
+        (ps->ps_widget.height - 19);
+    playlistwin_set_toprow(pos);
+}
+
+
+void
+playlistslider_button_press_cb(GtkWidget * widget,
+                               GdkEventButton * event, PlaylistSlider * ps)
+{
+    gint y = event->y - ps->ps_widget.y;
+
+    if (!widget_contains(&ps->ps_widget, event->x, event->y))
+        return;
+
+    if (event->button != 1 && event->button != 2)
+        return;
+
+    if ((y >= ps->ps_prev_y && y < ps->ps_prev_y + 18)) {
+        ps->ps_is_draging |= event->button;
+        ps->ps_drag_y = y - ps->ps_prev_y;
+        widget_draw(WIDGET(ps));
+    }
+    else if (event->button == 2) {
+        playlistslider_set_pos(ps, y);
+        ps->ps_is_draging |= event->button;
+        ps->ps_drag_y = 0;
+        widget_draw(WIDGET(ps));
+    }
+    else {
+        gint n = ps->ps_list->pl_num_visible / 2;
+        if (y < ps->ps_prev_y)
+            n *= -1;
+        playlistwin_scroll(n);
+    }
+}
+
+void
+playlistslider_button_release_cb(GtkWidget * widget,
+                                 GdkEventButton * event,
+                                 PlaylistSlider * ps)
+{
+    if (ps->ps_is_draging) {
+        ps->ps_is_draging &= ~event->button;
+        widget_draw(WIDGET(ps));
+    }
+}
+
+void
+playlistslider_motion_cb(GtkWidget * widget, GdkEventMotion * event,
+                         PlaylistSlider * ps)
+{
+    gint y;
+
+    if (!ps->ps_is_draging)
+        return;
+
+    y = event->y - ps->ps_widget.y - ps->ps_drag_y;
+    playlistslider_set_pos(ps, y);
+}
+
+PlaylistSlider *
+create_playlistslider(GList ** wlist, GdkPixmap * parent,
+                      GdkGC * gc, gint x, gint y, gint h,
+                      PlayList_List * list)
+{
+    PlaylistSlider *ps;
+
+    ps = g_new0(PlaylistSlider, 1);
+    widget_init(&ps->ps_widget, parent, gc, x, y, 8, h, 1);
+
+    ps->ps_widget.button_press_cb =
+        (void (*)(GtkWidget *, GdkEventButton *, gpointer))
+        playlistslider_button_press_cb;
+
+    ps->ps_widget.button_release_cb =
+        (void (*)(GtkWidget *, GdkEventButton *, gpointer))
+        playlistslider_button_release_cb;
+
+    ps->ps_widget.motion_cb =
+        (void (*)(GtkWidget *, GdkEventMotion *, gpointer))
+        playlistslider_motion_cb;
+
+    ps->ps_widget.draw = playlistslider_draw;
+    ps->ps_list = list;
+
+    widget_list_add(wlist, WIDGET(ps));
+    return ps;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/playlist_slider.h	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,51 @@
+/*  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; 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.
+ */
+
+#ifndef _WIDGETCORE_H_
+#error Please do not include me directly! Use widgetcore.h instead!
+#endif
+
+#ifndef PLAYLIST_SLIDER_H
+#define PLAYLIST_SLIDER_H
+
+#include <glib.h>
+#include <gdk/gdk.h>
+
+#include "playlist_list.h"
+#include "widget.h"
+
+#define PLAYLIST_SLIDER(x)  ((PlayerlistSlider *)(x))
+struct _PlaylistSlider {
+    Widget ps_widget;
+    PlayList_List *ps_list;
+    gboolean ps_is_draging;
+    gint ps_drag_y, ps_prev_y, ps_prev_height;
+    GdkImage *ps_back_image;
+    gint ps_skin_id;
+};
+
+typedef struct _PlaylistSlider PlaylistSlider;
+
+PlaylistSlider *create_playlistslider(GList ** wlist, GdkPixmap * parent,
+                                      GdkGC * gc, gint x, gint y, gint h,
+                                      PlayList_List * list);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/playstatus.c	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,102 @@
+/*  XMMS - Cross-platform multimedia player
+ *  Copyright (C) 1998-2000  Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "widgetcore.h"
+
+#include <glib.h>
+#include <gdk/gdk.h>
+
+#include "skin.h"
+#include "widget.h"
+
+void
+playstatus_draw(Widget * w)
+{
+    PlayStatus *ps = PLAY_STATUS(w);
+    GdkPixmap *obj;
+
+    if (!w)
+        return;
+
+    obj = ps->ps_widget.parent;
+    if (ps->ps_status == STATUS_STOP && ps->ps_status_buffering == TRUE)
+        ps->ps_status_buffering = FALSE;
+    if (ps->ps_status == STATUS_PLAY && ps->ps_status_buffering == TRUE)
+        skin_draw_pixmap(bmp_active_skin, obj, ps->ps_widget.gc,
+                         SKIN_PLAYPAUSE, 39, 0, ps->ps_widget.x,
+                         ps->ps_widget.y, 3, 9);
+    else if (ps->ps_status == STATUS_PLAY)
+        skin_draw_pixmap(bmp_active_skin, obj, ps->ps_widget.gc,
+                         SKIN_PLAYPAUSE, 36, 0, ps->ps_widget.x,
+                         ps->ps_widget.y, 3, 9);
+    else
+        skin_draw_pixmap(bmp_active_skin, obj, ps->ps_widget.gc,
+                         SKIN_PLAYPAUSE, 27, 0, ps->ps_widget.x,
+                         ps->ps_widget.y, 2, 9);
+    switch (ps->ps_status) {
+    case STATUS_STOP:
+        skin_draw_pixmap(bmp_active_skin, obj, ps->ps_widget.gc,
+                         SKIN_PLAYPAUSE, 18, 0,
+                         ps->ps_widget.x + 2, ps->ps_widget.y, 9, 9);
+        break;
+    case STATUS_PAUSE:
+        skin_draw_pixmap(bmp_active_skin, obj, ps->ps_widget.gc,
+                         SKIN_PLAYPAUSE, 9, 0,
+                         ps->ps_widget.x + 2, ps->ps_widget.y, 9, 9);
+        break;
+    case STATUS_PLAY:
+        skin_draw_pixmap(bmp_active_skin, obj, ps->ps_widget.gc,
+                         SKIN_PLAYPAUSE, 1, 0,
+                         ps->ps_widget.x + 3, ps->ps_widget.y, 8, 9);
+        break;
+    }
+}
+
+void
+playstatus_set_status(PlayStatus * ps, PStatus status)
+{
+    if (!ps)
+        return;
+
+    ps->ps_status = status;
+    widget_draw(WIDGET(ps));
+}
+
+void
+playstatus_set_status_buffering(PlayStatus * ps, gboolean status)
+{
+    if (!ps)
+        return;
+
+    ps->ps_status_buffering = status;
+    widget_draw(WIDGET(ps));
+}
+
+PlayStatus *
+create_playstatus(GList ** wlist, GdkPixmap * parent,
+                  GdkGC * gc, gint x, gint y)
+{
+    PlayStatus *ps;
+
+    ps = g_new0(PlayStatus, 1);
+    widget_init(&ps->ps_widget, parent, gc, x, y, 11, 9, TRUE);
+    ps->ps_widget.draw = playstatus_draw;
+
+    widget_list_add(wlist, WIDGET(ps));
+    return ps;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/playstatus.h	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,46 @@
+/*  XMMS - Cross-platform multimedia player
+ *  Copyright (C) 1998-2000  Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _WIDGETCORE_H_
+#error Please do not include me directly! Use widgetcore.h instead!
+#endif
+
+#ifndef PLAYSTATUS_H
+#define PLAYSTATUS_H
+
+#include "widget.h"
+
+typedef enum {
+    STATUS_STOP, STATUS_PAUSE, STATUS_PLAY
+} PStatus;
+
+#define PLAY_STATUS(x)  ((PlayStatus *)(x))
+struct _PlayStatus {
+    Widget ps_widget;
+    PStatus ps_status;
+    gboolean ps_status_buffering;
+};
+
+typedef struct _PlayStatus PlayStatus;
+
+void playstatus_set_status(PlayStatus * ps, PStatus status);
+void playstatus_set_status_buffering(PlayStatus * ps, gboolean status);
+PlayStatus *create_playstatus(GList ** wlist, GdkPixmap * parent,
+                              GdkGC * gc, gint x, gint y);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/sbutton.c	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,99 @@
+/*  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; 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 "widgetcore.h"
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+
+void
+sbutton_button_press_cb(GtkWidget * widget,
+                        GdkEventButton * event,
+                        SButton * button)
+{
+    if (event->button != 1)
+        return;
+
+    if (widget_contains(&button->sb_widget, event->x, event->y)) {
+        button->sb_pressed = 1;
+        button->sb_inside = 1;
+    }
+}
+
+void
+sbutton_button_release_cb(GtkWidget * widget, GdkEventButton * event,
+                          SButton * button)
+{
+    if (event->button != 1)
+        return;
+    if (button->sb_inside && button->sb_pressed) {
+        button->sb_inside = 0;
+        if (button->sb_push_cb)
+            button->sb_push_cb();
+    }
+    if (button->sb_pressed)
+        button->sb_pressed = 0;
+}
+
+void
+sbutton_motion_cb(GtkWidget * widget, GdkEventMotion * event,
+                  SButton * button)
+{
+    int inside;
+
+    if (!button->sb_pressed)
+        return;
+
+    inside = widget_contains(&button->sb_widget, event->x, event->y);
+
+    if (inside != button->sb_inside)
+        button->sb_inside = inside;
+}
+
+SButton *
+create_sbutton(GList ** wlist, GdkPixmap * parent, GdkGC * gc,
+               gint x, gint y, gint w, gint h, void (*cb) (void))
+{
+    SButton *b;
+
+    b = g_new0(SButton, 1);
+    widget_init(&b->sb_widget, parent, gc, x, y, w, h, 1);
+    b->sb_widget.button_press_cb =
+        (void (*)(GtkWidget *, GdkEventButton *, gpointer))
+        sbutton_button_press_cb;
+    b->sb_widget.button_release_cb =
+        (void (*)(GtkWidget *, GdkEventButton *, gpointer))
+        sbutton_button_release_cb;
+    b->sb_widget.motion_cb =
+        (void (*)(GtkWidget *, GdkEventMotion *, gpointer))
+        sbutton_motion_cb;
+    b->sb_push_cb = cb;
+
+    widget_list_add(wlist, WIDGET(b));
+    return b;
+}
+
+void
+free_sbutton(SButton * b)
+{
+    g_free(b);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/sbutton.h	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,47 @@
+/*  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; 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.
+ */
+
+#ifndef _WIDGETCORE_H_
+#error Please do not include me directly! Use widgetcore.h instead!
+#endif
+
+#ifndef SBUTTON_H
+#define SBUTTON_H
+
+#include <glib.h>
+#include <gdk/gdk.h>
+
+#include "widget.h"
+
+#define SBUTTON(x)  ((SButton *)(x))
+struct _SButton {
+    Widget sb_widget;
+    gint sb_pressed, sb_inside;
+    void (*sb_push_cb) (void);
+};
+
+typedef struct _SButton SButton;
+
+SButton *create_sbutton(GList ** wlist, GdkPixmap * parent, GdkGC * gc,
+                        gint x, gint y, gint w, gint h, void (*cb) (void));
+void free_sbutton(SButton * b);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/skin.c	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,1254 @@
+/*  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; 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+/* TODO: enforce default sizes! */
+
+#include <glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "widgetcore.h"
+
+#include "equalizer.h"
+#include "main.h"
+#include "ui_playlist.h"
+#include "skinwin.h"
+#include "util.h"
+
+#include "debug.h"
+
+#include "platform/smartinclude.h"
+
+#define EXTENSION_TARGETS 7
+
+static gchar *ext_targets[EXTENSION_TARGETS] = { "bmp", "xpm", "png", "svg", 
+	"gif", "jpg", "jpeg" };
+
+struct _SkinPixmapIdMapping {
+    SkinPixmapId id;
+    const gchar *name;
+    const gchar *alt_name;
+    gint width, height;
+};
+
+struct _SkinMaskInfo {
+    gint width, height;
+    gchar *inistr;
+};
+
+typedef struct _SkinPixmapIdMapping SkinPixmapIdMapping;
+typedef struct _SkinMaskInfo SkinMaskInfo;
+
+
+Skin *bmp_active_skin = NULL;
+
+static gint skin_current_num;
+
+static SkinMaskInfo skin_mask_info[] = {
+    {275, 116, "Normal"},
+    {275, 16,  "WindowShade"},
+    {275, 116, "Equalizer"},
+    {275, 16,  "EqualizerWS"}
+};
+
+static SkinPixmapIdMapping skin_pixmap_id_map[] = {
+    {SKIN_MAIN, "main", NULL, 0, 0},
+    {SKIN_CBUTTONS, "cbuttons", NULL, 0, 0},
+    {SKIN_SHUFREP, "shufrep", NULL, 0, 0},
+    {SKIN_TEXT, "text", NULL, 0, 0},
+    {SKIN_TITLEBAR, "titlebar", NULL, 0, 0},
+    {SKIN_VOLUME, "volume", NULL, 0, 0},
+    {SKIN_BALANCE, "balance", "volume", 0, 0},
+    {SKIN_MONOSTEREO, "monoster", NULL, 0, 0},
+    {SKIN_PLAYPAUSE, "playpaus", NULL, 0, 0},
+    {SKIN_NUMBERS, "nums_ex", "numbers", 0, 0},
+    {SKIN_POSBAR, "posbar", NULL, 0, 0},
+    {SKIN_EQMAIN, "eqmain", NULL, 0, 0},
+    {SKIN_PLEDIT, "pledit", NULL, 0, 0},
+    {SKIN_EQ_EX, "eq_ex", NULL, 0, 0}
+};
+
+static guint skin_pixmap_id_map_size = G_N_ELEMENTS(skin_pixmap_id_map);
+
+static const guchar skin_default_viscolor[24][3] = {
+    {9, 34, 53},
+    {10, 18, 26},
+    {0, 54, 108},
+    {0, 58, 116},
+    {0, 62, 124},
+    {0, 66, 132},
+    {0, 70, 140},
+    {0, 74, 148},
+    {0, 78, 156},
+    {0, 82, 164},
+    {0, 86, 172},
+    {0, 92, 184},
+    {0, 98, 196},
+    {0, 104, 208},
+    {0, 110, 220},
+    {0, 116, 232},
+    {0, 122, 244},
+    {0, 128, 255},
+    {0, 128, 255},
+    {0, 104, 208},
+    {0, 80, 160},
+    {0, 56, 112},
+    {0, 32, 64},
+    {200, 200, 200}
+};
+
+static GdkBitmap *
+skin_create_transparent_mask(const gchar *,
+                             const gchar *,
+                             const gchar *,
+                             GdkWindow *,
+                             gint, gint);
+
+static void
+skin_setup_masks(Skin * skin);
+
+static void
+skin_set_default_vis_color(Skin * skin);
+
+
+void
+skin_lock(Skin * skin)
+{
+    g_mutex_lock(skin->lock);
+}
+
+void
+skin_unlock(Skin * skin)
+{
+    g_mutex_unlock(skin->lock);
+}
+
+gboolean
+bmp_active_skin_reload(void) 
+{
+    return bmp_active_skin_load(bmp_active_skin->path);	
+}
+
+gboolean
+bmp_active_skin_load(const gchar * path)
+{
+    g_return_val_if_fail(bmp_active_skin != NULL, FALSE);
+
+    memset(&bmp_active_skin->properties, 0, sizeof(SkinProperties));
+
+    if (!skin_load(bmp_active_skin, path))
+        return FALSE;
+
+    skin_setup_masks(bmp_active_skin);
+
+    if (cfg.playlist_transparent)
+    {
+        if (rootpix != NULL)
+            g_object_unref(rootpix);
+
+        rootpix = shade_pixmap(get_transparency_pixmap(), 0, 0, 0, 0, gdk_screen_width(), gdk_screen_height(),
+                               skin_get_color(bmp_active_skin, SKIN_PLEDIT_NORMALBG));
+    }
+
+    draw_main_window(TRUE);
+    draw_playlist_window(TRUE);
+    draw_equalizer_window(TRUE);
+
+    vis_set_window(mainwin_vis, mainwin->window);
+    playlistwin_update_list();
+
+    return TRUE;
+}
+
+void
+skin_pixmap_free(SkinPixmap * p)
+{
+    g_return_if_fail(p != NULL);
+    g_return_if_fail(p->pixmap != NULL);
+
+    g_object_unref(p->pixmap);
+    p->pixmap = NULL;
+}
+
+Skin *
+skin_new(void)
+{
+    Skin *skin;
+    skin = g_new0(Skin, 1);
+    skin->lock = g_mutex_new();
+    return skin;
+}
+
+void
+skin_free(Skin * skin)
+{
+    gint i;
+
+    g_return_if_fail(skin != NULL);
+
+    skin_lock(skin);
+
+    for (i = 0; i < SKIN_PIXMAP_COUNT; i++)
+        skin_pixmap_free(&skin->pixmaps[i]);
+
+    for (i = 0; i < SKIN_PIXMAP_COUNT; i++) {
+        if (skin->masks[i])
+            g_object_unref(skin->masks[i]);
+
+        skin->masks[i] = NULL;
+    }
+
+    skin_set_default_vis_color(skin);
+    skin_unlock(skin);
+}
+
+void
+skin_destroy(Skin * skin)
+{
+    g_return_if_fail(skin != NULL);
+    skin_free(skin);
+    g_mutex_free(skin->lock);
+    g_free(skin);
+}
+
+const SkinPixmapIdMapping *
+skin_pixmap_id_lookup(guint id)
+{
+    guint i;
+
+    for (i = 0; i < skin_pixmap_id_map_size; i++) {
+        if (id == skin_pixmap_id_map[i].id) {
+            return &skin_pixmap_id_map[i];
+        }
+    }
+
+    return NULL;
+}
+
+const gchar *
+skin_pixmap_id_to_name(SkinPixmapId id)
+{
+    guint i;
+
+    for (i = 0; i < skin_pixmap_id_map_size; i++) {
+        if (id == skin_pixmap_id_map[i].id)
+            return skin_pixmap_id_map[i].name;
+    }
+    return NULL;
+}
+
+static void
+skin_set_default_vis_color(Skin * skin)
+{
+    memcpy(skin->vis_color, skin_default_viscolor,
+           sizeof(skin_default_viscolor));
+}
+
+/*
+ * I have rewritten this to take an array of possible targets,
+ * once we find a matching target we now return, instead of loop
+ * recursively. This allows for us to support many possible format
+ * targets for our skinning engine than just the original winamp 
+ * formats.
+ *
+ *    -- nenolod, 16 January 2006
+ */
+gchar *
+skin_pixmap_locate(const gchar * dirname, gchar ** basenames)
+{
+    gchar *filename;
+    gint i;
+
+    for (i = 0; basenames[i]; i++)
+	if (!(filename = find_file_recursively(dirname, basenames[i]))) 
+            g_free(filename);
+        else
+            return filename;
+
+    /* can't find any targets -- sorry */
+    return NULL;
+}
+
+/* FIXME: this function is temporary. It will be removed when the skinning system
+   uses GdkPixbuf in place of GdkPixmap */
+
+static GdkPixmap *
+pixmap_new_from_file(const gchar * filename)
+{
+    GdkPixbuf *pixbuf;
+    GdkPixmap *pixmap;
+    gint width, height;
+
+    if (!(pixbuf = gdk_pixbuf_new_from_file(filename, NULL)))
+        return NULL;
+
+    width = gdk_pixbuf_get_width(pixbuf);
+    height = gdk_pixbuf_get_height(pixbuf);
+
+    if (!(pixmap = gdk_pixmap_new(mainwin->window, width, height,
+                                  gdk_rgb_get_visual()->depth))) {
+        g_object_unref(pixbuf);
+        return NULL;
+    }
+
+    gdk_pixbuf_render_to_drawable(pixbuf, pixmap, mainwin_gc, 0, 0, 0, 0,
+                                  width, height, GDK_RGB_DITHER_MAX, 0, 0);
+    g_object_unref(pixbuf);
+
+    return pixmap;
+}
+
+static gboolean
+skin_load_pixmap_id(Skin * skin, SkinPixmapId id, const gchar * path_p)
+{
+    const gchar *path;
+    gchar *filename;
+    gint width, height;
+    const SkinPixmapIdMapping *pixmap_id_mapping;
+    GdkPixmap *gpm;
+    SkinPixmap *pm = NULL;
+    gchar *basenames[EXTENSION_TARGETS * 2 + 1]; /* alternate basenames */
+    gint i, y;
+
+    g_return_val_if_fail(skin != NULL, FALSE);
+    g_return_val_if_fail(id < SKIN_PIXMAP_COUNT, FALSE);
+
+    pixmap_id_mapping = skin_pixmap_id_lookup(id);
+    g_return_val_if_fail(pixmap_id_mapping != NULL, FALSE);
+
+    memset(&basenames, 0, sizeof(basenames));
+
+    for (i = 0, y = 0; i < EXTENSION_TARGETS; i++, y++)
+    {
+        basenames[y] = g_strdup_printf("%s.%s", pixmap_id_mapping->name,
+			ext_targets[i]);
+
+        if (pixmap_id_mapping->alt_name)
+            basenames[++y] = g_strdup_printf("%s.%s", 
+			pixmap_id_mapping->alt_name, ext_targets[i]);
+    }
+
+    path = path_p ? path_p : skin->path;
+    filename = skin_pixmap_locate(path, basenames);
+
+    for (i = 0; basenames[i] != NULL; i++)
+    {
+         g_free(basenames[i]);
+         basenames[i] = NULL;
+    }
+
+    if (!(gpm = pixmap_new_from_file(filename))) {
+        g_warning("loading of %s failed", filename);
+        g_free(filename);
+        return FALSE;
+    }
+
+    g_free(filename);
+
+    gdk_drawable_get_size(GDK_DRAWABLE(gpm), &width, &height);
+    pm = &skin->pixmaps[id];
+    pm->pixmap = gpm;
+    pm->width = width;
+    pm->height = height;
+    pm->current_width = width;
+    pm->current_height = height;
+
+    return TRUE;
+}
+
+void
+skin_mask_create(Skin * skin,
+                 const gchar * path,
+                 gint id,
+                 GdkWindow * window)
+{
+    skin->masks[id] =
+        skin_create_transparent_mask(path, "region.txt",
+                                     skin_mask_info[id].inistr, window,
+                                     skin_mask_info[id].width,
+                                     skin_mask_info[id].height);
+}
+
+static void
+skin_setup_masks(Skin * skin)
+{
+    GdkBitmap *mask;
+
+    if (cfg.show_wm_decorations)
+        return;
+
+    if (cfg.player_visible) {
+        mask = skin_get_mask(skin, SKIN_MASK_MAIN + cfg.player_shaded);
+        gtk_widget_shape_combine_mask(mainwin, mask, 0, 0);
+    }
+
+    mask = skin_get_mask(skin, SKIN_MASK_EQ + cfg.equalizer_shaded);
+    gtk_widget_shape_combine_mask(equalizerwin, mask, 0, 0);
+}
+
+static GdkBitmap *
+create_default_mask(GdkWindow * parent, gint w, gint h)
+{
+    GdkBitmap *ret;
+    GdkGC *gc;
+    GdkColor pattern;
+
+    ret = gdk_pixmap_new(parent, w, h, 1);
+    gc = gdk_gc_new(ret);
+    pattern.pixel = 1;
+    gdk_gc_set_foreground(gc, &pattern);
+    gdk_draw_rectangle(ret, gc, TRUE, 0, 0, w, h);
+    gdk_gc_destroy(gc);
+
+    return ret;
+}
+
+static void
+skin_query_color(GdkColormap * cm, GdkColor * c)
+{
+#ifdef GDK_WINDOWING_X11
+    XColor xc = { 0,0,0,0,0,0 };
+
+    xc.pixel = c->pixel;
+    XQueryColor(GDK_COLORMAP_XDISPLAY(cm), GDK_COLORMAP_XCOLORMAP(cm), &xc);
+    c->red = xc.red;
+    c->green = xc.green;
+    c->blue = xc.blue;
+#else
+    /* do nothing. see what breaks? */
+#endif
+}
+
+static glong
+skin_calc_luminance(GdkColor * c)
+{
+    return (0.212671 * c->red + 0.715160 * c->green + 0.072169 * c->blue);
+}
+
+static void
+skin_get_textcolors(GdkPixmap * text, GdkColor * bgc, GdkColor * fgc)
+{
+    /*
+     * Try to extract reasonable background and foreground colors
+     * from the font pixmap
+     */
+
+    GdkImage *gi;
+    GdkColormap *cm;
+    gint i;
+
+    g_return_if_fail(text != NULL);
+
+    /* Get the first line of text */
+    gi = gdk_drawable_get_image(text, 0, 0, 152, 6);
+    cm = gdk_window_get_colormap(playlistwin->window);
+    g_return_if_fail(GDK_IS_WINDOW(playlistwin->window));
+
+    for (i = 0; i < 6; i++) {
+        GdkColor c;
+        gint x;
+        glong d, max_d;
+
+        /* Get a pixel from the middle of the space character */
+        bgc[i].pixel = gdk_image_get_pixel(gi, 151, i);
+        skin_query_color(cm, &bgc[i]);
+
+        max_d = 0;
+        for (x = 1; x < 150; x++) {
+            c.pixel = gdk_image_get_pixel(gi, x, i);
+            skin_query_color(cm, &c);
+
+            d = labs(skin_calc_luminance(&c) - skin_calc_luminance(&bgc[i]));
+            if (d > max_d) {
+                memcpy(&fgc[i], &c, sizeof(GdkColor));
+                max_d = d;
+            }
+        }
+    }
+    gdk_image_destroy(gi);
+}
+
+gboolean
+init_skins(const gchar * path)
+{
+    bmp_active_skin = skin_new();
+
+    if (!bmp_active_skin_load(path)) {
+        /* FIXME: Oddly, g_message() causes a crash if path is NULL on
+         * Solaris (see bug #165) */
+        if (path) 
+            g_message("Unable to load skin (%s), trying default...", path);
+
+        /* can't load configured skin, retry with default */
+        if (!bmp_active_skin_load(BMP_DEFAULT_SKIN_PATH)) {
+            g_message("Unable to load default skin (%s)! Giving up.",
+                      BMP_DEFAULT_SKIN_PATH);
+            return FALSE;
+        }
+    }
+
+    if (cfg.random_skin_on_play)
+        skinlist_update();
+
+    return TRUE;
+}
+
+/*
+ * Opens and parses a skin's hints file.
+ * Hints files are somewhat like "scripts" in Winamp3/5.
+ * We'll probably add scripts to it next.
+ */
+void
+skin_parse_hints(Skin * skin, gchar *path_p)
+{
+    gchar *filename, *tmp;
+
+    path_p = path_p ? path_p : skin->path;
+
+    filename = find_file_recursively(path_p, "skin.hints");
+
+    if (filename == NULL)
+        return;
+
+#if 0
+    skin->description = read_ini_string(filename, "skin", "skinDescription");
+#endif
+
+    tmp = read_ini_string(filename, "skin", "mainwinOthertext");
+
+    if (tmp != NULL)
+        skin->properties.mainwin_othertext = atoi(tmp);
+}
+
+static guint
+hex_chars_to_int(gchar hi, gchar lo)
+{
+    /*
+     * Converts a value in the range 0x00-0xFF
+     * to a integer in the range 0-65535
+     */
+    gchar str[3];
+
+    str[0] = hi;
+    str[1] = lo;
+    str[2] = 0;
+
+    return (CLAMP(strtol(str, NULL, 16), 0, 0xFF) << 8);
+}
+
+GdkColor *
+skin_load_color(const gchar * path, const gchar * file,
+                const gchar * section, const gchar * key,
+                gchar * default_hex)
+{
+    gchar *filename, *value;
+    GdkColor *color = NULL;
+
+    filename = find_file_recursively(path, file);
+    if (filename || default_hex) {
+        if (filename) {
+            value = read_ini_string(filename, section, key);
+            if (value == NULL) {
+                value = g_strdup(default_hex);
+            }
+        } else {
+            value = g_strdup(default_hex);
+        }
+        if (value) {
+            gchar *ptr = value;
+            gint len;
+
+            color = g_new0(GdkColor, 1);
+            g_strstrip(value);
+
+            if (value[0] == '#')
+                ptr++;
+            len = strlen(ptr);
+            /*
+             * The handling of incomplete values is done this way
+             * to maximize winamp compatibility
+             */
+            if (len >= 6) {
+                color->red = hex_chars_to_int(*ptr, *(ptr + 1));
+                ptr += 2;
+            }
+            if (len >= 4) {
+                color->green = hex_chars_to_int(*ptr, *(ptr + 1));
+                ptr += 2;
+            }
+            if (len >= 2)
+                color->blue = hex_chars_to_int(*ptr, *(ptr + 1));
+
+            gdk_color_alloc(gdk_window_get_colormap(playlistwin->window),
+                            color);
+            g_free(value);
+        }
+        if (filename)
+            g_free(filename);
+    }
+    return color;
+}
+
+
+
+GdkBitmap *
+skin_create_transparent_mask(const gchar * path,
+                             const gchar * file,
+                             const gchar * section,
+                             GdkWindow * window,
+                             gint width,
+                             gint height)
+{
+    GdkBitmap *mask = NULL;
+    GdkGC *gc = NULL;
+    GdkColor pattern;
+    GdkPoint *gpoints;
+
+    gchar *filename = NULL;
+    gboolean created_mask = FALSE;
+    GArray *num, *point;
+    guint i, j;
+    gint k;
+
+    if (path)
+        filename = find_file_recursively(path, file);
+
+    /* filename will be null if path wasn't set */
+    if (!filename) {
+        return create_default_mask(window, width, height);
+    }
+
+    if ((num = read_ini_array(filename, section, "NumPoints")) == NULL) {
+        g_free(filename);
+        return NULL;
+    }
+
+    if ((point = read_ini_array(filename, section, "PointList")) == NULL) {
+        g_array_free(num, TRUE);
+        g_free(filename);
+        return NULL;
+    }
+
+    mask = gdk_pixmap_new(window, width, height, 1);
+    gc = gdk_gc_new(mask);
+
+    pattern.pixel = 0;
+    gdk_gc_set_foreground(gc, &pattern);
+    gdk_draw_rectangle(mask, gc, TRUE, 0, 0, width, height);
+    pattern.pixel = 1;
+    gdk_gc_set_foreground(gc, &pattern);
+
+    j = 0;
+    for (i = 0; i < num->len; i++) {
+        if ((int)(point->len - j) >= (g_array_index(num, gint, i) * 2)) {
+            created_mask = TRUE;
+            gpoints = g_new(GdkPoint, g_array_index(num, gint, i));
+            for (k = 0; k < g_array_index(num, gint, i); k++) {
+                gpoints[k].x = g_array_index(point, gint, j + k * 2);
+                gpoints[k].y = g_array_index(point, gint, j + k * 2 + 1);
+            }
+            j += k * 2;
+            gdk_draw_polygon(mask, gc, TRUE, gpoints,
+                             g_array_index(num, gint, i));
+            g_free(gpoints);
+        }
+    }
+    g_array_free(num, TRUE);
+    g_array_free(point, TRUE);
+    g_free(filename);
+
+    if (!created_mask)
+        gdk_draw_rectangle(mask, gc, TRUE, 0, 0, width, height);
+
+    gdk_gc_destroy(gc);
+
+    return mask;
+}
+
+void
+skin_load_viscolor(Skin * skin, const gchar * path, const gchar * basename)
+{
+    FILE *file;
+    gint i, c;
+    gchar line[256], *filename;
+    GArray *a;
+
+    g_return_if_fail(skin != NULL);
+    g_return_if_fail(path != NULL);
+    g_return_if_fail(basename != NULL);
+
+    skin_set_default_vis_color(skin);
+
+    filename = find_file_recursively(path, basename);
+    if (!filename)
+        return;
+
+    if (!(file = fopen(filename, "r"))) {
+        g_free(filename);
+        return;
+    }
+
+    g_free(filename);
+
+    for (i = 0; i < 24; i++) {
+        if (fgets(line, 255, file)) {
+            a = string_to_garray(line);
+            if (a->len > 2) {
+                for (c = 0; c < 3; c++)
+                    skin->vis_color[i][c] = g_array_index(a, gint, c);
+            }
+            g_array_free(a, TRUE);
+        }
+        else
+            break;
+    }
+
+    fclose(file);
+}
+
+#if 0
+static void
+skin_numbers_generate_dash(Skin * skin)
+{
+    GdkGC *gc;
+    GdkPixmap *pixmap;
+    SkinPixmap *numbers;
+
+    g_return_if_fail(skin != NULL);
+
+    numbers = &skin->pixmaps[SKIN_NUMBERS];
+    if (!numbers->pixmap || numbers->current_width < 99)
+        return;
+
+    gc = gdk_gc_new(numbers->pixmap);
+    pixmap = gdk_pixmap_new(mainwin->window, 108,
+                            numbers->current_height,
+                            -1);
+
+    skin_draw_pixmap(skin, pixmap, gc, SKIN_NUMBERS, 0, 0, 0, 0, 99, 13);
+    skin_draw_pixmap(skin, pixmap, gc, SKIN_NUMBERS, 90, 0, 99, 0, 9, 13);
+    skin_draw_pixmap(skin, pixmap, gc, SKIN_NUMBERS, 20, 6, 101, 6, 5, 1);
+
+    g_object_unref(numbers->pixmap);
+    g_object_unref(gc);
+
+    numbers->pixmap = pixmap;
+    numbers->current_width = 108;
+}
+#endif
+
+static void
+skin_load_cursor(Skin * skin, const gchar * dirname)
+{
+    const gchar * basename = "normal.cur";
+    gchar * filename = NULL;
+    GdkPixbuf * cursor_pixbuf = NULL;
+    GdkPixbufAnimation * cursor_animated = NULL;
+    GdkCursor * cursor_gdk = NULL;
+    GError * error = NULL;
+ 
+    filename = find_file_recursively(dirname, basename);
+
+    if (filename && cfg.custom_cursors)	{
+    	cursor_animated = gdk_pixbuf_animation_new_from_file(filename, &error);
+        cursor_pixbuf = gdk_pixbuf_animation_get_static_image(cursor_animated);
+        cursor_gdk = gdk_cursor_new_from_pixbuf(gdk_display_get_default(),
+                                                cursor_pixbuf, 0, 0);
+    } else {
+        cursor_gdk = gdk_cursor_new(GDK_LEFT_PTR);
+    }
+
+    gdk_window_set_cursor(mainwin->window, cursor_gdk);
+    gdk_window_set_cursor(playlistwin->window, cursor_gdk);
+    gdk_window_set_cursor(equalizerwin->window, cursor_gdk);
+    gdk_cursor_unref(cursor_gdk);
+}
+
+static void
+skin_load_pixmaps(Skin * skin, const gchar * path)
+{
+    GdkPixmap *text_pm;
+    guint i;
+
+    for (i = 0; i < SKIN_PIXMAP_COUNT; i++)
+        skin_load_pixmap_id(skin, i, path);
+
+    text_pm = skin->pixmaps[SKIN_TEXT].pixmap;
+
+    if (text_pm)
+        skin_get_textcolors(text_pm, skin->textbg, skin->textfg);
+
+#if 0
+    if (skin->pixmaps[SKIN_NUMBERS].pixmap)
+        skin_numbers_generate_dash(skin);
+#endif
+
+    skin->colors[SKIN_PLEDIT_NORMAL] =
+        skin_load_color(path, "pledit.txt", "text", "normal", "#2499ff");
+    skin->colors[SKIN_PLEDIT_CURRENT] =
+        skin_load_color(path, "pledit.txt", "text", "current", "#ffeeff");
+    skin->colors[SKIN_PLEDIT_NORMALBG] =
+        skin_load_color(path, "pledit.txt", "text", "normalbg", "#0a120a");
+    skin->colors[SKIN_PLEDIT_SELECTEDBG] =
+        skin_load_color(path, "pledit.txt", "text", "selectedbg", "#0a124a");
+
+    skin_mask_create(skin, path, SKIN_MASK_MAIN, mainwin->window);
+    skin_mask_create(skin, path, SKIN_MASK_MAIN_SHADE, mainwin->window);
+
+    skin_mask_create(skin, path, SKIN_MASK_EQ, equalizerwin->window);
+    skin_mask_create(skin, path, SKIN_MASK_EQ_SHADE, equalizerwin->window);
+
+    skin_load_viscolor(skin, path, "viscolor.txt");
+}
+
+static gboolean
+skin_load_nolock(Skin * skin, const gchar * path, gboolean force)
+{
+    gchar *cpath;
+
+    g_return_val_if_fail(skin != NULL, FALSE);
+    g_return_val_if_fail(path != NULL, FALSE);
+    REQUIRE_LOCK(skin->lock);
+
+    if (!g_file_test(path, G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_DIR))
+	return FALSE;
+   
+    if (!force) {
+        if (skin->path)
+            if (!strcmp(skin->path, path))
+                return FALSE;
+    }
+      
+    skin_current_num++;
+
+    skin->path = g_strdup(path);
+
+    if (!file_is_archive(path)) {
+        skin_load_pixmaps(skin, path);
+        skin_load_cursor(skin, path);
+
+        /* Parse the hints for this skin. */
+        skin_parse_hints(skin, NULL);
+
+        return TRUE;
+    }
+
+    if (!(cpath = archive_decompress(path))) {
+        g_message("Unable to extract skin archive (%s)", path);
+        return FALSE;
+    }
+
+    skin_load_pixmaps(skin, cpath);
+    skin_load_cursor(skin, cpath);
+
+    /* Parse the hints for this skin. */
+    skin_parse_hints(skin, cpath);
+
+    del_directory(cpath);
+    g_free(cpath);
+
+    return TRUE;
+}
+
+void
+skin_install_skin(const gchar * path)
+{
+    gchar *command;
+
+    g_return_if_fail(path != NULL);
+
+    command = g_strdup_printf("cp %s %s", path, bmp_paths[BMP_PATH_USER_SKIN_DIR]);
+    if (system(command)) {
+        g_message("Unable to install skin (%s) into user directory (%s)",
+                  path, bmp_paths[BMP_PATH_USER_SKIN_DIR]);
+    }
+    g_free(command);
+}
+
+
+gboolean
+skin_load(Skin * skin, const gchar * path)
+{
+    gboolean error;
+
+    g_return_val_if_fail(skin != NULL, FALSE);
+
+    if (!path)
+        return FALSE;
+
+    skin_lock(skin);
+    error = skin_load_nolock(skin, path, FALSE);
+    skin_unlock(skin);
+    
+    return error;
+}
+
+gboolean
+skin_reload_forced(void) 
+{
+   gboolean error;
+
+   skin_lock(bmp_active_skin);
+   error = skin_load_nolock(bmp_active_skin, bmp_active_skin->path, TRUE);
+   skin_unlock(bmp_active_skin);
+
+   return error;
+}
+
+void
+skin_reload(Skin * skin)
+{
+    g_return_if_fail(skin != NULL);
+    skin_load_nolock(skin, skin->path, TRUE);
+}
+
+
+static SkinPixmap *
+skin_get_pixmap(Skin * skin, SkinPixmapId map_id)
+{
+    g_return_val_if_fail(skin != NULL, NULL);
+    g_return_val_if_fail(map_id < SKIN_PIXMAP_COUNT, NULL);
+
+    return &skin->pixmaps[map_id];
+}
+
+GdkBitmap *
+skin_get_mask(Skin * skin, SkinMaskId mi)
+{
+    g_return_val_if_fail(skin != NULL, NULL);
+    g_return_val_if_fail(mi < SKIN_PIXMAP_COUNT, NULL);
+
+    return skin->masks[mi];
+}
+
+GdkColor *
+skin_get_color(Skin * skin, SkinColorId color_id)
+{
+    GdkColor *ret = NULL;
+
+    g_return_val_if_fail(skin != NULL, NULL);
+
+    switch (color_id) {
+    case SKIN_TEXTBG:
+        if (skin->pixmaps[SKIN_TEXT].pixmap)
+            ret = skin->textbg;
+        else
+            ret = skin->def_textbg;
+        break;
+    case SKIN_TEXTFG:
+        if (skin->pixmaps[SKIN_TEXT].pixmap)
+            ret = skin->textfg;
+        else
+            ret = skin->def_textfg;
+        break;
+    default:
+        if (color_id < SKIN_COLOR_COUNT)
+            ret = skin->colors[color_id];
+        break;
+    }
+    return ret;
+}
+
+void
+skin_get_viscolor(Skin * skin, guchar vis_color[24][3])
+{
+    gint i;
+
+    g_return_if_fail(skin != NULL);
+
+    for (i = 0; i < 24; i++) {
+        vis_color[i][0] = skin->vis_color[i][0];
+        vis_color[i][1] = skin->vis_color[i][1];
+        vis_color[i][2] = skin->vis_color[i][2];
+    }
+}
+
+gint
+skin_get_id(void)
+{
+    return skin_current_num;
+}
+
+void
+skin_draw_pixmap(Skin * skin, GdkDrawable * drawable, GdkGC * gc,
+                 SkinPixmapId pixmap_id,
+                 gint xsrc, gint ysrc, gint xdest, gint ydest,
+                 gint width, gint height)
+{
+    SkinPixmap *pixmap;
+
+    g_return_if_fail(skin != NULL);
+
+    pixmap = skin_get_pixmap(skin, pixmap_id);
+    g_return_if_fail(pixmap != NULL);
+    g_return_if_fail(pixmap->pixmap != NULL);
+
+    if (xsrc > pixmap->width || ysrc > pixmap->height)
+        return;
+
+    width = MIN(width, pixmap->width - xsrc);
+    height = MIN(height, pixmap->height - ysrc);
+    gdk_draw_pixmap(drawable, gc, pixmap->pixmap, xsrc, ysrc,
+                    xdest, ydest, width, height);
+}
+
+void
+skin_get_eq_spline_colors(Skin * skin, guint32 colors[19])
+{
+    gint i;
+    GdkPixmap *pixmap;
+    GdkImage *img;
+    SkinPixmap *eqmainpm;
+
+    g_return_if_fail(skin != NULL);
+
+    eqmainpm = &skin->pixmaps[SKIN_EQMAIN];
+    if (eqmainpm->pixmap &&
+        eqmainpm->current_width >= 116 && eqmainpm->current_height >= 313)
+        pixmap = eqmainpm->pixmap;
+    else
+        return;
+
+    if (!GDK_IS_DRAWABLE(pixmap))
+        return;
+
+    if (!(img = gdk_drawable_get_image(pixmap, 115, 294, 1, 19)))
+        return;
+
+    for (i = 0; i < 19; i++)
+        colors[i] = gdk_image_get_pixel(img, 0, i);
+
+    gdk_image_destroy(img);
+}
+
+
+static void
+skin_draw_playlistwin_frame_top(Skin * skin,
+                                GdkDrawable * drawable,
+                                GdkGC * gc,
+                                gint width, gint height, gboolean focus)
+{
+    /* The title bar skin consists of 2 sets of 4 images, 1 set
+     * for focused state and the other for unfocused. The 4 images
+     * are: 
+     *
+     * a. right corner (25,20)
+     * b. left corner  (25,20)
+     * c. tiler        (25,20)
+     * d. title        (100,20)
+     * 
+     * min allowed width = 100+25+25 = 150
+     */
+
+    gint i, y, c;
+
+    /* get y offset of the pixmap set to use */
+    if (focus)
+        y = 0;
+    else
+        y = 21;
+
+    /* left corner */
+    skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 0, y, 0, 0, 25, 20);
+
+    /* titlebar title */
+    skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 26, y,
+                     (width - 100) / 2, 0, 100, 20);
+
+    /* titlebar right corner  */
+    skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 153, y,
+                     width - 25, 0, 25, 20);
+
+    /* tile draw the remaining frame */
+
+    /* compute tile count */
+    c = (width - (100 + 25 + 25)) / 25;
+
+    for (i = 0; i < c / 2; i++) {
+        /* left of title */
+        skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 127, y,
+                         25 + i * 25, 0, 25, 20);
+
+        /* right of title */
+        skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 127, y,
+                         (width + 100) / 2 + i * 25, 0, 25, 20);
+    }
+
+    if (c & 1) {
+        /* Odd tile count, so one remaining to draw. Here we split
+         * it into two and draw half on either side of the title */
+        skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 127, y,
+                         ((c / 2) * 25) + 25, 0, 12, 20);
+        skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 127, y,
+                         (width / 2) + ((c / 2) * 25) + 50, 0, 13, 20);
+    }
+}
+
+static void
+skin_draw_playlistwin_frame_bottom(Skin * skin,
+                                   GdkDrawable * drawable,
+                                   GdkGC * gc,
+                                   gint width, gint height, gboolean focus)
+{
+    /* The bottom frame skin consists of 1 set of 4 images. The 4
+     * images are:
+     *
+     * a. left corner with menu buttons (125,38)
+     * b. visualization window (75,38)
+     * c. right corner with play buttons (150,38)
+     * d. frame tile (25,38)
+     * 
+     * (min allowed width = 125+150+25=300
+     */
+
+    gint i, c;
+
+    /* bottom left corner (menu buttons) */
+    skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 0, 72,
+                     0, height - 38, 125, 38);
+
+    c = (width - 275) / 25;
+
+    /* draw visualization window, if width allows */
+    if (c >= 3) {
+        c -= 3;
+        skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 205, 0,
+                         width - (150 + 75), height - 38, 75, 38);
+    }
+
+    /* Bottom right corner (playbuttons etc) */
+    skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT,
+                     126, 72, width - 150, height - 38, 150, 38);
+
+    /* Tile draw the remaining undrawn portions */
+    for (i = 0; i < c; i++)
+        skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 179, 0,
+                         125 + i * 25, height - 38, 25, 38);
+}
+
+static void
+skin_draw_playlistwin_frame_sides(Skin * skin,
+                                  GdkDrawable * drawable,
+                                  GdkGC * gc,
+                                  gint width, gint height, gboolean focus)
+{
+    /* The side frames consist of 2 tile images. 1 for the left, 1 for
+     * the right. 
+     * a. left  (12,29)
+     * b. right (19,29)
+     */
+
+    gint i;
+
+    /* frame sides */
+    for (i = 0; i < (height - (20 + 38)) / 29; i++) {
+        /* left */
+        skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 0, 42,
+                         0, 20 + i * 29, 12, 29);
+
+        /* right */
+        skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 32, 42,
+                         width - 19, 20 + i * 29, 19, 29);
+    }
+}
+
+
+void
+skin_draw_playlistwin_frame(Skin * skin,
+                            GdkDrawable * drawable, GdkGC * gc,
+                            gint width, gint height, gboolean focus)
+{
+    skin_draw_playlistwin_frame_top(skin, drawable, gc, width, height, focus);
+    skin_draw_playlistwin_frame_bottom(skin, drawable, gc, width, height,
+                                       focus);
+    skin_draw_playlistwin_frame_sides(skin, drawable, gc, width, height,
+                                      focus);
+}
+
+
+void
+skin_draw_playlistwin_shaded(Skin * skin,
+                             GdkDrawable * drawable, GdkGC * gc,
+                             gint width, gboolean focus)
+{
+    /* The shade mode titlebar skin consists of 4 images:
+     * a) left corner               offset (72,42) size (25,14)
+     * b) right corner, focused     offset (99,57) size (50,14)
+     * c) right corner, unfocused   offset (99,42) size (50,14)
+     * d) bar tile                  offset (72,57) size (25,14)
+     */
+
+    gint i;
+
+    /* left corner */
+    skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 72, 42, 0, 0, 25, 14);
+
+    /* bar tile */
+    for (i = 0; i < (width - 75) / 25; i++)
+        skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 72, 57,
+                         (i * 25) + 25, 0, 25, 14);
+
+    /* right corner */
+    skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 99, focus ? 57 : 42,
+                     width - 50, 0, 50, 14);
+}
+
+
+void
+skin_draw_mainwin_titlebar(Skin * skin,
+                           GdkDrawable * drawable, GdkGC * gc,
+                           gboolean shaded, gboolean focus)
+{
+    /* The titlebar skin consists of 2 sets of 2 images, one for for
+     * shaded and the other for unshaded mode, giving a total of 4.
+     * The images are exactly 275x14 pixels, aligned and arranged
+     * vertically on each other in the pixmap in the following order:
+     * 
+     * a) unshaded, focused      offset (27, 0)
+     * b) unshaded, unfocused    offset (27, 15)
+     * c) shaded, focused        offset (27, 29)
+     * d) shaded, unfocused      offset (27, 42)
+     */
+
+    gint y_offset;
+
+    if (shaded) {
+        if (focus)
+            y_offset = 29;
+        else
+            y_offset = 42;
+    }
+    else {
+        if (focus)
+            y_offset = 0;
+        else
+            y_offset = 15;
+    }
+
+    skin_draw_pixmap(skin, drawable, gc, SKIN_TITLEBAR, 27, y_offset,
+                     0, 0, MAINWIN_WIDTH, MAINWIN_TITLEBAR_HEIGHT);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/skin.h	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,149 @@
+/*  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; 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.
+ */
+
+#ifndef _WIDGETCORE_H_
+#error Please do not include me directly! Use widgetcore.h instead!
+#endif
+
+#ifndef SKIN_H
+#define SKIN_H
+
+
+#include <glib.h>
+#include <gdk/gdk.h>
+
+
+#define BMP_DEFAULT_SKIN_PATH \
+  DATA_DIR G_DIR_SEPARATOR_S "Skins" G_DIR_SEPARATOR_S "Default"
+
+
+typedef enum {
+    SKIN_MAIN = 0,
+    SKIN_CBUTTONS,
+    SKIN_TITLEBAR,
+    SKIN_SHUFREP,
+    SKIN_TEXT,
+    SKIN_VOLUME,
+    SKIN_BALANCE,
+    SKIN_MONOSTEREO,
+    SKIN_PLAYPAUSE,
+    SKIN_NUMBERS,
+    SKIN_POSBAR,
+    SKIN_PLEDIT,
+    SKIN_EQMAIN,
+    SKIN_EQ_EX,
+    SKIN_PIXMAP_COUNT
+} SkinPixmapId;
+
+typedef enum {
+    SKIN_MASK_MAIN = 0,
+    SKIN_MASK_MAIN_SHADE,
+    SKIN_MASK_EQ,
+    SKIN_MASK_EQ_SHADE,
+    SKIN_MASK_COUNT
+} SkinMaskId;
+
+typedef enum {
+    SKIN_PLEDIT_NORMAL = 0,
+    SKIN_PLEDIT_CURRENT,
+    SKIN_PLEDIT_NORMALBG,
+    SKIN_PLEDIT_SELECTEDBG,
+    SKIN_TEXTBG,
+    SKIN_TEXTFG,
+    SKIN_COLOR_COUNT
+} SkinColorId;
+
+typedef struct _SkinProperties {
+	gboolean mainwin_othertext;
+} SkinProperties;
+
+#define SKIN_PIXMAP(x)  ((SkinPixmap *)(x))
+typedef struct _SkinPixmap {
+    GdkPixmap *pixmap;
+    /* GdkPixmap *def_pixmap; */
+
+    /* The real size of the pixmap */
+    gint width, height;
+
+    /* The size of the pixmap from the current skin,
+       which might be smaller */
+    gint current_width, current_height;
+} SkinPixmap;
+
+
+#define SKIN(x)  ((Skin *)(x))
+typedef struct _Skin {
+    GMutex *lock;
+    gchar *path;
+    gchar *def_path;
+    SkinPixmap pixmaps[SKIN_PIXMAP_COUNT];
+    GdkColor textbg[6], def_textbg[6];
+    GdkColor textfg[6], def_textfg[6];
+    GdkColor *colors[SKIN_COLOR_COUNT];
+    guchar vis_color[24][3];
+    GdkBitmap *masks[SKIN_MASK_COUNT];
+    SkinProperties properties;
+} Skin;
+
+extern Skin *bmp_active_skin;
+
+gboolean init_skins(const gchar * path);
+void cleanup_skins(void);
+
+gboolean bmp_active_skin_load(const gchar * path);
+gboolean bmp_active_skin_reload(void);
+
+Skin *skin_new(void);
+gboolean skin_load(Skin * skin, const gchar * path);
+void skin_reload(Skin * skin);
+void skin_free(Skin * skin);
+
+GdkBitmap *skin_get_mask(Skin * skin, SkinMaskId mi);
+GdkColor *skin_get_color(Skin * skin, SkinColorId color_id);
+
+void skin_get_viscolor(Skin * skin, guchar vis_color[24][3]);
+gint skin_get_id(void);
+void skin_draw_pixmap(Skin * skin, GdkDrawable * drawable, GdkGC * gc,
+                      SkinPixmapId pixmap_id,
+                      gint xsrc, gint ysrc, gint xdest, gint ydest,
+                      gint width, gint height);
+void skin_get_eq_spline_colors(Skin * skin, guint32 colors[19]);
+void skin_install_skin(const gchar * path);
+
+void skin_draw_playlistwin_shaded(Skin * skin,
+                                  GdkDrawable * drawable, GdkGC * gc,
+                                  gint width, gboolean focus);
+void skin_draw_playlistwin_frame(Skin * skin,
+                                 GdkDrawable * drawable, GdkGC * gc,
+                                 gint width, gint height, gboolean focus);
+
+void skin_draw_mainwin_titlebar(Skin * skin,
+                                GdkDrawable * drawable, GdkGC * gc,
+                                gboolean shaded, gboolean focus);
+
+
+void skin_parse_hints(Skin * skin, gchar *path_p);
+
+
+gboolean
+skin_reload_forced(void);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/svis.c	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,204 @@
+/*  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; 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 "widgetcore.h"
+
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <string.h>
+
+#include "main.h"
+#include "mainwin.h"
+#include "plugin.h"
+#include "widget.h"
+#include "vis.h"
+
+static gint svis_redraw_delays[] = { 1, 2, 4, 8 };
+
+/* FIXME: Are the svis_scope_colors correct? */
+static guint8 svis_scope_colors[] = { 20, 19, 18, 19, 20 };
+static guint8 svis_vu_normal_colors[] = { 17, 17, 17, 12, 12, 12, 2, 2 };
+
+#define DRAW_DS_PIXEL(ptr,value) \
+	*(ptr) = (value); \
+	*((ptr) + 1) = (value); \
+	*((ptr) + 76) = (value); \
+	*((ptr) + 77) = (value);
+
+#define SVIS_HEIGHT 5
+#define SVIS_WIDTH 38
+
+void
+svis_timeout_func(SVis * svis, guchar * data)
+{
+    static GTimer *timer = NULL;
+    gulong micros = 9999999;
+    gboolean falloff = FALSE;
+    gint i;
+
+    if (!timer) {
+        timer = g_timer_new();
+        g_timer_start(timer);
+    }
+    else {
+        g_timer_elapsed(timer, &micros);
+        if (micros > 14000)
+            g_timer_reset(timer);
+
+    }
+
+    if (cfg.vis_type == INPUT_VIS_ANALYZER) {
+        if (micros > 14000)
+            falloff = TRUE;
+
+        for (i = 0; i < 2; i++) {
+            if (falloff || data) {
+                if (data && data[i] > svis->vs_data[i])
+                    svis->vs_data[i] = data[i];
+                else if (falloff) {
+                    if (svis->vs_data[i] >= 2)
+                        svis->vs_data[i] -= 2;
+                    else
+                        svis->vs_data[i] = 0;
+                }
+            }
+
+        }
+    }
+    else if (data) {
+        for (i = 0; i < 75; i++)
+            svis->vs_data[i] = data[i];
+    }
+
+    if (micros > 14000) {
+        if (!svis->vs_refresh_delay) {
+            svis_draw((Widget *) svis);
+            svis->vs_refresh_delay = svis_redraw_delays[cfg.vis_refresh];
+
+        }
+        svis->vs_refresh_delay--;
+    }
+}
+
+void
+svis_draw(Widget * w)
+{
+    SVis *svis = (SVis *) w;
+    gint x, y, h;
+    guchar svis_color[24][3];
+    guchar rgb_data[SVIS_WIDTH * 2 * SVIS_HEIGHT * 2], *ptr, c;
+    guint32 colors[24];
+    GdkRgbCmap *cmap;
+
+    GDK_THREADS_ENTER();
+
+    skin_get_viscolor(bmp_active_skin, svis_color);
+    for (y = 0; y < 24; y++) {
+        colors[y] =
+            svis_color[y][0] << 16 | svis_color[y][1] << 8 | svis_color[y][2];
+    }
+    cmap = gdk_rgb_cmap_new(colors, 24);
+
+    memset(rgb_data, 0, SVIS_WIDTH * SVIS_HEIGHT);
+    if (cfg.vis_type == VIS_ANALYZER) {
+        switch (cfg.vu_mode) {
+        case VU_NORMAL:
+            for (y = 0; y < 2; y++) {
+                ptr = rgb_data + ((y * 3) * 38);
+                h = (svis->vs_data[y] * 7) / 37;
+                for (x = 0; x < h; x++, ptr += 5) {
+                    c = svis_vu_normal_colors[x];
+                    *(ptr) = c;
+                    *(ptr + 1) = c;
+                    *(ptr + 2) = c;
+                    *(ptr + 38) = c;
+                    *(ptr + 39) = c;
+                    *(ptr + 40) = c;
+                }
+            }
+            break;
+        case VU_SMOOTH:
+            for (y = 0; y < 2; y++) {
+                ptr = rgb_data + ((y * 3) * SVIS_WIDTH);
+                for (x = 0; x < svis->vs_data[y]; x++, ptr++) {
+                    c = 17 - ((x * 15) / 37);
+                    *(ptr) = c;
+                    *(ptr + 38) = c;
+                }
+            }
+            break;
+        }
+    }
+    else if (cfg.vis_type == VIS_SCOPE) {
+        for (x = 0; x < 38; x++) {
+            h = svis->vs_data[x << 1] / 3;
+            ptr = rgb_data + ((4 - h) * 38) + x;
+            *ptr = svis_scope_colors[h];
+        }
+    }
+
+    gdk_draw_indexed_image(mainwin->window, mainwin_gc,
+                           svis->vs_widget.x, svis->vs_widget.y,
+                           svis->vs_widget.width,
+                           svis->vs_widget.height,
+                           GDK_RGB_DITHER_NORMAL, (guchar *) rgb_data,
+                           38, cmap);
+
+    gdk_rgb_cmap_free(cmap);
+    GDK_THREADS_LEAVE();
+}
+
+void
+svis_clear_data(SVis * svis)
+{
+    gint i;
+
+    if (!svis)
+        return;
+
+    for (i = 0; i < 75; i++) {
+        svis->vs_data[i] = (cfg.vis_type == VIS_SCOPE) ? 6 : 0;
+    }
+}
+
+void
+svis_clear(SVis * svis)
+{
+    gdk_window_clear_area(mainwin->window, svis->vs_widget.x,
+                          svis->vs_widget.y, svis->vs_widget.width,
+                          svis->vs_widget.height);
+}
+
+SVis *
+create_svis(GList ** wlist,
+            GdkPixmap * parent, 
+            GdkGC * gc,
+            gint x, gint y)
+{
+    SVis *svis;
+
+    svis = g_new0(SVis, 1);
+    widget_init(&svis->vs_widget, parent, gc, x, y, SVIS_WIDTH, SVIS_HEIGHT,
+                1);
+
+    widget_list_add(wlist, WIDGET(svis));
+    return svis;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/svis.h	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,52 @@
+/*  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; 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.
+ */
+
+#ifndef _WIDGETCORE_H_
+#error Please do not include me directly! Use widgetcore.h instead!
+#endif
+
+#ifndef SVIS_H
+#define SVIS_H
+
+#include <glib.h>
+#include <gdk/gdk.h>
+
+#include "svis.h"
+#include "widget.h"
+
+#define SVIS(x)  ((SVis *)(x))
+struct _SVis {
+    Widget vs_widget;
+    gint vs_data[75];
+    gint vs_refresh_delay;
+};
+
+typedef struct _SVis SVis;
+
+void svis_draw(Widget * w);
+void svis_timeout_func(SVis * svis, guchar * data);
+SVis *create_svis(GList ** wlist, GdkPixmap * parent, GdkGC * gc, gint x,
+                  gint y);
+void svis_set_data(SVis * vis, guchar * data);
+void svis_clear_data(SVis * vis);
+void svis_clear(SVis * vis);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/tbutton.c	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,176 @@
+/*  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; 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 "widgetcore.h"
+
+#include <glib.h>
+#include <gdk/gdk.h>
+
+#include "widget.h"
+
+void
+tbutton_draw(Widget * w)
+{
+    TButton *button = TBUTTON(w);
+    GdkPixmap *obj;
+
+    obj = button->tb_widget.parent;
+
+    if (button->tb_pressed && button->tb_inside) {
+        if (button->tb_selected) {
+            skin_draw_pixmap(bmp_active_skin, obj,
+                             button->tb_widget.gc,
+                             button->tb_skin_index,
+                             button->tb_psx, button->tb_psy,
+                             button->tb_widget.x, button->tb_widget.y,
+                             button->tb_widget.width,
+                             button->tb_widget.height);
+        }
+        else {
+            skin_draw_pixmap(bmp_active_skin, obj,
+                             button->tb_widget.gc,
+                             button->tb_skin_index,
+                             button->tb_pux, button->tb_puy,
+                             button->tb_widget.x, button->tb_widget.y,
+                             button->tb_widget.width,
+                             button->tb_widget.height);
+        }
+    }
+    else {
+        if (button->tb_selected) {
+            skin_draw_pixmap(bmp_active_skin, obj,
+                             button->tb_widget.gc,
+                             button->tb_skin_index,
+                             button->tb_nsx, button->tb_nsy,
+                             button->tb_widget.x, button->tb_widget.y,
+                             button->tb_widget.width,
+                             button->tb_widget.height);
+        }
+        else {
+            skin_draw_pixmap(bmp_active_skin, obj,
+                             button->tb_widget.gc,
+                             button->tb_skin_index,
+                             button->tb_nux, button->tb_nuy,
+                             button->tb_widget.x, button->tb_widget.y,
+                             button->tb_widget.width,
+                             button->tb_widget.height);
+
+        }
+    }
+}
+
+void
+tbutton_button_press_cb(GtkWidget * widget, GdkEventButton * event,
+                        TButton * button)
+{
+    if (event->button != 1)
+        return;
+
+    if (widget_contains(&button->tb_widget, event->x, event->y)) {
+        button->tb_pressed = 1;
+        button->tb_inside = 1;
+        widget_draw(WIDGET(button));
+    }
+}
+
+void
+tbutton_button_release_cb(GtkWidget * widget, GdkEventButton * event,
+                          TButton * button)
+{
+    if (event->button != 1)
+        return;
+
+    if (button->tb_inside && button->tb_pressed) {
+        button->tb_inside = 0;
+        button->tb_selected = !button->tb_selected;
+
+        widget_draw(WIDGET(button));
+
+        if (button->tb_push_cb)
+            button->tb_push_cb(button->tb_selected);
+    }
+
+    if (button->tb_pressed)
+        button->tb_pressed = 0;
+}
+
+void
+tbutton_motion_cb(GtkWidget * widget, GdkEventMotion * event,
+                  TButton * button)
+{
+    gint inside;
+
+    if (!button->tb_pressed)
+        return;
+    inside = widget_contains(&button->tb_widget, event->x, event->y);
+    if (inside != button->tb_inside) {
+        button->tb_inside = inside;
+        widget_draw(WIDGET(button));
+    }
+}
+
+TButton *
+create_tbutton(GList ** wlist, GdkPixmap * parent, GdkGC * gc,
+               gint x, gint y, gint w, gint h, gint nux, gint nuy,
+               gint pux, gint puy, gint nsx, gint nsy, gint psx,
+               gint psy, void (*cb) (gboolean), SkinPixmapId si)
+{
+    TButton *b;
+
+    b = g_new0(TButton, 1);
+    widget_init(&b->tb_widget, parent, gc, x, y, w, h, 1);
+    b->tb_widget.button_press_cb =
+        (void (*)(GtkWidget *, GdkEventButton *, gpointer))
+        tbutton_button_press_cb;
+    b->tb_widget.button_release_cb =
+        (void (*)(GtkWidget *, GdkEventButton *, gpointer))
+        tbutton_button_release_cb;
+    b->tb_widget.motion_cb =
+        (void (*)(GtkWidget *, GdkEventMotion *, gpointer))
+        tbutton_motion_cb;
+    b->tb_widget.draw = tbutton_draw;
+    b->tb_nux = nux;
+    b->tb_nuy = nuy;
+    b->tb_pux = pux;
+    b->tb_puy = puy;
+    b->tb_nsx = nsx;
+    b->tb_nsy = nsy;
+    b->tb_psx = psx;
+    b->tb_psy = psy;
+    b->tb_push_cb = cb;
+    b->tb_skin_index = si;
+
+    widget_list_add(wlist, WIDGET(b));
+    return b;
+}
+
+void
+tbutton_set_toggled(TButton * tb, gboolean toggled)
+{
+    tb->tb_selected = toggled;
+    widget_draw(WIDGET(tb));
+}
+
+void
+free_tbutton(TButton * b)
+{
+    g_free(b);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/tbutton.h	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,52 @@
+/*  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; 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.
+ */
+
+#ifndef _WIDGETCORE_H_
+#error Please do not include me directly! Use widgetcore.h instead!
+#endif
+
+#ifndef TBUTTON_H
+#define TBUTTON_H
+
+#include <glib.h>
+
+#include "skin.h"
+#include "widget.h"
+
+#define TBUTTON(x) ((TButton *)(x))
+struct _TButton {
+    Widget tb_widget;
+    gint tb_nux, tb_nuy, tb_pux, tb_puy, tb_nsx, tb_nsy, tb_psx, tb_psy;
+    gint tb_pressed, tb_inside, tb_selected;
+    void (*tb_push_cb) (gboolean);
+    SkinPixmapId tb_skin_index;
+};
+
+typedef struct _TButton TButton;
+
+TButton *create_tbutton(GList ** wlist, GdkPixmap * parent, GdkGC * gc,
+                        gint x, gint y, gint w, gint h, gint nux, gint nuy,
+                        gint pux, gint puy, gint nsx, gint nsy, gint psx,
+                        gint psy, void (*cb) (gboolean), SkinPixmapId si);
+void tbutton_set_toggled(TButton * tb, gboolean toggled);
+void free_tbutton(TButton * b);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/textbox.c	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,579 @@
+/*  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; 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 "widgetcore.h"
+
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkprivate.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "main.h"
+#include "util.h"
+
+static void textbox_generate_pixmap(TextBox * tb);
+
+static void
+textbox_draw(Widget * w)
+{
+    TextBox *tb = TEXT_BOX(w);
+    gint cw;
+    GdkPixmap *obj;
+    GdkPixmap *src;
+
+    g_return_if_fail(tb != NULL);
+
+    if (tb->tb_text &&
+        (!tb->tb_pixmap_text || strcmp(tb->tb_text, tb->tb_pixmap_text)))
+        textbox_generate_pixmap(tb);
+
+    if (tb->tb_pixmap) {
+        if (skin_get_id() != tb->tb_skin_id) {
+            tb->tb_skin_id = skin_get_id();
+            textbox_generate_pixmap(tb);
+        }
+        obj = tb->tb_widget.parent;
+        src = tb->tb_pixmap;
+
+        cw = tb->tb_pixmap_width - tb->tb_offset;
+        if (cw > tb->tb_widget.width)
+            cw = tb->tb_widget.width;
+        gdk_draw_pixmap(obj, tb->tb_widget.gc, src, tb->tb_offset, 0,
+                        tb->tb_widget.x, tb->tb_widget.y, cw,
+                        tb->tb_widget.height);
+        if (cw < tb->tb_widget.width)
+            gdk_draw_pixmap(obj, tb->tb_widget.gc, src, 0, 0,
+                            tb->tb_widget.x + cw, tb->tb_widget.y,
+                            tb->tb_widget.width - cw, tb->tb_widget.height);
+    }
+}
+
+static gboolean
+textbox_scroll(gpointer data)
+{
+    TextBox *tb = TEXT_BOX(data);
+
+    if (!tb->tb_is_dragging) {
+        tb->tb_offset += 1;
+        if (tb->tb_offset >= tb->tb_pixmap_width)
+            tb->tb_offset -= tb->tb_pixmap_width;
+        widget_draw(WIDGET(tb));
+    }
+
+    return TRUE;
+}
+
+static void
+textbox_button_press(GtkWidget * w, GdkEventButton * event, gpointer data)
+{
+    TextBox *tb = TEXT_BOX(data);
+
+    if (event->button != 1)
+        return;
+    if (widget_contains(&tb->tb_widget, event->x, event->y) &&
+        tb->tb_scroll_allowed &&
+        tb->tb_pixmap_width > tb->tb_widget.width && tb->tb_is_scrollable) {
+        tb->tb_is_dragging = TRUE;
+        tb->tb_drag_off = tb->tb_offset;
+        tb->tb_drag_x = event->x;
+    }
+}
+
+static void
+textbox_motion(GtkWidget * w, GdkEventMotion * event, gpointer data)
+{
+    TextBox *tb = TEXT_BOX(data);
+
+    if (tb->tb_is_dragging) {
+        if (tb->tb_scroll_allowed &&
+            tb->tb_pixmap_width > tb->tb_widget.width) {
+            tb->tb_offset = tb->tb_drag_off - (event->x - tb->tb_drag_x);
+
+            while (tb->tb_offset < 0)
+                tb->tb_offset += tb->tb_pixmap_width;
+
+            while (tb->tb_offset > tb->tb_pixmap_width)
+                tb->tb_offset -= tb->tb_pixmap_width;
+
+            widget_draw(WIDGET(tb));
+        }
+    }
+}
+
+static void
+textbox_button_release(GtkWidget * w, GdkEventButton * event, gpointer data)
+{
+    TextBox *tb = TEXT_BOX(data);
+
+    if (event->button == 1)
+        tb->tb_is_dragging = FALSE;
+}
+
+static gboolean
+textbox_should_scroll(TextBox * tb)
+{
+    g_return_val_if_fail(tb != NULL, FALSE);
+
+    if (!tb->tb_scroll_allowed)
+        return FALSE;
+
+    if (tb->tb_font) {
+        gint width;
+
+        text_get_extents(tb->tb_fontname, tb->tb_text, &width, NULL, NULL,
+                         NULL);
+
+        if (width <= tb->tb_widget.width)
+            return FALSE;
+        else
+            return TRUE;
+    }
+
+    if (g_utf8_strlen(tb->tb_text, -1) * 5 > tb->tb_widget.width)
+        return TRUE;
+
+    return FALSE;
+}
+
+void
+textbox_set_text(TextBox * tb, const gchar * text)
+{
+    g_return_if_fail(tb != NULL);
+    g_return_if_fail(text != NULL);
+
+    widget_lock(WIDGET(tb));
+
+    if (tb->tb_text) {
+        if (!strcmp(text, tb->tb_text)) {
+            widget_unlock(WIDGET(tb));
+            return;
+        }
+        g_free(tb->tb_text);
+    }
+
+    tb->tb_text = str_to_utf8(text);
+
+    widget_unlock(WIDGET(tb));
+    widget_draw(WIDGET(tb));
+}
+
+static void
+textbox_generate_xfont_pixmap(TextBox * tb, const gchar * pixmaptext)
+{
+    gint length, i;
+    GdkGC *gc, *maskgc;
+    GdkColor *c, pattern;
+    GdkBitmap *mask;
+    PangoLayout *layout;
+    gint width;
+
+    g_return_if_fail(tb != NULL);
+    g_return_if_fail(pixmaptext != NULL);
+
+    length = g_utf8_strlen(pixmaptext, -1);
+
+    text_get_extents(tb->tb_fontname, pixmaptext, &width, NULL, NULL, NULL);
+
+    tb->tb_pixmap_width = MAX(width, tb->tb_widget.width);
+    tb->tb_pixmap = gdk_pixmap_new(mainwin->window, tb->tb_pixmap_width,
+                                   tb->tb_widget.height,
+                                   gdk_rgb_get_visual()->depth);
+    gc = tb->tb_widget.gc;
+    c = skin_get_color(bmp_active_skin, SKIN_TEXTBG);
+    for (i = 0; i < tb->tb_widget.height; i++) {
+        gdk_gc_set_foreground(gc, &c[6 * i / tb->tb_widget.height]);
+        gdk_draw_line(tb->tb_pixmap, gc, 0, i, tb->tb_pixmap_width, i);
+    }
+
+    mask = gdk_pixmap_new(mainwin->window, tb->tb_pixmap_width,
+                          tb->tb_widget.height, 1);
+    maskgc = gdk_gc_new(mask);
+    pattern.pixel = 0;
+    gdk_gc_set_foreground(maskgc, &pattern);
+
+    gdk_draw_rectangle(mask, maskgc, TRUE, 0, 0,
+                       tb->tb_pixmap_width, tb->tb_widget.height);
+    pattern.pixel = 1;
+    gdk_gc_set_foreground(maskgc, &pattern);
+
+    gdk_gc_set_foreground(gc, skin_get_color(bmp_active_skin, SKIN_TEXTFG));
+
+    layout = gtk_widget_create_pango_layout(mainwin, pixmaptext);
+    pango_layout_set_font_description(layout, tb->tb_font);
+
+    gdk_draw_layout(tb->tb_pixmap, gc, 0, (tb->tb_font_descent / 2), layout);
+    g_object_unref(layout);
+
+    g_object_unref(maskgc);
+
+    gdk_gc_set_clip_mask(gc, mask);
+    c = skin_get_color(bmp_active_skin, SKIN_TEXTFG);
+    for (i = 0; i < tb->tb_widget.height; i++) {
+        gdk_gc_set_foreground(gc, &c[6 * i / tb->tb_widget.height]);
+        gdk_draw_line(tb->tb_pixmap, gc, 0, i, tb->tb_pixmap_width, i);
+    }
+    g_object_unref(mask);
+    gdk_gc_set_clip_mask(gc, NULL);
+}
+
+static void
+textbox_handle_special_char(gchar c, gint * x, gint * y)
+{
+    switch (c) {
+    case '"':
+        *x = 130;
+        *y = 0;
+        break;
+    case '\r':
+        *x = 50;
+        *y = 6;
+        break;
+    case ':':
+    case ';':
+        *x = 60;
+        *y = 6;
+        break;
+    case '(':
+        *x = 65;
+        *y = 6;
+        break;
+    case ')':
+        *x = 70;
+        *y = 6;
+        break;
+    case '-':
+        *x = 75;
+        *y = 6;
+        break;
+    case '`':
+    case '\'':
+        *x = 80;
+        *y = 6;
+        break;
+    case '!':
+        *x = 85;
+        *y = 6;
+        break;
+    case '_':
+        *x = 90;
+        *y = 6;
+        break;
+    case '+':
+        *x = 95;
+        *y = 6;
+        break;
+    case '\\':
+        *x = 100;
+        *y = 6;
+        break;
+    case '/':
+        *x = 105;
+        *y = 6;
+        break;
+    case '[':
+        *x = 110;
+        *y = 6;
+        break;
+    case ']':
+        *x = 115;
+        *y = 6;
+        break;
+    case '^':
+        *x = 120;
+        *y = 6;
+        break;
+    case '&':
+        *x = 125;
+        *y = 6;
+        break;
+    case '%':
+        *x = 130;
+        *y = 6;
+        break;
+    case '.':
+    case ',':
+        *x = 135;
+        *y = 6;
+        break;
+    case '=':
+        *x = 140;
+        *y = 6;
+        break;
+    case '$':
+        *x = 145;
+        *y = 6;
+        break;
+    case '#':
+        *x = 150;
+        *y = 6;
+        break;
+    case 'å':
+    case 'Å':
+        *x = 0;
+        *y = 12;
+        break;
+    case 'ö':
+    case 'Ö':
+        *x = 5;
+        *y = 12;
+        break;
+    case 'ä':
+    case 'Ä':
+        *x = 10;
+        *y = 12;
+        break;
+    case 'ü':
+    case 'Ü':
+        *x = 100;
+        *y = 0;
+        break;
+    case '?':
+        *x = 15;
+        *y = 12;
+        break;
+    case '*':
+        *x = 20;
+        *y = 12;
+        break;
+    default:
+        *x = 145;
+        *y = 0;
+        break;
+    }
+}
+
+static void
+textbox_generate_pixmap(TextBox * tb)
+{
+    gint length, i, x, y, wl;
+    gchar *pixmaptext;
+    GdkGC *gc;
+
+    g_return_if_fail(tb != NULL);
+
+    if (tb->tb_pixmap) {
+        g_object_unref(tb->tb_pixmap);
+        tb->tb_pixmap = NULL;
+    }
+
+    /*
+     * Don't reset the offset if only text after the last '(' has
+     * changed.  This is a hack to avoid visual noice on vbr files
+     * where we guess the length.
+     */
+    if (!(tb->tb_pixmap_text && strrchr(tb->tb_text, '(') &&
+          !strncmp(tb->tb_pixmap_text, tb->tb_text,
+                   strrchr(tb->tb_text, '(') - tb->tb_text)))
+        tb->tb_offset = 0;
+
+    g_free(tb->tb_pixmap_text);
+    tb->tb_pixmap_text = g_strdup(tb->tb_text);
+
+    /*
+     * wl is the number of (partial) letters visible. Only makes
+     * sense when using skinned font.
+     */
+
+    wl = tb->tb_widget.width / 5;
+    if (wl * 5 != tb->tb_widget.width)
+        wl++;
+
+    length = g_utf8_strlen(tb->tb_text, -1);
+
+    tb->tb_is_scrollable = FALSE;
+
+    if (textbox_should_scroll(tb)) {
+        tb->tb_is_scrollable = TRUE;
+        pixmaptext = g_strconcat(tb->tb_pixmap_text, "  ***  ", NULL);
+        length += 7;
+    }
+    else if (!tb->tb_font && length <= wl) {
+        gint pad = wl - length;
+        gchar *padchars = g_strnfill(pad, ' ');
+
+        pixmaptext = g_strconcat(tb->tb_pixmap_text, padchars, NULL);
+        g_free(padchars);
+        length += pad;
+    }
+    else
+        pixmaptext = g_strdup(tb->tb_pixmap_text);
+
+
+    if (tb->tb_is_scrollable) {
+        if (tb->tb_scroll_enabled && !tb->tb_timeout_tag) {
+            gint tag;
+            tag = TEXTBOX_SCROLL_SMOOTH_TIMEOUT;
+            tb->tb_timeout_tag = gtk_timeout_add(tag, textbox_scroll, tb);
+        }
+    }
+    else {
+        if (tb->tb_timeout_tag) {
+            gtk_timeout_remove(tb->tb_timeout_tag);
+            tb->tb_timeout_tag = 0;
+        }
+        tb->tb_offset = 0;
+    }
+
+    if (tb->tb_font) {
+        textbox_generate_xfont_pixmap(tb, pixmaptext);
+        g_free(pixmaptext);
+        return;
+    }
+
+    tb->tb_pixmap_width = length * 5;
+    tb->tb_pixmap = gdk_pixmap_new(mainwin->window,
+                                   tb->tb_pixmap_width, 6,
+                                   gdk_rgb_get_visual()->depth);
+    gc = tb->tb_widget.gc;
+
+    for (i = 0; i < length; i++) {
+        gchar c;
+        x = y = -1;
+        c = toupper(pixmaptext[i]);
+        if (c >= 'A' && c <= 'Z') {
+            x = 5 * (c - 'A');
+            y = 0;
+        }
+        else if (c >= '0' && c <= '9') {
+            x = 5 * (c - '0');
+            y = 6;
+        }
+        else
+            textbox_handle_special_char(c, &x, &y);
+
+        skin_draw_pixmap(bmp_active_skin,
+                         tb->tb_pixmap, gc, tb->tb_skin_index,
+                         x, y, i * 5, 0, 5, 6);
+    }
+    g_free(pixmaptext);
+}
+
+void
+textbox_set_scroll(TextBox * tb, gboolean s)
+{
+    g_return_if_fail(tb != NULL);
+
+    tb->tb_scroll_enabled = s;
+    if (tb->tb_scroll_enabled && tb->tb_is_scrollable
+        && tb->tb_scroll_allowed) {
+        gint tag;
+        tag = TEXTBOX_SCROLL_SMOOTH_TIMEOUT;
+
+	if (tb->tb_timeout_tag)
+        {
+	    gtk_timeout_remove(tb->tb_timeout_tag);
+            tb->tb_timeout_tag = 0;
+	}
+
+        tb->tb_timeout_tag = gtk_timeout_add(tag, textbox_scroll, tb);
+    }
+    else
+    {
+        if (tb->tb_timeout_tag)
+        {
+            gtk_timeout_remove(tb->tb_timeout_tag);
+            tb->tb_timeout_tag = 0;
+        }
+
+        tb->tb_offset = 0;
+        widget_draw(WIDGET(tb));
+    }
+
+}
+
+void
+textbox_set_xfont(TextBox * tb, gboolean use_xfont, const gchar * fontname)
+{
+    gint ascent, descent;
+
+    g_return_if_fail(tb != NULL);
+
+    if (tb->tb_font) {
+        pango_font_description_free(tb->tb_font);
+        tb->tb_font = NULL;
+    }
+
+    tb->tb_widget.y = tb->tb_nominal_y;
+    tb->tb_widget.height = tb->tb_nominal_height;
+
+    /* Make sure the pixmap is regenerated */
+    if (tb->tb_pixmap_text) {
+        g_free(tb->tb_pixmap_text);
+        tb->tb_pixmap_text = NULL;
+    }
+
+    if (!use_xfont || strlen(fontname) == 0)
+        return;
+
+    tb->tb_font = pango_font_description_from_string(fontname);
+    tb->tb_fontname = g_strdup(fontname);
+
+    text_get_extents(fontname,
+                     "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz ",
+                     NULL, NULL, &ascent, &descent);
+    tb->tb_font_ascent = ascent;
+    tb->tb_font_descent = descent;
+
+
+    if (tb->tb_font == NULL)
+        return;
+
+    tb->tb_widget.height = tb->tb_font_ascent;
+    if (tb->tb_widget.height > tb->tb_nominal_height)
+        tb->tb_widget.y -= (tb->tb_widget.height - tb->tb_nominal_height) / 2;
+    else
+        tb->tb_widget.height = tb->tb_nominal_height;
+}
+
+TextBox *
+create_textbox(GList ** wlist, GdkPixmap * parent, GdkGC * gc,
+               gint x, gint y, gint w, gboolean allow_scroll, SkinPixmapId si)
+{
+    TextBox *tb;
+
+    tb = g_new0(TextBox, 1);
+    widget_init(&tb->tb_widget, parent, gc, x, y, w, 6, 1);
+    tb->tb_widget.button_press_cb = textbox_button_press;
+    tb->tb_widget.button_release_cb = textbox_button_release;
+    tb->tb_widget.motion_cb = textbox_motion;
+    tb->tb_widget.draw = textbox_draw;
+    tb->tb_scroll_allowed = allow_scroll;
+    tb->tb_scroll_enabled = TRUE;
+    tb->tb_skin_index = si;
+    tb->tb_nominal_y = y;
+    tb->tb_nominal_height = tb->tb_widget.height;
+    widget_list_add(wlist, WIDGET(tb));
+    tb->tb_timeout_tag = 0;
+    return tb;
+}
+
+void
+textbox_free(TextBox * tb)
+{
+    g_return_if_fail(tb != NULL);
+
+    if (tb->tb_pixmap)
+        g_object_unref(tb->tb_pixmap);
+    g_free(tb->tb_text);
+    g_free(tb);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/textbox.h	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,68 @@
+/*  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; 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.
+ */
+
+#ifndef _WIDGETCORE_H_
+#error Please do not include me directly! Use widgetcore.h instead!
+#endif
+
+#ifndef TEXTBOX_H
+#define	TEXTBOX_H
+
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <pango/pango.h>
+
+#include "skin.h"
+#include "widget.h"
+
+#define	TEXTBOX_SCROLL_TIMEOUT	       200
+#define TEXTBOX_SCROLL_SMOOTH_TIMEOUT  30
+
+#define TEXT_BOX(x)  ((TextBox *)(x))
+struct _TextBox {
+    Widget tb_widget;
+    GdkPixmap *tb_pixmap;
+    gchar *tb_text, *tb_pixmap_text;
+    gint tb_pixmap_width;
+    gint tb_offset;
+    gboolean tb_scroll_allowed, tb_scroll_enabled;
+    gboolean tb_is_scrollable, tb_is_dragging;
+    gint tb_timeout_tag, tb_drag_x, tb_drag_off;
+    gint tb_nominal_y, tb_nominal_height;
+    gint tb_skin_id;
+    SkinPixmapId tb_skin_index;
+    PangoFontDescription *tb_font;
+    gint tb_font_ascent, tb_font_descent;
+    gchar *tb_fontname;
+};
+
+typedef struct _TextBox TextBox;
+
+void textbox_set_text(TextBox * tb, const gchar * text);
+void textbox_set_scroll(TextBox * tb, gboolean s);
+TextBox *create_textbox(GList ** wlist, GdkPixmap * parent, GdkGC * gc,
+                        gint x, gint y, gint w, gboolean allow_scroll,
+                        SkinPixmapId si);
+void textbox_set_xfont(TextBox * tb, gboolean use_xfont,
+                       const gchar * fontname);
+void textbox_free(TextBox * tb);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/vis.c	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,291 @@
+/*  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; 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 "widgetcore.h"
+
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <string.h>
+
+#include "main.h"
+#include "skin.h"
+#include "widget.h"
+
+static const gfloat vis_afalloff_speeds[] = { 0.34, 0.5, 1.0, 1.3, 1.6 };
+static const gfloat vis_pfalloff_speeds[] = { 1.2, 1.3, 1.4, 1.5, 1.6 };
+static const gint vis_redraw_delays[] = { 1, 2, 4, 8 };
+static const guint8 vis_scope_colors[] =
+    { 21, 21, 20, 20, 19, 19, 18, 19, 19, 20, 20, 21, 21 };
+
+void
+vis_timeout_func(Vis * vis, guchar * data)
+{
+    static GTimer *timer = NULL;
+    gulong micros = 9999999;
+    gboolean falloff = FALSE;
+    gint i;
+
+    if (!timer) {
+        timer = g_timer_new();
+        g_timer_start(timer);
+    }
+    else {
+        g_timer_elapsed(timer, &micros);
+        if (micros > 14000)
+            g_timer_reset(timer);
+
+    }
+
+    if (cfg.vis_type == VIS_ANALYZER) {
+        if (micros > 14000)
+            falloff = TRUE;
+        if (data || falloff) {
+            for (i = 0; i < 75; i++) {
+                if (data && data[i] > vis->vs_data[i]) {
+                    vis->vs_data[i] = data[i];
+                    if (vis->vs_data[i] > vis->vs_peak[i]) {
+                        vis->vs_peak[i] = vis->vs_data[i];
+                        vis->vs_peak_speed[i] = 0.01;
+
+                    }
+                    else if (vis->vs_peak[i] > 0.0) {
+                        vis->vs_peak[i] -= vis->vs_peak_speed[i];
+                        vis->vs_peak_speed[i] *=
+                            vis_pfalloff_speeds[cfg.peaks_falloff];
+                        if (vis->vs_peak[i] < vis->vs_data[i])
+                            vis->vs_peak[i] = vis->vs_data[i];
+                        if (vis->vs_peak[i] < 0.0)
+                            vis->vs_peak[i] = 0.0;
+                    }
+                }
+                else if (falloff) {
+                    if (vis->vs_data[i] > 0.0) {
+                        vis->vs_data[i] -=
+                            vis_afalloff_speeds[cfg.analyzer_falloff];
+                        if (vis->vs_data[i] < 0.0)
+                            vis->vs_data[i] = 0.0;
+                    }
+                    if (vis->vs_peak[i] > 0.0) {
+                        vis->vs_peak[i] -= vis->vs_peak_speed[i];
+                        vis->vs_peak_speed[i] *=
+                            vis_pfalloff_speeds[cfg.peaks_falloff];
+                        if (vis->vs_peak[i] < vis->vs_data[i])
+                            vis->vs_peak[i] = vis->vs_data[i];
+                        if (vis->vs_peak[i] < 0.0)
+                            vis->vs_peak[i] = 0.0;
+                    }
+                }
+            }
+        }
+    }
+    else if (data) {
+        for (i = 0; i < 75; i++)
+            vis->vs_data[i] = data[i];
+    }
+
+    if (micros > 14000) {
+        if (!vis->vs_refresh_delay) {
+            vis_draw((Widget *) vis);
+            vis->vs_refresh_delay = vis_redraw_delays[cfg.vis_refresh];
+
+        }
+        vis->vs_refresh_delay--;
+    }
+}
+
+void
+vis_draw(Widget * w)
+{
+    Vis *vis = (Vis *) w;
+    gint x, y, h = 0, h2;
+    guchar vis_color[24][3];
+    guchar rgb_data[152 * 32], *ptr, c;
+    guint32 colors[24];
+    GdkRgbCmap *cmap;
+
+    if (!vis->vs_widget.visible)
+        return;
+
+    skin_get_viscolor(bmp_active_skin, vis_color);
+    for (y = 0; y < 24; y++) {
+        colors[y] =
+            vis_color[y][0] << 16 | vis_color[y][1] << 8 | vis_color[y][2];
+    }
+    cmap = gdk_rgb_cmap_new(colors, 24);
+
+    memset(rgb_data, 0, 76 * 16);
+    for (y = 1; y < 16; y += 2) {
+        ptr = rgb_data + (y * 76);
+        for (x = 0; x < 76; x += 2, ptr += 2)
+            *ptr = 1;
+    }
+    if (cfg.vis_type == VIS_ANALYZER) {
+        for (x = 0; x < 75; x++) {
+            if (cfg.analyzer_type == ANALYZER_BARS && (x % 4) == 0)
+                h = vis->vs_data[x >> 2];
+            else if (cfg.analyzer_type == ANALYZER_LINES)
+                h = vis->vs_data[x];
+            if (h && (cfg.analyzer_type == ANALYZER_LINES ||
+                      (x % 4) != 3)) {
+                ptr = rgb_data + ((16 - h) * 76) + x;
+                switch (cfg.analyzer_mode) {
+                case ANALYZER_NORMAL:
+                    for (y = 0; y < h; y++, ptr += 76)
+                        *ptr = 18 - h + y;
+                    break;
+                case ANALYZER_FIRE:
+                    for (y = 0; y < h; y++, ptr += 76)
+                        *ptr = y + 2;
+                    break;
+                case ANALYZER_VLINES:
+                    for (y = 0; y < h; y++, ptr += 76)
+                        *ptr = 18 - h;
+                    break;
+                }
+            }
+        }
+        if (cfg.analyzer_peaks) {
+            for (x = 0; x < 75; x++) {
+                if (cfg.analyzer_type == ANALYZER_BARS && (x % 4) == 0)
+                    h = vis->vs_peak[x >> 2];
+                else if (cfg.analyzer_type == ANALYZER_LINES)
+                    h = vis->vs_peak[x];
+                if (h
+                    && (cfg.analyzer_type == ANALYZER_LINES
+                        || (x % 4) != 3))
+                    rgb_data[(16 - h) * 76 + x] = 23;
+            }
+        }
+    }
+    else if (cfg.vis_type == VIS_SCOPE) {
+        for (x = 0; x < 75; x++) {
+            switch (cfg.scope_mode) {
+            case SCOPE_DOT:
+                h = vis->vs_data[x];
+                ptr = rgb_data + ((15 - h) * 76) + x;
+                *ptr = vis_scope_colors[h];
+                break;
+            case SCOPE_LINE:
+                if (x != 74) {
+                    h = 15 - vis->vs_data[x];
+                    h2 = 15 - vis->vs_data[x + 1];
+                    if (h > h2) {
+                        y = h;
+                        h = h2;
+                        h2 = y;
+                    }
+                    ptr = rgb_data + (h * 76) + x;
+                    for (y = h; y <= h2; y++, ptr += 76)
+                        *ptr = vis_scope_colors[y - 3];
+
+                }
+                else {
+                    h = 15 - vis->vs_data[x];
+                    ptr = rgb_data + (h * 76) + x;
+                    *ptr = vis_scope_colors[h];
+                }
+                break;
+            case SCOPE_SOLID:
+                h = 15 - vis->vs_data[x];
+                h2 = 9;
+                c = vis_scope_colors[(gint) vis->vs_data[x]];
+                if (h > h2) {
+                    y = h;
+                    h = h2;
+                    h2 = y;
+                }
+                ptr = rgb_data + (h * 76) + x;
+                for (y = h; y <= h2; y++, ptr += 76)
+                    *ptr = c;
+                break;
+            }
+        }
+    }
+
+    /* FIXME: The check "shouldn't" be neccessary? */
+    /*	if (GTK_IS_WINDOW(vis->vs_window)) { */
+    GDK_THREADS_ENTER();
+    gdk_draw_indexed_image(vis->vs_window, vis->vs_widget.gc,
+                           vis->vs_widget.x, vis->vs_widget.y,
+                           vis->vs_widget.width, vis->vs_widget.height,
+                           GDK_RGB_DITHER_NORMAL, (guchar *) rgb_data,
+                           76, cmap);
+    GDK_THREADS_LEAVE();
+    /*	} else {
+        vis->vs_window = mainwin->window;
+        GDK_THREADS_ENTER();
+        gdk_draw_indexed_image(vis->vs_window, vis->vs_widget.gc,
+        vis->vs_widget.x, vis->vs_widget.y,
+        vis->vs_widget.width, vis->vs_widget.height,
+        GDK_RGB_DITHER_NORMAL, (guchar *) rgb_data,
+        76, cmap);
+        GDK_THREADS_LEAVE();
+        }
+    */
+
+    gdk_rgb_cmap_free(cmap);
+}
+
+void
+vis_clear_data(Vis * vis)
+{
+    gint i;
+
+    if (!vis)
+        return;
+
+    for (i = 0; i < 75; i++) {
+        vis->vs_data[i] = (cfg.vis_type == VIS_SCOPE) ? 6 : 0;
+        vis->vs_peak[i] = 0;
+    }
+}
+
+void
+vis_clear(Vis * vis)
+{
+    gdk_window_clear_area(vis->vs_window, vis->vs_widget.x,
+                          vis->vs_widget.y, vis->vs_widget.width,
+                          vis->vs_widget.height);
+}
+
+void
+vis_set_window(Vis * vis, GdkWindow * window)
+{
+    vis->vs_window = window;
+}
+
+Vis *
+create_vis(GList ** wlist,
+           GdkPixmap * parent,
+           GdkWindow * window,
+           GdkGC * gc,
+           gint x, gint y,
+           gint width)
+{
+    Vis *vis;
+
+    vis = g_new0(Vis, 1);
+
+    widget_init(&vis->vs_widget, parent, gc, x, y, width, 16, 1);
+    widget_list_add(wlist, WIDGET(vis));
+
+    return vis;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/vis.h	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,82 @@
+/*  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; 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.
+ */
+
+#ifndef _WIDGETCORE_H_
+#error Please do not include me directly! Use widgetcore.h instead!
+#endif
+
+#ifndef VIS_H
+#define VIS_H
+
+#include <glib.h>
+#include <gdk/gdk.h>
+
+#include "widget.h"
+
+typedef enum {
+    VIS_ANALYZER, VIS_SCOPE, VIS_OFF
+} VisType;
+
+typedef enum {
+    ANALYZER_NORMAL, ANALYZER_FIRE, ANALYZER_VLINES
+} AnalyzerMode;
+
+typedef enum {
+    ANALYZER_LINES, ANALYZER_BARS
+} AnalyzerType;
+
+typedef enum {
+    SCOPE_DOT, SCOPE_LINE, SCOPE_SOLID
+} ScopeMode;
+
+typedef enum {
+    VU_NORMAL, VU_SMOOTH
+} VUMode;
+
+typedef enum {
+    REFRESH_FULL, REFRESH_HALF, REFRESH_QUARTER, REFRESH_EIGTH
+} RefreshRate;
+
+typedef enum {
+    FALLOFF_SLOWEST, FALLOFF_SLOW, FALLOFF_MEDIUM, FALLOFF_FAST,
+    FALLOFF_FASTEST
+} FalloffSpeed;
+
+#define VIS(x)  ((Vis *)(x))
+struct _Vis {
+    Widget vs_widget;
+    GdkWindow *vs_window;
+    gfloat vs_data[75], vs_peak[75], vs_peak_speed[75];
+    gint vs_refresh_delay;
+};
+
+typedef struct _Vis Vis;
+
+void vis_draw(Widget * w);
+
+Vis *create_vis(GList ** wlist, GdkPixmap * parent, GdkWindow * window,
+                GdkGC * gc, gint x, gint y, gint width);
+void vis_timeout_func(Vis * vis, guchar * data);
+void vis_clear_data(Vis * vis);
+void vis_clear(Vis * vis);
+void vis_set_window(Vis * vis, GdkWindow * window);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/widget.c	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,263 @@
+/*  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; 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 "widgetcore.h"
+
+#include <glib.h>
+#include <gdk/gdk.h>
+
+#include "debug.h"
+
+
+void
+widget_init(Widget * widget, GdkPixmap * parent, GdkGC * gc,
+            gint x, gint y, gint width, gint height, gint visible)
+{
+    widget->parent = parent;
+    widget->gc = gc;
+    widget_set_position(widget, x, y);
+    widget_set_size(widget, width, height);
+    widget->visible = visible;
+    widget->redraw = TRUE;
+    widget->mutex = g_mutex_new();
+}
+
+void
+widget_set_position(Widget * widget, gint x, gint y)
+{
+    widget->x = x;
+    widget->y = y;
+    widget_queue_redraw(widget);
+}
+
+void
+widget_set_size(Widget * widget, gint width, gint height)
+{
+    widget->width = width;
+    widget->height = height;
+    widget_queue_redraw(widget);
+}
+
+void
+widget_queue_redraw(Widget * widget)
+{
+    widget->redraw = TRUE;
+}
+
+gboolean
+widget_contains(Widget * widget, gint x, gint y)
+{
+    return (widget->visible &&
+            x >= widget->x && 
+            y >= widget->y && 
+            x <  widget->x + widget->width && 
+            y <  widget->y + widget->height);
+}
+
+void
+widget_show(Widget * widget)
+{
+    if (!widget)
+        return;
+
+    widget->visible = TRUE;
+    widget_draw(widget);
+}
+
+void
+widget_hide(Widget * widget)
+{
+    if (!widget)
+        return;
+
+    widget->visible = FALSE;
+}
+
+gboolean
+widget_is_visible(Widget * widget)
+{
+    if (!widget)
+        return FALSE;
+
+    return widget->visible;
+}
+
+void
+widget_resize(Widget * widget, gint width, gint height)
+{
+    widget_set_size(widget, width, height);
+}
+
+void
+widget_move(Widget * widget, gint x, gint y)
+{
+    widget_lock(widget);
+    widget_set_position(widget, x, y);
+    widget_unlock(widget);
+}
+
+void
+widget_draw(Widget * widget)
+{
+    widget_lock(widget);
+    WIDGET(widget)->redraw = TRUE;
+    widget_unlock(widget);
+}
+
+void
+widget_draw_quick(Widget * widget)
+{
+    widget_lock(widget);
+    if (WIDGET(widget)->draw != NULL)
+    {
+	WIDGET(widget)->draw(widget);
+        gdk_flush();
+    }
+    widget_unlock(widget);
+}
+
+void
+widget_list_add(GList ** list, Widget * widget)
+{
+    (*list) = g_list_append(*list, widget);
+}
+
+void
+handle_press_cb(GList * widget_list, GtkWidget * widget,
+                GdkEventButton * event)
+{
+    GList *wl;
+
+    for (wl = widget_list; wl; wl = g_list_next(wl)) {
+        if (WIDGET(wl->data)->button_press_cb)
+            WIDGET(wl->data)->button_press_cb(widget, event, wl->data);
+    }
+}
+
+void
+handle_release_cb(GList * widget_list, GtkWidget * widget,
+                  GdkEventButton * event)
+{
+    GList *wl;
+
+    for (wl = widget_list; wl; wl = g_list_next(wl)) {
+        if (WIDGET(wl->data)->button_release_cb)
+            WIDGET(wl->data)->button_release_cb(widget, event, wl->data);
+    }
+}
+
+void
+handle_motion_cb(GList * widget_list, GtkWidget * widget,
+                 GdkEventMotion * event)
+{
+    GList *wl;
+
+    for (wl = widget_list; wl; wl = g_list_next(wl)) {
+        if (WIDGET(wl->data)->motion_cb)
+            WIDGET(wl->data)->motion_cb(widget, event, wl->data);
+    }
+}
+
+void
+handle_scroll_cb(GList * wlist, GtkWidget * widget, GdkEventScroll * event)
+{
+    GList *wl;
+
+    for (wl = wlist; wl; wl = g_list_next(wl)) {
+        if (WIDGET(wl->data)->mouse_scroll_cb)
+            WIDGET(wl->data)->mouse_scroll_cb(widget, event, wl->data);
+    }
+}
+
+void
+widget_list_draw(GList * widget_list, gboolean * redraw, gboolean force)
+{
+    GList *wl;
+    Widget *w;
+
+    *redraw = FALSE;
+    wl = widget_list;
+
+    for (wl = widget_list; wl; wl = g_list_next(wl)) {
+        w = WIDGET(wl->data);
+
+        REQUIRE_LOCK(w->mutex);
+
+        if (!w->draw)
+            continue;
+
+        if (!w->visible)
+            continue;
+
+        if (w->redraw || force) {
+            w->draw(w);
+/*             w->redraw = FALSE; */
+            *redraw = TRUE;
+        }
+    }
+}
+
+void
+widget_list_change_pixmap(GList * widget_list, GdkPixmap * pixmap)
+{
+    GList *wl;
+
+    for (wl = widget_list; wl; wl = g_list_next(wl)) {
+        Widget *widget = wl->data;
+        widget->parent = pixmap;
+        widget_queue_redraw(widget);
+    }
+}
+
+void
+widget_list_clear_redraw(GList * widget_list)
+{
+    GList *wl;
+
+    for (wl = widget_list; wl; wl = g_list_next(wl)) {
+        REQUIRE_LOCK(WIDGET(wl->data)->mutex);
+        WIDGET(wl->data)->redraw = FALSE;
+    }
+}
+
+void
+widget_lock(Widget * widget)
+{
+    g_mutex_lock(WIDGET(widget)->mutex);
+}
+
+void
+widget_unlock(Widget * widget)
+{
+    g_mutex_unlock(WIDGET(widget)->mutex);
+}
+
+void
+widget_list_lock(GList * widget_list)
+{
+    g_list_foreach(widget_list, (GFunc) widget_lock, NULL);
+}
+
+void
+widget_list_unlock(GList * widget_list)
+{
+    g_list_foreach(widget_list, (GFunc) widget_unlock, NULL);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/widget.h	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,107 @@
+/*  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; 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.
+ */
+
+#ifndef _WIDGETCORE_H_
+#error Please do not include me directly! Use widgetcore.h instead!
+#endif
+
+#ifndef WIDGET_H
+#define WIDGET_H
+
+
+#include <glib.h>
+#include <gdk/gdk.h>
+#include <gtk/gtk.h>
+
+
+typedef struct _Widget Widget;
+
+
+typedef void (*WidgetButtonPressFunc) (GtkWidget *, GdkEventButton *,
+                                       gpointer);
+typedef void (*WidgetButtonReleaseFunc) (GtkWidget *, GdkEventButton *,
+                                         gpointer);
+typedef void (*WidgetMotionFunc) (GtkWidget *, GdkEventMotion *, gpointer);
+typedef void (*WidgetDrawFunc) (Widget *);
+typedef void (*WidgetScrollFunc) (GtkWidget *, GdkEventScroll *, gpointer);
+
+
+#define WIDGET(x)  ((Widget *)(x))
+struct _Widget {
+    GdkPixmap *parent;
+    GdkGC *gc;
+
+    gint x, y;
+    gint width, height;
+
+    gint visible;
+    gboolean redraw;
+
+    GMutex *mutex;
+
+    WidgetButtonPressFunc button_press_cb;
+    WidgetButtonReleaseFunc button_release_cb;
+    WidgetMotionFunc motion_cb;
+    WidgetDrawFunc draw;
+    WidgetScrollFunc mouse_scroll_cb;
+};
+
+
+void widget_init(Widget * widget, GdkPixmap * parent, GdkGC * gc,
+                 gint x, gint y, gint width, gint height, gint visible);
+
+void widget_set_position(Widget * widget, gint x, gint y);
+void widget_set_size(Widget * widget, gint width, gint height);
+void widget_queue_redraw(Widget * widget);
+
+void widget_lock(Widget * widget);
+void widget_unlock(Widget * widget);
+
+gboolean widget_contains(Widget * widget, gint x, gint y);
+
+void widget_show(Widget * widget);
+void widget_hide(Widget * widget);
+gboolean widget_is_visible(Widget * widget);
+
+void widget_resize(Widget * widget, gint width, gint height);
+void widget_move(Widget * widget, gint x, gint y);
+void widget_draw(Widget * widget);
+void widget_draw_quick(Widget * widget);
+
+void handle_press_cb(GList * wlist, GtkWidget * widget,
+                     GdkEventButton * event);
+void handle_release_cb(GList * wlist, GtkWidget * widget,
+                       GdkEventButton * event);
+void handle_motion_cb(GList * wlist, GtkWidget * widget,
+                      GdkEventMotion * event);
+void handle_scroll_cb(GList * wlist, GtkWidget * widget,
+                      GdkEventScroll * event);
+
+void widget_list_add(GList ** list, Widget * widget);
+void widget_list_draw(GList * list, gboolean * redraw, gboolean force);
+void widget_list_change_pixmap(GList * list, GdkPixmap * pixmap);
+void widget_list_clear_redraw(GList * list);
+void widget_list_lock(GList * list);
+void widget_list_unlock(GList * list);
+
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audacious/widgets/widgetcore.h	Thu Sep 07 22:26:54 2006 -0700
@@ -0,0 +1,41 @@
+/*
+ * Audacious - a cross-platform multimedia player
+ * Copyright (c) 2006  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; 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.
+ */
+
+#ifndef _WIDGETCORE_H_
+#define _WIDGETCORE_H_
+
+#include "tbutton.h"
+#include "eq_graph.h"
+#include "eq_slider.h"
+#include "hslider.h"
+#include "menurow.h"
+#include "monostereo.h"
+#include "number.h"
+#include "pbutton.h"
+#include "playlist_list.h"
+#include "playlist_slider.h"
+#include "playstatus.h"
+#include "sbutton.h"
+#include "skin.h"
+#include "svis.h"
+#include "textbox.h"
+#include "vis.h"
+#include "widget.h"
+
+#endif