changeset 1643:1a525bd78cf0 trunk

[svn] - copy the WA2GUI code into a Plugin shell
author nenolod
date Thu, 07 Sep 2006 19:57:27 -0700
parents 3d3b60e66491
children a556e7ff1871
files ChangeLog Plugins/UserInterface/wa2gui/Makefile Plugins/UserInterface/wa2gui/equalizer.c Plugins/UserInterface/wa2gui/equalizer.h Plugins/UserInterface/wa2gui/filepopup.c Plugins/UserInterface/wa2gui/filepopup.h Plugins/UserInterface/wa2gui/mainwin.c Plugins/UserInterface/wa2gui/mainwin.h Plugins/UserInterface/wa2gui/playlist.c Plugins/UserInterface/wa2gui/ui_playlist.h
diffstat 10 files changed, 8254 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Thu Sep 07 18:33:19 2006 -0700
+++ b/ChangeLog	Thu Sep 07 19:57:27 2006 -0700
@@ -1,3 +1,12 @@
+2006-09-08 01:33:19 +0000  William Pitcock <nenolod@nenolod.net>
+  revision [2215]
+  - use gdk_drawable_get_size() instead
+  
+
+  Changes:        Modified:
+  +1 -11          trunk/audacious/widgets/skin.c  
+
+
 2006-09-08 00:27:27 +0000  William Pitcock <nenolod@nenolod.net>
   revision [2213]
   - use smartinclude here instead of conditional-based imports
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/UserInterface/wa2gui/Makefile	Thu Sep 07 19:57:27 2006 -0700
@@ -0,0 +1,17 @@
+include ../../../mk/rules.mk
+include ../../../mk/init.mk
+
+OBJECTIVE_LIBS = libwa2_gui$(SHARED_SUFFIX)
+
+noinst_HEADERS = coreaudio.h
+
+LIBDIR = $(plugindir)/$(USERINTERFACE_PLUGIN_DIR)
+
+LIBADD = $(GTK_LIBS)
+SOURCES = mainwin.c equalizer.c playlist.c filepopup.c wa2gui.c
+
+OBJECTS = ${SOURCES:.c=.o}
+
+CFLAGS += $(PICFLAGS) $(GTK_CFLAGS) -I../../../intl -I../../..
+
+include ../../../mk/objective.mk
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/UserInterface/wa2gui/equalizer.c	Thu Sep 07 19:57:27 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/Plugins/UserInterface/wa2gui/equalizer.h	Thu Sep 07 19:57:27 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/UserInterface/wa2gui/filepopup.c	Thu Sep 07 19:57:27 2006 -0700
@@ -0,0 +1,512 @@
+/*
+ * Audacious: A cross-platform multimedia player
+ * Copyright (c) 2006 William Pitcock, Tony Vroon, George Averill,
+ *                    Giacomo Lozito, Derek Pomery and Yoshiki Yazawa.
+ *
+ * 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 <gtk/gtk.h>
+#include <glade/glade.h>
+#include <string.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "glade.h"
+
+#include "plugin.h"
+#include "pluginenum.h"
+#include "input.h"
+#include "effect.h"
+#include "general.h"
+#include "output.h"
+#include "visualization.h"
+
+#include "main.h"
+#include "urldecode.h"
+#include "util.h"
+#include "dnd.h"
+#include "libaudacious/configdb.h"
+#include "libaudacious/titlestring.h"
+
+#include "playlist.h"
+
+#include "mainwin.h"
+#include "ui_playlist.h"
+#include "skinwin.h"
+#include "build_stamp.h"
+#include "ui_fileinfo.h"
+#include "ui_playlist.h"
+
+GtkWidget *fileinfo_win;
+GtkWidget *filepopup_win;
+GdkPixbuf *filepopup_pixbuf;
+
+static void
+fileinfo_entry_set_text(const char *entry, const char *text)
+{
+	GladeXML *xml = g_object_get_data(G_OBJECT(fileinfo_win), "glade-xml");
+	GtkWidget *widget = glade_xml_get_widget(xml, entry);
+
+	if (xml == NULL || widget == NULL)
+		return;
+
+	gtk_entry_set_text(GTK_ENTRY(widget), text);
+}
+
+static void
+fileinfo_entry_set_text_free(const char *entry, char *text)
+{
+	GladeXML *xml = g_object_get_data(G_OBJECT(fileinfo_win), "glade-xml");
+	GtkWidget *widget = glade_xml_get_widget(xml, entry);
+
+	if (xml == NULL || widget == NULL)
+		return;
+
+	gtk_entry_set_text(GTK_ENTRY(widget), text);
+
+	g_free(text);
+}
+
+static void
+fileinfo_entry_set_image(const char *entry, const char *text)
+{
+	GladeXML *xml = g_object_get_data(G_OBJECT(fileinfo_win), "glade-xml");
+	GtkWidget *widget = glade_xml_get_widget(xml, entry);
+	GdkPixbuf *pixbuf;
+
+	if (xml == NULL || widget == NULL)
+		return;
+
+	pixbuf = gdk_pixbuf_new_from_file(text, NULL);
+
+	if (pixbuf == NULL)
+		return;
+
+	if (gdk_pixbuf_get_height(GDK_PIXBUF(pixbuf)) > 150)
+	{
+		GdkPixbuf *pixbuf2 = gdk_pixbuf_scale_simple(GDK_PIXBUF(pixbuf), 150, 150, GDK_INTERP_BILINEAR);
+		g_object_unref(G_OBJECT(pixbuf));
+		pixbuf = pixbuf2;
+	}
+
+	gtk_image_set_from_pixbuf(GTK_IMAGE(widget), GDK_PIXBUF(pixbuf));
+	g_object_unref(G_OBJECT(pixbuf));
+}
+
+static void
+filepopup_entry_set_text(const char *entry, const char *text)
+{
+	GladeXML *xml = g_object_get_data(G_OBJECT(filepopup_win), "glade-xml");
+	GtkWidget *widget = glade_xml_get_widget(xml, entry);
+
+	if (xml == NULL || widget == NULL)
+		return;
+
+	gtk_label_set_text(GTK_LABEL(widget), text);
+}
+
+static void
+filepopup_entry_set_image(const char *entry, const char *text)
+{
+	GladeXML *xml = g_object_get_data(G_OBJECT(filepopup_win), "glade-xml");
+	GtkWidget *widget = glade_xml_get_widget(xml, entry);
+	GdkPixbuf *pixbuf;
+
+	if (xml == NULL || widget == NULL)
+		return;
+
+	pixbuf = gdk_pixbuf_new_from_file(text, NULL);
+
+	if (pixbuf == NULL)
+		return;
+
+	if (gdk_pixbuf_get_height(GDK_PIXBUF(pixbuf)) > 150)
+	{
+		GdkPixbuf *pixbuf2 = gdk_pixbuf_scale_simple(GDK_PIXBUF(pixbuf), 150, 150, GDK_INTERP_BILINEAR);
+		g_object_unref(G_OBJECT(pixbuf));
+		pixbuf = pixbuf2;
+	}
+
+	gtk_image_set_from_pixbuf(GTK_IMAGE(widget), GDK_PIXBUF(pixbuf));
+	g_object_unref(G_OBJECT(pixbuf));
+}
+
+static void
+filepopup_entry_set_text_free(const char *entry, char *text)
+{
+	GladeXML *xml = g_object_get_data(G_OBJECT(filepopup_win), "glade-xml");
+	GtkWidget *widget = glade_xml_get_widget(xml, entry);
+
+	if (xml == NULL || widget == NULL)
+		return;
+
+	gtk_label_set_text(GTK_LABEL(widget), text);
+
+	g_free(text);
+}
+
+static gboolean
+filepopup_pointer_check_iter(gpointer unused)
+{
+	gint x, y, pos;
+	TitleInput *tuple;
+	static gint prev_x = 0, prev_y = 0, ctr = 0, prev_pos = -1;
+	gboolean skip = FALSE;
+	GdkWindow *win;
+
+	win = gdk_window_at_pointer(NULL, NULL);
+	gdk_window_get_pointer(GDK_WINDOW(playlistwin->window), &x, &y, NULL);
+	pos = playlist_list_get_playlist_position(playlistwin_list, x, y);
+
+	if (win == NULL
+		|| cfg.show_filepopup_for_tuple == FALSE
+		|| playlistwin_list->pl_tooltips == FALSE
+		|| pos != prev_pos
+		|| win != GDK_WINDOW(playlistwin->window)
+		|| playlistwin_is_shaded())
+	{
+		prev_pos = pos;
+		ctr = 0;
+                if ( filepopup_win->window != NULL &&
+                     gdk_window_is_viewable(GDK_WINDOW(filepopup_win->window)) )
+                  filepopup_hide(NULL);
+		return TRUE;
+	}
+
+	if (prev_x == x && prev_y == y)
+		ctr++;
+	else
+	{
+		ctr = 0;
+		prev_x = x;
+		prev_y = y;
+		filepopup_hide(NULL);
+		return TRUE;
+	}
+
+	if (filepopup_win->window == NULL)
+		skip = TRUE;
+
+        if (ctr >= 20 && (skip == TRUE || gdk_window_is_viewable(GDK_WINDOW(filepopup_win->window)) != TRUE))
+        {
+		if (pos == -1)
+  		{
+			filepopup_hide(NULL);
+			return TRUE;
+	    	}
+
+		prev_pos = pos;
+
+		tuple = playlist_get_tuple(pos);
+		filepopup_show_for_tuple(tuple);
+	}
+
+	return TRUE;
+}
+
+void fileinfo_hide(gpointer unused)
+{
+	gtk_widget_hide(fileinfo_win);
+
+	/* Clear it out. */
+	fileinfo_entry_set_text("entry_title", "");
+	fileinfo_entry_set_text("entry_artist", "");
+	fileinfo_entry_set_text("entry_album", "");
+	fileinfo_entry_set_text("entry_comment", "");
+	fileinfo_entry_set_text("entry_genre", "");
+	fileinfo_entry_set_text("entry_year", "");
+	fileinfo_entry_set_text("entry_track", "");
+	fileinfo_entry_set_text("entry_location", "");
+
+	fileinfo_entry_set_image("image_artwork", DATA_DIR "/images/audio.png");
+}
+
+void filepopup_hide(gpointer unused)
+{
+	gtk_widget_hide(filepopup_win);
+
+	filepopup_entry_set_text("label_title", "");
+	filepopup_entry_set_text("label_artist", "");
+	filepopup_entry_set_text("label_album", "");
+	filepopup_entry_set_text("label_genre", "");
+	filepopup_entry_set_text("label_track", "");
+	filepopup_entry_set_text("label_year", "");
+	filepopup_entry_set_text("label_length", "");
+
+	filepopup_entry_set_image("image_artwork", DATA_DIR "/images/audio.png");
+
+	gtk_window_resize(GTK_WINDOW(filepopup_win), 1, 1);
+}
+
+void
+create_fileinfo_window(void)
+{
+	const gchar *glade_file = DATA_DIR "/glade/fileinfo.glade";
+	GladeXML *xml;
+	GtkWidget *widget;
+
+	xml = glade_xml_new_or_die(_("Track Information Window"), glade_file, NULL, NULL);
+
+	glade_xml_signal_autoconnect(xml);
+
+	fileinfo_win = glade_xml_get_widget(xml, "fileinfo_win");
+	g_object_set_data(G_OBJECT(fileinfo_win), "glade-xml", xml);
+	gtk_window_set_transient_for(GTK_WINDOW(fileinfo_win), GTK_WINDOW(mainwin));
+
+	widget = glade_xml_get_widget(xml, "image_artwork");
+	gtk_image_set_from_file(GTK_IMAGE(widget), DATA_DIR "/images/audio.png");
+
+	widget = glade_xml_get_widget(xml, "btn_close");
+	g_signal_connect(G_OBJECT(widget), "clicked", (GCallback) fileinfo_hide, NULL);
+}
+
+void
+create_filepopup_window(void)
+{
+	const gchar *glade_file = DATA_DIR "/glade/fileinfo_popup.glade";
+	GladeXML *xml;
+	GtkWidget *widget;
+
+	xml = glade_xml_new_or_die(_("Track Information Popup"), glade_file, NULL, NULL);
+
+	glade_xml_signal_autoconnect(xml);
+
+	filepopup_win = glade_xml_get_widget(xml, "win_pl_popup");
+	g_object_set_data(G_OBJECT(filepopup_win), "glade-xml", xml);
+	gtk_window_set_transient_for(GTK_WINDOW(filepopup_win), GTK_WINDOW(mainwin));
+
+	widget = glade_xml_get_widget(xml, "image_artwork");
+	gtk_image_set_from_file(GTK_IMAGE(widget), DATA_DIR "/images/audio.png");
+
+	g_timeout_add(50, filepopup_pointer_check_iter, NULL);
+}
+
+void
+fileinfo_show_for_tuple(TitleInput *tuple)
+{
+	gchar *tmp = NULL;
+
+	if (tuple == NULL)
+		return;
+
+	gtk_widget_realize(fileinfo_win);
+
+	fileinfo_entry_set_text("entry_title", tuple->track_name);
+	fileinfo_entry_set_text("entry_artist", tuple->performer);
+	fileinfo_entry_set_text("entry_album", tuple->album_name);
+	fileinfo_entry_set_text("entry_comment", tuple->comment);
+	fileinfo_entry_set_text("entry_genre", tuple->genre);
+
+	tmp = g_strdup_printf("%s/%s", tuple->file_path, tuple->file_name);
+	if(tmp){
+		fileinfo_entry_set_text_free("entry_location", str_to_utf8(tmp));
+		g_free(tmp);
+		tmp = NULL;
+	}
+
+	if (tuple->year != 0)
+		fileinfo_entry_set_text_free("entry_year", g_strdup_printf("%d", tuple->year));
+
+	if (tuple->track_number != 0)
+		fileinfo_entry_set_text_free("entry_track", g_strdup_printf("%d", tuple->track_number));
+
+	tmp = fileinfo_recursive_get_image(tuple->file_path, 0);
+	
+	if(tmp)
+	{
+		fileinfo_entry_set_image("image_artwork", tmp);
+		g_free(tmp);
+	}
+	
+	gtk_widget_show(fileinfo_win);
+}
+
+static gboolean
+cover_name_filter(const gchar *name, const gchar *filter, const gboolean ret_on_empty)
+{
+	gboolean result = FALSE;
+	gchar **splitted;
+	gchar *current;
+	gchar *lname;
+	gint i;
+
+	if (!filter || strlen(filter) == 0) {
+		return ret_on_empty;
+	}
+
+	splitted = g_strsplit(filter, ",", 0);
+
+	lname = g_strdup(name);
+	g_strdown(lname);
+
+	for (i = 0; !result && (current = splitted[i]); i++) {
+		gchar *stripped = g_strstrip(g_strdup(current));
+		g_strdown(stripped);
+
+		result = result || strstr(lname, stripped);
+
+		g_free(stripped);
+	}
+
+	g_free(lname);
+	g_strfreev(splitted);
+
+	return result;
+}
+
+/* Check wether it's an image we want */
+static gboolean
+is_front_cover_image(const gchar *name)
+{
+	char *ext;
+
+	ext = strrchr(name, '.');
+	if (!ext) {
+		/* No file extension */
+		return FALSE;
+	}
+
+	if (g_strcasecmp(ext, ".jpg") != 0 &&
+	    g_strcasecmp(ext, ".jpeg") != 0 &&
+	    g_strcasecmp(ext, ".png") != 0) {
+		/* No recognized file extension */
+		return FALSE;
+	}
+
+	return cover_name_filter(name, cfg.cover_name_include, TRUE) &&
+	       !cover_name_filter(name, cfg.cover_name_exclude, FALSE);
+}
+
+gchar*
+fileinfo_recursive_get_image(const gchar* path, gint depth)
+{
+	GDir *d;
+
+	if (cfg.recurse_for_cover && depth > cfg.recurse_for_cover_depth)
+		return NULL;
+	
+	d = g_dir_open(path, 0, NULL);
+
+	if (d)
+	{	
+		const gchar *f = g_dir_read_name(d);
+		
+		while (f)
+		{
+			gchar *newpath = g_strdup_printf("%s/%s", path, f);
+
+			if (is_front_cover_image(f))
+			{
+				/* We found a suitable file in the current
+				 * directory, use that. The string will be
+				 * freed by the caller */
+				g_dir_close(d);
+				return newpath;
+			}
+			else
+			{
+				f = g_dir_read_name(d);
+				if (cfg.recurse_for_cover)
+				{
+					/* File/directory wasn't suitable, try and recurse into it.
+					 * This should either return a filename for a image file, 
+					 * or NULL if there was no suitable file, or 'f' wasn't a dir.
+					 */
+					gchar *tmp = fileinfo_recursive_get_image(newpath, depth+1);
+					
+					if(tmp)
+					{
+						g_free(newpath);
+						g_dir_close(d);
+						return tmp;
+					}
+				}
+			}
+		}
+		
+		g_dir_close(d);
+	}
+	
+	return NULL;
+}
+
+void
+filepopup_show_for_tuple(TitleInput *tuple)
+{
+	gchar *tmp;
+	gint x, y, x_off = 3, y_off = 3, h, w;
+
+	if (tuple == NULL)
+		return;
+
+	gtk_widget_realize(filepopup_win);
+
+	filepopup_entry_set_text("label_title", tuple->track_name);
+	filepopup_entry_set_text("label_artist", tuple->performer);
+	filepopup_entry_set_text("label_album", tuple->album_name);
+	filepopup_entry_set_text("label_genre", tuple->genre);
+
+	if (tuple->length != -1)
+		filepopup_entry_set_text_free("label_length", g_strdup_printf("%d:%02d", tuple->length / 60000, (tuple->length / 1000) % 60));
+
+	if (tuple->year != 0)
+		filepopup_entry_set_text_free("label_year", g_strdup_printf("%d", tuple->year));
+
+	if (tuple->track_number != 0)
+		filepopup_entry_set_text_free("label_track", g_strdup_printf("%d", tuple->track_number));
+
+	tmp = fileinfo_recursive_get_image(tuple->file_path, 0);
+	
+	if(tmp)
+	{
+		filepopup_entry_set_image("image_artwork", tmp);
+		g_free(tmp);
+	}
+	
+	gdk_window_get_pointer(NULL, &x, &y, NULL);
+	gtk_window_get_size(GTK_WINDOW(filepopup_win), &w, &h);
+	if (gdk_screen_width()-(w+3) < x) x_off = (w*-1)-3;
+	if (gdk_screen_height()-(h+3) < y) y_off = (h*-1)-3;
+	gtk_window_move(GTK_WINDOW(filepopup_win), x + x_off, y + y_off);
+
+	gtk_widget_show(filepopup_win);
+}
+
+void
+fileinfo_show_for_path(gchar *path)
+{
+	TitleInput *tuple = input_get_song_tuple(path);
+
+	if (tuple == NULL)
+		return input_file_info_box(path);
+
+	fileinfo_show_for_tuple(tuple);
+
+	bmp_title_input_free(tuple);
+	tuple = NULL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/UserInterface/wa2gui/filepopup.h	Thu Sep 07 19:57:27 2006 -0700
@@ -0,0 +1,33 @@
+/*
+ * Audacious: A cross-platform multimedia player
+ * Copyright (c) 2006 William Pitcock, Tony Vroon, George Averill,
+ *                    Giacomo Lozito, Derek Pomery and Yoshiki Yazawa.
+ *
+ * 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 _UI_FILEINFO_H_
+#define _UI_FILEINFO_H_
+
+void create_fileinfo_window(void);
+void create_filepopup_window(void);
+void fileinfo_show_for_tuple(TitleInput *tuple);
+void filepopup_show_for_tuple(TitleInput *tuple);
+gchar* fileinfo_recursive_get_image(const gchar* path, gint depth);
+void fileinfo_show_for_path(gchar *path);
+
+void filepopup_hide(gpointer unused);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/UserInterface/wa2gui/mainwin.c	Thu Sep 07 19:57:27 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/Plugins/UserInterface/wa2gui/mainwin.h	Thu Sep 07 19:57:27 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/UserInterface/wa2gui/playlist.c	Thu Sep 07 19:57:27 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/Plugins/UserInterface/wa2gui/ui_playlist.h	Thu Sep 07 19:57:27 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