Mercurial > audlegacy
changeset 1653:a6e6d3500c13 trunk
[svn] - revert back to r2216
line wrap: on
line diff
--- a/ChangeLog Thu Sep 07 22:00:35 2006 -0700 +++ b/ChangeLog Thu Sep 07 22:26:54 2006 -0700 @@ -1,3 +1,12 @@ +2006-09-08 05:00:35 +0000 William Pitcock <nenolod@nenolod.net> + revision [2235] + - more hammering + + + Changes: Modified: + +4 -4 trunk/audacious/main.c + + 2006-09-08 04:51:36 +0000 William Pitcock <nenolod@nenolod.net> revision [2233] - hammer a little more
--- a/audacious/Makefile Thu Sep 07 22:00:35 2006 -0700 +++ b/audacious/Makefile Thu Sep 07 22:26:54 2006 -0700 @@ -2,7 +2,7 @@ include ../mk/init.mk include ../mk/objective.mk -SUBDIRS = glade images +SUBDIRS = widgets glade images beepincludedir = $(includedir)/audacious @@ -16,7 +16,8 @@ ../sqlite/libsqlite.a \ $(CHARDET_LIBS) \ $(GTK_LIBS) \ - $(LIBGLADE_LIBS) + $(LIBGLADE_LIBS) \ + ./widgets/libwidgets.a CFLAGS += \ $(GTK_CFLAGS) \ @@ -50,9 +51,12 @@ playback.c \ main.c \ logger.c \ + mainwin.c \ skinwin.c \ prefswin.c \ ui_fileinfo.c \ + ui_playlist.c \ + equalizer.c \ glade.c \ hints.c \ about.c credits.c \
--- a/audacious/controlsocket.c Thu Sep 07 22:00:35 2006 -0700 +++ b/audacious/controlsocket.c Thu Sep 07 22:26:54 2006 -0700 @@ -44,12 +44,14 @@ #include <arpa/inet.h> #include "main.h" +#include "equalizer.h" +#include "mainwin.h" #include "input.h" #include "playback.h" #include "playlist.h" +#include "ui_playlist.h" #include "prefswin.h" #include "libaudacious/beepctrl.h" -#include "interface.h" #define CTRLSOCKET_BACKLOG 100 #define CTRLSOCKET_TIMEOUT 100000 @@ -432,6 +434,10 @@ ctrl_write_gint(pkt->fd, b); ctrl_ack_packet(pkt); break; + case CMD_GET_SKIN: + ctrl_write_string(pkt->fd, bmp_active_skin->path); + ctrl_ack_packet(pkt); + break; case CMD_GET_PLAYLIST_FILE: if (pkt->data) { gchar *filename; @@ -750,7 +756,7 @@ mainwin_quit_cb(); break; case CMD_ACTIVATE: - current_interface->present(); + gtk_window_present(GTK_WINDOW(mainwin)); break; default: g_message("Unknown socket command received");
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/equalizer.c Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,1753 @@ +/* Audacious - Cross-platform multimedia player + * Copyright (C) 2005-2006 Audacious development team. + * + * BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "equalizer.h" + +#include <glib.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <stdio.h> +#include <math.h> +#include <string.h> + +#include "platform/smartinclude.h" +#include "widgets/widgetcore.h" +#include "dock.h" +#include "hints.h" +#include "input.h" +#include "main.h" +#include "playlist.h" +#include "ui_playlist.h" +#include "util.h" +#include "output.h" + +#include "libaudacious/rcfile.h" +#include "libaudacious/vfs.h" + +#include "images/audacious_eq.xpm" + +enum PresetViewCols { + PRESET_VIEW_COL_NAME, + PRESET_VIEW_N_COLS +}; + +enum { + EQUALIZER_PRESETS_LOAD_PRESET, + EQUALIZER_PRESETS_LOAD_AUTOPRESET, + EQUALIZER_PRESETS_LOAD_DEFAULT, + EQUALIZER_PRESETS_LOAD_ZERO, + EQUALIZER_PRESETS_LOAD_FROM_FILE, + EQUALIZER_PRESETS_LOAD_FROM_WINAMPFILE, + EQUALIZER_PRESETS_IMPORT_WINAMPFILE, + EQUALIZER_PRESETS_SAVE_PRESET, + EQUALIZER_PRESETS_SAVE_AUTOPRESET, + EQUALIZER_PRESETS_SAVE_DEFAULT, + EQUALIZER_PRESETS_SAVE_TO_FILE, + EQUALIZER_PRESETS_SAVE_TO_WINAMPFILE, + EQUALIZER_PRESETS_DELETE_PRESET, + EQUALIZER_PRESETS_DELETE_AUTOPRESET +}; + + +struct _EqualizerPreset { + gchar *name; + gfloat preamp, bands[10]; +}; + +typedef struct _EqualizerPreset EqualizerPreset; + + +GtkWidget *equalizerwin; + +static GtkWidget *equalizerwin_load_window = NULL; +static GtkWidget *equalizerwin_load_auto_window = NULL; +static GtkWidget *equalizerwin_save_window = NULL; +static GtkWidget *equalizerwin_save_entry; +static GtkWidget *equalizerwin_save_auto_window = NULL; +static GtkWidget *equalizerwin_save_auto_entry; +static GtkWidget *equalizerwin_delete_window = NULL; +static GtkWidget *equalizerwin_delete_auto_window = NULL; + +static GdkPixmap *equalizerwin_bg; +static GdkGC *equalizerwin_gc; + +static GList *equalizerwin_wlist = NULL; + +static GtkAccelGroup *equalizerwin_accel; + +static TButton *equalizerwin_on, *equalizerwin_auto; + +static PButton *equalizerwin_presets, *equalizerwin_shade; +PButton *equalizerwin_close; +static EqGraph *equalizerwin_graph; +static EqSlider *equalizerwin_preamp, *equalizerwin_bands[10]; +static HSlider *equalizerwin_volume, *equalizerwin_balance; + +static GtkItemFactory *equalizerwin_presets_menu; + +static GList *equalizer_presets = NULL, *equalizer_auto_presets = NULL; + + +static void equalizerwin_presets_menu_cb(gpointer cb_data, guint action, + GtkWidget * w); + +static GtkItemFactoryEntry equalizerwin_presets_menu_entries[] = { + {N_("/Load"), NULL, NULL, 0, "<Branch>", NULL}, + {N_("/Load/Preset"), NULL, equalizerwin_presets_menu_cb, + EQUALIZER_PRESETS_LOAD_PRESET, "<Item>", NULL}, + {N_("/Load/Auto-load preset"), NULL, equalizerwin_presets_menu_cb, + EQUALIZER_PRESETS_LOAD_AUTOPRESET, "<Item>", NULL}, + {N_("/Load/Default"), NULL, equalizerwin_presets_menu_cb, + EQUALIZER_PRESETS_LOAD_DEFAULT, "<Item>", NULL}, + {"/Load/-", NULL, NULL, 0, "<Separator>", NULL}, + {N_("/Load/Zero"), NULL, equalizerwin_presets_menu_cb, + EQUALIZER_PRESETS_LOAD_ZERO, "<Item>", NULL}, + {"/Load/-", NULL, NULL, 0, "<Separator>", NULL}, + {N_("/Load/From file"), NULL, equalizerwin_presets_menu_cb, + EQUALIZER_PRESETS_LOAD_FROM_FILE, "<Item>", NULL}, + {N_("/Load/From WinAMP EQF file"), NULL, equalizerwin_presets_menu_cb, + EQUALIZER_PRESETS_LOAD_FROM_WINAMPFILE, "<Item>", NULL}, + {N_("/Import"), NULL, NULL, 0, "<Branch>", NULL}, + {N_("/Import/WinAMP Presets"), NULL, equalizerwin_presets_menu_cb, + EQUALIZER_PRESETS_IMPORT_WINAMPFILE, "<Item>", NULL}, + {N_("/Save"), NULL, NULL, 0, "<Branch>", NULL}, + {N_("/Save/Preset"), NULL, equalizerwin_presets_menu_cb, + EQUALIZER_PRESETS_SAVE_PRESET, "<Item>", NULL}, + {N_("/Save/Auto-load preset"), NULL, equalizerwin_presets_menu_cb, + EQUALIZER_PRESETS_SAVE_AUTOPRESET, "<Item>", NULL}, + {N_("/Save/Default"), NULL, equalizerwin_presets_menu_cb, + EQUALIZER_PRESETS_SAVE_DEFAULT, "<Item>", NULL}, + {"/Save/-", NULL, NULL, 0, "<Separator>", NULL}, + {N_("/Save/To file"), NULL, equalizerwin_presets_menu_cb, + EQUALIZER_PRESETS_SAVE_TO_FILE, "<Item>", NULL}, + {N_("/Save/To WinAMP EQF file"), NULL, equalizerwin_presets_menu_cb, + EQUALIZER_PRESETS_SAVE_TO_WINAMPFILE, "<Item>", NULL}, + {N_("/Delete"), NULL, NULL, 0, "<Branch>", NULL}, + {N_("/Delete/Preset"), NULL, equalizerwin_presets_menu_cb, + EQUALIZER_PRESETS_DELETE_PRESET, "<Item>", NULL}, + {N_("/Delete/Auto-load preset"), NULL, equalizerwin_presets_menu_cb, + EQUALIZER_PRESETS_DELETE_AUTOPRESET, "<Item>", NULL}, +}; + +static gint equalizerwin_presets_menu_entries_num = + G_N_ELEMENTS(equalizerwin_presets_menu_entries); + + +EqualizerPreset * +equalizer_preset_new(const gchar * name) +{ + EqualizerPreset *preset = g_new0(EqualizerPreset, 1); + preset->name = g_strdup(name); + return preset; +} + +void +equalizer_preset_free(EqualizerPreset * preset) +{ + if (!preset) + return; + + g_free(preset->name); + g_free(preset); +} + + +static void +equalizerwin_set_shape_mask(void) +{ + GdkBitmap *mask; + + if (cfg.show_wm_decorations) + return; + + mask = skin_get_mask(bmp_active_skin, SKIN_MASK_EQ + cfg.equalizer_shaded); + gtk_widget_shape_combine_mask(equalizerwin, mask, 0, 0); +} + + +void +equalizerwin_set_shade_menu_cb(gboolean shaded) +{ + cfg.equalizer_shaded = shaded; + + equalizerwin_set_shape_mask(); + + if (shaded) { + dock_shade(dock_window_list, GTK_WINDOW(equalizerwin), 14); + pbutton_set_button_data(equalizerwin_shade, -1, 3, -1, 47); + pbutton_set_skin_index1(equalizerwin_shade, SKIN_EQ_EX); + pbutton_set_button_data(equalizerwin_close, 11, 38, 11, 47); + pbutton_set_skin_index(equalizerwin_close, SKIN_EQ_EX); + widget_show(WIDGET(equalizerwin_volume)); + widget_show(WIDGET(equalizerwin_balance)); + } + else { + dock_shade(dock_window_list, GTK_WINDOW(equalizerwin), 116); + pbutton_set_button_data(equalizerwin_shade, -1, 137, -1, 38); + pbutton_set_skin_index1(equalizerwin_shade, SKIN_EQMAIN); + pbutton_set_button_data(equalizerwin_close, 0, 116, 0, 125); + pbutton_set_skin_index(equalizerwin_close, SKIN_EQMAIN); + widget_hide(WIDGET(equalizerwin_volume)); + widget_hide(WIDGET(equalizerwin_balance)); + } + + draw_equalizer_window(TRUE); +} + +static void +equalizerwin_set_shade(gboolean shaded) +{ + GtkWidget *widget; + widget = gtk_item_factory_get_widget(mainwin_view_menu, + "/Roll up Equalizer"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), shaded); +} + +static void +equalizerwin_shade_toggle(void) +{ + equalizerwin_set_shade(!cfg.equalizer_shaded); +} + +static void +equalizerwin_raise(void) +{ + if (cfg.equalizer_visible) + gtk_window_present(GTK_WINDOW(equalizerwin)); +} + +void +equalizerwin_eq_changed(void) +{ + gint i; + + cfg.equalizer_preamp = eqslider_get_position(equalizerwin_preamp); + for (i = 0; i < 10; i++) + cfg.equalizer_bands[i] = eqslider_get_position(equalizerwin_bands[i]); + /* um .. i think we need both of these for xmms compatibility .. + not sure. -larne */ + input_set_eq(cfg.equalizer_active, cfg.equalizer_preamp, + cfg.equalizer_bands); + output_set_eq(cfg.equalizer_active, cfg.equalizer_preamp, + cfg.equalizer_bands); + + widget_draw(WIDGET(equalizerwin_graph)); +} + +static void +equalizerwin_on_pushed(gboolean toggled) +{ + cfg.equalizer_active = toggled; + equalizerwin_eq_changed(); +} + +static void +equalizerwin_presets_pushed(void) +{ + GdkModifierType modmask; + gint x, y; + + gdk_window_get_pointer(NULL, &x, &y, &modmask); + util_item_factory_popup(equalizerwin_presets_menu, x, y, 1, + GDK_CURRENT_TIME); +} + +static void +equalizerwin_auto_pushed(gboolean toggled) +{ + cfg.equalizer_autoload = toggled; +} + +void +draw_equalizer_window(gboolean force) +{ + gboolean redraw; + + widget_list_lock(equalizerwin_wlist); + + if (force) { + skin_draw_pixmap(bmp_active_skin, equalizerwin_bg, equalizerwin_gc, + SKIN_EQMAIN, 0, 0, 0, 0, 275, 116); + if (gtk_window_has_toplevel_focus(GTK_WINDOW(equalizerwin)) || + !cfg.dim_titlebar) { + if (!cfg.equalizer_shaded) + skin_draw_pixmap(bmp_active_skin, equalizerwin_bg, + equalizerwin_gc, SKIN_EQMAIN, 0, 134, 0, + 0, 275, 14); + else + skin_draw_pixmap(bmp_active_skin, equalizerwin_bg, + equalizerwin_gc, SKIN_EQ_EX, 0, 0, 0, 0, + 275, 14); + } + else { + if (!cfg.equalizer_shaded) + skin_draw_pixmap(bmp_active_skin, equalizerwin_bg, + equalizerwin_gc, SKIN_EQMAIN, 0, 149, 0, + 0, 275, 14); + else + skin_draw_pixmap(bmp_active_skin, equalizerwin_bg, + equalizerwin_gc, SKIN_EQ_EX, 0, 15, 0, 0, + 275, 14); + + } + } + + widget_list_draw(equalizerwin_wlist, &redraw, force); + + if (force || redraw) { + widget_list_clear_redraw(equalizerwin_wlist); + gdk_window_clear(equalizerwin->window); + gdk_flush(); + } + + widget_list_unlock(equalizerwin_wlist); +} + +static gboolean +inside_sensitive_widgets(gint x, gint y) +{ + return (widget_contains(WIDGET(equalizerwin_on), x, y) || + widget_contains(WIDGET(equalizerwin_auto), x, y) || + widget_contains(WIDGET(equalizerwin_presets), x, y) || + widget_contains(WIDGET(equalizerwin_close), x, y) || + widget_contains(WIDGET(equalizerwin_shade), x, y) || + widget_contains(WIDGET(equalizerwin_preamp), x, y) || + widget_contains(WIDGET(equalizerwin_bands[0]), x, y) || + widget_contains(WIDGET(equalizerwin_bands[1]), x, y) || + widget_contains(WIDGET(equalizerwin_bands[2]), x, y) || + widget_contains(WIDGET(equalizerwin_bands[3]), x, y) || + widget_contains(WIDGET(equalizerwin_bands[4]), x, y) || + widget_contains(WIDGET(equalizerwin_bands[5]), x, y) || + widget_contains(WIDGET(equalizerwin_bands[6]), x, y) || + widget_contains(WIDGET(equalizerwin_bands[7]), x, y) || + widget_contains(WIDGET(equalizerwin_bands[8]), x, y) || + widget_contains(WIDGET(equalizerwin_bands[9]), x, y) || + widget_contains(WIDGET(equalizerwin_volume), x, y) || + widget_contains(WIDGET(equalizerwin_balance), x, y)); +} + +gboolean +equalizerwin_press(GtkWidget * widget, GdkEventButton * event, + gpointer callback_data) +{ + gint mx, my; + gboolean grab = TRUE; + + mx = event->x; + my = event->y; + + if (event->button == 1 && event->type == GDK_BUTTON_PRESS && + ((cfg.equalizer_shaded || event->y < 14) && + !inside_sensitive_widgets(event->x, event->y))) { + if (0 && hint_move_resize_available()) { + hint_move_resize(equalizerwin, event->x_root, + event->y_root, TRUE); + grab = FALSE; + } + else { + equalizerwin_raise(); + dock_move_press(dock_window_list, GTK_WINDOW(equalizerwin), event, + FALSE); + } + } + else if (event->button == 1 && event->type == GDK_2BUTTON_PRESS + && event->y < 14) { + equalizerwin_set_shade(!cfg.equalizer_shaded); + if (dock_is_moving(GTK_WINDOW(equalizerwin))) + dock_move_release(GTK_WINDOW(equalizerwin)); + } + else if (event->button == 3 && + !(widget_contains(WIDGET(equalizerwin_on), event->x, event->y) || + widget_contains(WIDGET(equalizerwin_auto), event->x, event->y))) { + /* + * Pop up the main menu a few pixels down to avoid + * anything to be selected initially. + */ + util_item_factory_popup(mainwin_general_menu, event->x_root, + event->y_root + 2, 3, event->time); + grab = FALSE; + } + else { + handle_press_cb(equalizerwin_wlist, widget, event); + draw_equalizer_window(FALSE); + } + if (grab) + gdk_pointer_grab(GDK_WINDOW(equalizerwin->window), FALSE, + GDK_BUTTON_MOTION_MASK | GDK_BUTTON_RELEASE_MASK, + NULL, NULL, GDK_CURRENT_TIME); + + return FALSE; +} + +static void +equalizerwin_scroll(GtkWidget * widget, GdkEventScroll * event, gpointer data) +{ + handle_scroll_cb(equalizerwin_wlist, widget, event); + draw_equalizer_window(FALSE); +} + +static gboolean +equalizerwin_motion(GtkWidget * widget, + GdkEventMotion * event, gpointer callback_data) +{ + GdkEvent *gevent; + + if (dock_is_moving(GTK_WINDOW(equalizerwin))) + { + dock_move_motion(GTK_WINDOW(equalizerwin), event); + } + else + { + handle_motion_cb(equalizerwin_wlist, widget, event); + draw_equalizer_window(FALSE); + } + + gdk_flush(); + + while ((gevent = gdk_event_get()) != NULL) gdk_event_free(gevent); + + return FALSE; +} + +static gboolean +equalizerwin_release(GtkWidget * widget, + GdkEventButton * event, gpointer callback_data) +{ + gdk_pointer_ungrab(GDK_CURRENT_TIME); + gdk_flush(); + + if (dock_is_moving(GTK_WINDOW(equalizerwin))) { + dock_move_release(GTK_WINDOW(equalizerwin)); + } + else { + handle_release_cb(equalizerwin_wlist, widget, event); + draw_equalizer_window(FALSE); + } + + return FALSE; +} + +static gboolean +equalizerwin_focus_in(GtkWidget * widget, + GdkEvent * event, + gpointer data) +{ + equalizerwin_close->pb_allow_draw = TRUE; + equalizerwin_shade->pb_allow_draw = TRUE; + draw_equalizer_window(TRUE); + return TRUE; +} + +static gboolean +equalizerwin_focus_out(GtkWidget * widget, + GdkEventButton * event, + gpointer data) +{ + equalizerwin_close->pb_allow_draw = FALSE; + equalizerwin_shade->pb_allow_draw = FALSE; + draw_equalizer_window(TRUE); + return TRUE; +} + +static gboolean +equalizerwin_keypress(GtkWidget * widget, + GdkEventKey * event, + gpointer data) +{ + if (!cfg.equalizer_shaded) { + gtk_widget_event(mainwin, (GdkEvent *) event); + return TRUE; + } + + switch (event->keyval) { + case GDK_Left: + case GDK_KP_Left: + mainwin_set_balance_diff(-4); + break; + case GDK_Right: + case GDK_KP_Right: + mainwin_set_balance_diff(4); + break; + default: + gtk_widget_event(mainwin, (GdkEvent *) event); + break; + } + + return FALSE; +} + +static gboolean +equalizerwin_configure(GtkWidget * window, + GdkEventConfigure * event, + gpointer data) +{ + if (!GTK_WIDGET_VISIBLE(window)) + return FALSE; + + cfg.equalizer_x = event->x; + cfg.equalizer_y = event->y; + return FALSE; +} + +static void +equalizerwin_set_back_pixmap(void) +{ + gdk_window_set_back_pixmap(equalizerwin->window, equalizerwin_bg, 0); + gdk_window_clear(equalizerwin->window); +} + +static void +equalizerwin_close_cb(void) +{ + equalizerwin_show(FALSE); +} + +static gboolean +equalizerwin_delete(GtkWidget * widget, + gpointer data) +{ + equalizerwin_show(FALSE); + return TRUE; +} + +static GList * +equalizerwin_read_presets(const gchar * basename) +{ + gchar *filename, *name; + RcFile *rcfile; + GList *list = NULL; + gint i, p = 0; + EqualizerPreset *preset; + + filename = g_build_filename(bmp_paths[BMP_PATH_USER_DIR], basename, NULL); + + if ((rcfile = bmp_rcfile_open(filename)) == NULL) { + g_free(filename); + return NULL; + } + + g_free(filename); + + for (;;) { + gchar section[21]; + + g_snprintf(section, sizeof(section), "Preset%d", p++); + if (bmp_rcfile_read_string(rcfile, "Presets", section, &name)) { + preset = g_new0(EqualizerPreset, 1); + preset->name = name; + bmp_rcfile_read_float(rcfile, name, "Preamp", &preset->preamp); + for (i = 0; i < 10; i++) { + gchar band[7]; + g_snprintf(band, sizeof(band), "Band%d", i); + bmp_rcfile_read_float(rcfile, name, band, &preset->bands[i]); + } + list = g_list_prepend(list, preset); + } + else + break; + } + list = g_list_reverse(list); + bmp_rcfile_free(rcfile); + return list; +} + +gint +equalizerwin_volume_frame_cb(gint pos) +{ + if (equalizerwin_volume) { + if (pos < 32) + equalizerwin_volume->hs_knob_nx = + equalizerwin_volume->hs_knob_px = 1; + else if (pos < 63) + equalizerwin_volume->hs_knob_nx = + equalizerwin_volume->hs_knob_px = 4; + else + equalizerwin_volume->hs_knob_nx = + equalizerwin_volume->hs_knob_px = 7; + } + return 1; +} + +static void +equalizerwin_volume_motion_cb(gint pos) +{ + gint v = (gint) rint(pos * 100 / 94.0); + mainwin_adjust_volume_motion(v); + mainwin_set_volume_slider(v); +} + +static void +equalizerwin_volume_release_cb(gint pos) +{ + mainwin_adjust_volume_release(); +} + +static gint +equalizerwin_balance_frame_cb(gint pos) +{ + if (equalizerwin_balance) { + if (pos < 13) + equalizerwin_balance->hs_knob_nx = + equalizerwin_balance->hs_knob_px = 11; + else if (pos < 26) + equalizerwin_balance->hs_knob_nx = + equalizerwin_balance->hs_knob_px = 14; + else + equalizerwin_balance->hs_knob_nx = + equalizerwin_balance->hs_knob_px = 17; + } + + return 1; +} + +static void +equalizerwin_balance_motion_cb(gint pos) +{ + gint b; + pos = MIN(pos, 38); /* The skin uses a even number of pixels + for the balance-slider *sigh* */ + b = (gint) rint((pos - 19) * 100 / 19.0); + mainwin_adjust_balance_motion(b); + mainwin_set_balance_slider(b); +} + +static void +equalizerwin_balance_release_cb(gint pos) +{ + mainwin_adjust_balance_release(); +} + +void +equalizerwin_set_balance_slider(gint percent) +{ + hslider_set_position(equalizerwin_balance, + (gint) rint((percent * 19 / 100.0) + 19)); +} + +void +equalizerwin_set_volume_slider(gint percent) +{ + hslider_set_position(equalizerwin_volume, + (gint) rint(percent * 94 / 100.0)); +} + +static void +equalizerwin_create_widgets(void) +{ + gint i; + + equalizerwin_on = + create_tbutton(&equalizerwin_wlist, equalizerwin_bg, + equalizerwin_gc, 14, 18, 25, 12, 10, 119, 128, 119, + 69, 119, 187, 119, equalizerwin_on_pushed, + SKIN_EQMAIN); + tbutton_set_toggled(equalizerwin_on, cfg.equalizer_active); + equalizerwin_auto = + create_tbutton(&equalizerwin_wlist, equalizerwin_bg, + equalizerwin_gc, 39, 18, 33, 12, 35, 119, 153, 119, + 94, 119, 212, 119, equalizerwin_auto_pushed, + SKIN_EQMAIN); + tbutton_set_toggled(equalizerwin_auto, cfg.equalizer_autoload); + equalizerwin_presets = + create_pbutton(&equalizerwin_wlist, equalizerwin_bg, + equalizerwin_gc, 217, 18, 44, 12, 224, 164, 224, + 176, equalizerwin_presets_pushed, SKIN_EQMAIN); + equalizerwin_close = + create_pbutton(&equalizerwin_wlist, equalizerwin_bg, + equalizerwin_gc, 264, 3, 9, 9, 0, 116, 0, 125, + equalizerwin_close_cb, SKIN_EQMAIN); + equalizerwin_close->pb_allow_draw = FALSE; + + equalizerwin_shade = + create_pbutton_ex(&equalizerwin_wlist, equalizerwin_bg, + equalizerwin_gc, 254, 3, 9, 9, 254, 137, 1, 38, + equalizerwin_shade_toggle, NULL, SKIN_EQMAIN, SKIN_EQ_EX); + equalizerwin_shade->pb_allow_draw = FALSE; + + equalizerwin_graph = + create_eqgraph(&equalizerwin_wlist, equalizerwin_bg, + equalizerwin_gc, 86, 17); + equalizerwin_preamp = + create_eqslider(&equalizerwin_wlist, equalizerwin_bg, + equalizerwin_gc, 21, 38); + eqslider_set_position(equalizerwin_preamp, cfg.equalizer_preamp); + for (i = 0; i < 10; i++) { + equalizerwin_bands[i] = + create_eqslider(&equalizerwin_wlist, equalizerwin_bg, + equalizerwin_gc, 78 + (i * 18), 38); + eqslider_set_position(equalizerwin_bands[i], cfg.equalizer_bands[i]); + } + + equalizerwin_volume = + create_hslider(&equalizerwin_wlist, equalizerwin_bg, + equalizerwin_gc, 61, 4, 97, 8, 1, 30, 1, 30, 3, 7, + 4, 61, 0, 94, equalizerwin_volume_frame_cb, + equalizerwin_volume_motion_cb, + equalizerwin_volume_release_cb, SKIN_EQ_EX); + equalizerwin_balance = + create_hslider(&equalizerwin_wlist, equalizerwin_bg, + equalizerwin_gc, 164, 4, 42, 8, 11, 30, 11, 30, 3, + 7, 4, 164, 0, 39, equalizerwin_balance_frame_cb, + equalizerwin_balance_motion_cb, + equalizerwin_balance_release_cb, SKIN_EQ_EX); + + if (!cfg.equalizer_shaded) { + widget_hide(WIDGET(equalizerwin_volume)); + widget_hide(WIDGET(equalizerwin_balance)); + } + else { + pbutton_set_button_data(equalizerwin_shade, -1, 3, -1, 47); + pbutton_set_skin_index1(equalizerwin_shade, SKIN_EQ_EX); + pbutton_set_button_data(equalizerwin_close, 11, 38, 11, 47); + pbutton_set_skin_index(equalizerwin_close, SKIN_EQ_EX); + } +} + + +static void +equalizerwin_create_window(void) +{ + GdkPixbuf *icon; + gint width, height; + + equalizerwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(equalizerwin), _("Audacious Equalizer")); + gtk_window_set_wmclass(GTK_WINDOW(equalizerwin), "equalizer", "Audacious"); + gtk_window_set_role(GTK_WINDOW(equalizerwin), "equalizer"); + + width = 275; + height = cfg.equalizer_shaded ? 14 : 116; + + gtk_window_set_default_size(GTK_WINDOW(equalizerwin), width, height); + gtk_window_set_resizable(GTK_WINDOW(equalizerwin), FALSE); + + dock_window_list = dock_window_set_decorated(dock_window_list, + GTK_WINDOW(equalizerwin), + cfg.show_wm_decorations); + + /* this will hide only mainwin. it's annoying! yaz */ + gtk_window_set_transient_for(GTK_WINDOW(equalizerwin), + GTK_WINDOW(mainwin)); + gtk_window_set_skip_taskbar_hint(GTK_WINDOW(equalizerwin), TRUE); + + icon = gdk_pixbuf_new_from_xpm_data((const gchar **) bmp_eq_icon); + gtk_window_set_icon(GTK_WINDOW(equalizerwin), icon); + g_object_unref(icon); + + gtk_widget_set_app_paintable(equalizerwin, TRUE); + + if (cfg.equalizer_x != -1 && cfg.save_window_position) + gtk_window_move(GTK_WINDOW(equalizerwin), + cfg.equalizer_x, cfg.equalizer_y); + + gtk_widget_set_events(equalizerwin, + GDK_FOCUS_CHANGE_MASK | GDK_BUTTON_MOTION_MASK | + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_VISIBILITY_NOTIFY_MASK); + gtk_widget_realize(equalizerwin); + + util_set_cursor(equalizerwin); + + g_signal_connect(equalizerwin, "delete_event", + G_CALLBACK(equalizerwin_delete), NULL); + g_signal_connect(equalizerwin, "button_press_event", + G_CALLBACK(equalizerwin_press), NULL); + g_signal_connect(equalizerwin, "button_release_event", + G_CALLBACK(equalizerwin_release), NULL); + g_signal_connect(equalizerwin, "motion_notify_event", + G_CALLBACK(equalizerwin_motion), NULL); + g_signal_connect_after(equalizerwin, "focus_in_event", + G_CALLBACK(equalizerwin_focus_in), NULL); + g_signal_connect_after(equalizerwin, "focus_out_event", + G_CALLBACK(equalizerwin_focus_out), NULL); + g_signal_connect(equalizerwin, "configure_event", + G_CALLBACK(equalizerwin_configure), NULL); + g_signal_connect(equalizerwin, "style_set", + G_CALLBACK(equalizerwin_set_back_pixmap), NULL); + g_signal_connect(equalizerwin, "key_press_event", + G_CALLBACK(equalizerwin_keypress), NULL); + g_signal_connect(equalizerwin, "scroll_event", + G_CALLBACK(equalizerwin_scroll), NULL); +} + +void +equalizerwin_create_popup_menus(void) +{ + equalizerwin_accel = gtk_accel_group_new(); + gtk_window_add_accel_group(GTK_WINDOW(equalizerwin), equalizerwin_accel); + + equalizerwin_presets_menu = create_menu(equalizerwin_presets_menu_entries, + equalizerwin_presets_menu_entries_num, + NULL); +} + +void +equalizerwin_create(void) +{ + equalizer_presets = equalizerwin_read_presets("eq.preset"); + equalizer_auto_presets = equalizerwin_read_presets("eq.auto_preset"); + + equalizerwin_create_window(); + equalizerwin_create_popup_menus(); + + equalizerwin_gc = gdk_gc_new(equalizerwin->window); + equalizerwin_bg = gdk_pixmap_new(equalizerwin->window, 275, 116, -1); + + equalizerwin_create_widgets(); + + equalizerwin_set_back_pixmap(); + gdk_window_set_back_pixmap(equalizerwin->window, equalizerwin_bg, 0); +} + + +void +equalizerwin_show(gboolean show) +{ + GtkWidget *item = gtk_item_factory_get_widget(mainwin_view_menu, + "/Show Equalizer"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), show); +} + +void +equalizerwin_real_show(void) +{ + /* + * This function should only be called from the + * main menu signal handler + */ + + gint x, y; + + gtk_window_get_position(GTK_WINDOW(equalizerwin), &x, &y); + gtk_window_move(GTK_WINDOW(equalizerwin), x, y); + gtk_widget_set_size_request(equalizerwin, 275, + (cfg.equalizer_shaded ? 14 : 116)); + gdk_flush(); + draw_equalizer_window(TRUE); + cfg.equalizer_visible = TRUE; + tbutton_set_toggled(mainwin_eq, TRUE); + + gtk_widget_show(equalizerwin); +} + +void +equalizerwin_real_hide(void) +{ + /* + * This function should only be called from the + * main menu signal handler + */ + gtk_widget_hide(equalizerwin); + cfg.equalizer_visible = FALSE; + tbutton_set_toggled(mainwin_eq, FALSE); +} + +static EqualizerPreset * +equalizerwin_find_preset(GList * list, const gchar * name) +{ + GList *node = list; + EqualizerPreset *preset; + + while (node) { + preset = node->data; + if (!strcasecmp(preset->name, name)) + return preset; + node = g_list_next(node); + } + return NULL; +} + +static void +equalizerwin_write_preset_file(GList * list, const gchar * basename) +{ + gchar *filename, *tmp; + gint i, p; + EqualizerPreset *preset; + RcFile *rcfile; + GList *node; + + rcfile = bmp_rcfile_new(); + p = 0; + for (node = list; node; node = g_list_next(node)) { + preset = node->data; + tmp = g_strdup_printf("Preset%d", p++); + bmp_rcfile_write_string(rcfile, "Presets", tmp, preset->name); + g_free(tmp); + bmp_rcfile_write_float(rcfile, preset->name, "Preamp", + preset->preamp); + for (i = 0; i < 10; i++) { + tmp = g_strdup_printf("Band%d\n", i); + bmp_rcfile_write_float(rcfile, preset->name, tmp, + preset->bands[i]); + g_free(tmp); + } + } + + filename = g_build_filename(bmp_paths[BMP_PATH_USER_DIR], basename, NULL); + bmp_rcfile_write(rcfile, filename); + bmp_rcfile_free(rcfile); + g_free(filename); +} + +static gboolean +equalizerwin_load_preset(GList * list, const gchar * name) +{ + EqualizerPreset *preset; + gint i; + + if ((preset = equalizerwin_find_preset(list, name)) != NULL) { + eqslider_set_position(equalizerwin_preamp, preset->preamp); + for (i = 0; i < 10; i++) + eqslider_set_position(equalizerwin_bands[i], preset->bands[i]); + equalizerwin_eq_changed(); + return TRUE; + } + return FALSE; +} + +static GList * +equalizerwin_save_preset(GList * list, const gchar * name, + const gchar * filename) +{ + gint i; + EqualizerPreset *preset; + + if (!(preset = equalizerwin_find_preset(list, name))) { + preset = g_new0(EqualizerPreset, 1); + preset->name = g_strdup(name); + list = g_list_append(list, preset); + } + + preset->preamp = eqslider_get_position(equalizerwin_preamp); + for (i = 0; i < 10; i++) + preset->bands[i] = eqslider_get_position(equalizerwin_bands[i]); + + equalizerwin_write_preset_file(list, filename); + + return list; +} + +static GList * +equalizerwin_delete_preset(GList * list, gchar * name, gchar * filename) +{ + EqualizerPreset *preset; + GList *node; + + if (!(preset = equalizerwin_find_preset(list, name))) + return list; + + if (!(node = g_list_find(list, preset))) + return list; + + list = g_list_remove_link(list, node); + equalizer_preset_free(preset); + g_list_free_1(node); + + equalizerwin_write_preset_file(list, filename); + + return list; +} + + +static GList * +import_winamp_eqf(VFSFile * file) +{ + gchar header[31]; + gchar name[257]; + gchar bands[11]; + gint i = 0; + GList *list = NULL; + EqualizerPreset *preset; + + vfs_fread(header, 1, 31, file); + if (!strncmp(header, "Winamp EQ library file v1.1", 27)) { + while (vfs_fread(name, 1, 257, file)) { + preset = equalizer_preset_new(name); + preset->preamp = 20.0 - ((bands[10] * 40.0) / 64); + + vfs_fread(bands, 1, 11, file); + + for (i = 0; i < 10; i++) + preset->bands[i] = 20.0 - ((bands[i] * 40.0) / 64); + + list = g_list_prepend(list, preset); + } + } + + list = g_list_reverse(list); + return list; +} + +static void +equalizerwin_read_winamp_eqf(VFSFile * file) +{ + gchar header[31]; + guchar bands[11]; + gint i; + + vfs_fread(header, 1, 31, file); + + if (!strncmp(header, "Winamp EQ library file v1.1", 27)) { + /* Skip name */ + if (vfs_fseek(file, 257, SEEK_CUR) == -1) + return; + + if (vfs_fread(bands, 1, 11, file) != 11) + return; + + eqslider_set_position(equalizerwin_preamp, + 20.0 - ((bands[10] * 40.0) / 63.0)); + + for (i = 0; i < 10; i++) + eqslider_set_position(equalizerwin_bands[i], + 20.0 - ((bands[i] * 40.0) / 64.0)); + } + + equalizerwin_eq_changed(); +} + +static void +equalizerwin_read_bmp_preset(RcFile * rcfile) +{ + gfloat val; + gint i; + + if (bmp_rcfile_read_float(rcfile, "Equalizer preset", "Preamp", &val)) + eqslider_set_position(equalizerwin_preamp, val); + for (i = 0; i < 10; i++) { + gchar tmp[7]; + g_snprintf(tmp, sizeof(tmp), "Band%d", i); + if (bmp_rcfile_read_float(rcfile, "Equalizer preset", tmp, &val)) + eqslider_set_position(equalizerwin_bands[i], val); + } + equalizerwin_eq_changed(); +} + +static void +equalizerwin_save_ok(GtkWidget * widget, gpointer data) +{ + const gchar *text; + + text = gtk_entry_get_text(GTK_ENTRY(equalizerwin_save_entry)); + if (strlen(text) != 0) + equalizer_presets = + equalizerwin_save_preset(equalizer_presets, text, "eq.preset"); + gtk_widget_destroy(equalizerwin_save_window); +} + +static void +equalizerwin_save_select(GtkCList * clist, gint row, + gint column, GdkEventButton * event, gpointer data) +{ + gchar *text; + + gtk_clist_get_text(clist, row, 0, &text); + + gtk_entry_set_text(GTK_ENTRY(equalizerwin_save_entry), text); + if (event->type == GDK_2BUTTON_PRESS) + equalizerwin_save_ok(NULL, NULL); + +} + +static void +equalizerwin_load_ok(GtkWidget * widget, gpointer data) +{ + gchar *text; + GtkCList *clist = GTK_CLIST(data); + + if (clist && clist->selection) { + gtk_clist_get_text(clist, GPOINTER_TO_INT(clist->selection->data), + 0, &text); + equalizerwin_load_preset(equalizer_presets, text); + } + gtk_widget_destroy(equalizerwin_load_window); +} + +static void +equalizerwin_load_apply(GtkWidget * widget, gpointer data) +{ + gchar *text; + GtkCList *clist = GTK_CLIST(data); + + if (clist && clist->selection) { + gtk_clist_get_text(clist, GPOINTER_TO_INT(clist->selection->data), + 0, &text); + equalizerwin_load_preset(equalizer_presets, text); + } +} + + +static void +equalizerwin_load_select(GtkCList * widget, gint row, + gint column, GdkEventButton * event, gpointer data) +{ + if (event->type == GDK_2BUTTON_PRESS) + equalizerwin_load_ok(NULL, widget); +} + +static void +equalizerwin_delete_delete(GtkWidget * widget, gpointer data) +{ + gchar *text; + GList *list, *next; + GtkCList *clist = GTK_CLIST(data); + + g_return_if_fail(clist != NULL); + + list = clist->selection; + gtk_clist_freeze(clist); + while (list) { + next = g_list_next(list); + gtk_clist_get_text(clist, GPOINTER_TO_INT(list->data), 0, &text); + equalizer_auto_presets = + equalizerwin_delete_preset(equalizer_presets, text, "eq.preset"); + gtk_clist_remove(clist, GPOINTER_TO_INT(list->data)); + list = next; + } + gtk_clist_thaw(clist); +} + +static void +equalizerwin_save_auto_ok(GtkWidget * widget, gpointer data) +{ + const gchar *text; + + text = gtk_entry_get_text(GTK_ENTRY(equalizerwin_save_auto_entry)); + if (strlen(text) != 0) + equalizer_auto_presets = + equalizerwin_save_preset(equalizer_auto_presets, text, + "eq.auto_preset"); + gtk_widget_destroy(equalizerwin_save_auto_window); +} + +static void +equalizerwin_save_auto_select(GtkCList * clist, gint row, + gint column, + GdkEventButton * event, gpointer data) +{ + gchar *text; + + gtk_clist_get_text(clist, row, 0, &text); + + gtk_entry_set_text(GTK_ENTRY(equalizerwin_save_auto_entry), text); + if (event->type == GDK_2BUTTON_PRESS) + equalizerwin_save_auto_ok(NULL, NULL); + +} + +static void +equalizerwin_load_auto_ok(GtkWidget * widget, gpointer data) +{ + gchar *text; + GtkCList *clist = GTK_CLIST(data); + + if (clist && clist->selection) { + gtk_clist_get_text(clist, GPOINTER_TO_INT(clist->selection->data), + 0, &text); + equalizerwin_load_preset(equalizer_auto_presets, text); + } + gtk_widget_destroy(equalizerwin_load_auto_window); +} + +static void +equalizerwin_load_auto_select(GtkWidget * widget, gint row, + gint column, + GdkEventButton * event, gpointer data) +{ + if (event->type == GDK_2BUTTON_PRESS) + equalizerwin_load_auto_ok(NULL, widget); +} + +static void +equalizerwin_delete_auto_delete(GtkWidget * widget, gpointer data) +{ + gchar *text; + GList *list, *next; + GtkCList *clist = GTK_CLIST(data); + + g_return_if_fail(clist != NULL); + + list = clist->selection; + gtk_clist_freeze(clist); + while (list) { + next = g_list_next(list); + gtk_clist_get_text(clist, GPOINTER_TO_INT(list->data), 0, &text); + equalizer_auto_presets = + equalizerwin_delete_preset(equalizer_auto_presets, text, + "eq.auto_preset"); + gtk_clist_remove(clist, GPOINTER_TO_INT(list->data)); + list = next; + } + gtk_clist_thaw(clist); +} + + +typedef void (*ResponseHandler)(const gchar *filename); + +static void +equalizerwin_file_chooser_on_response(GtkWidget * dialog, + gint response, + gpointer data) +{ + GtkFileChooser *file_chooser = GTK_FILE_CHOOSER(dialog); + ResponseHandler handler = (ResponseHandler) data; + gchar *filename; + + gtk_widget_hide(dialog); + + switch (response) + { + case GTK_RESPONSE_ACCEPT: + filename = gtk_file_chooser_get_filename(file_chooser); + handler(filename); + g_free(filename); + break; + + case GTK_RESPONSE_REJECT: + break; + } + + gtk_widget_destroy(dialog); +} + + + +static void +load_preset_file(const gchar *filename) +{ + RcFile *rcfile; + + if ((rcfile = bmp_rcfile_open(filename)) != NULL) { + equalizerwin_read_bmp_preset(rcfile); + bmp_rcfile_free(rcfile); + } +} + +static void +load_winamp_file(const gchar * filename) +{ + VFSFile *file; + gchar *tmp; + + if (!(file = vfs_fopen(filename, "rb"))) { + tmp = g_strconcat("Failed to load WinAmp file: ",filename,"\n",NULL); + report_error(tmp); + g_free(tmp); + return; + } + + equalizerwin_read_winamp_eqf(file); + vfs_fclose(file); +} + +static void +import_winamp_file(const gchar * filename) +{ + VFSFile *file; + gchar *tmp; + + if (!(file = vfs_fopen(filename, "rb"))) { + tmp = g_strconcat("Failed to import WinAmp file: ",filename,"\n",NULL); + report_error(tmp); + g_free(tmp); + return; + } + + equalizer_presets = g_list_concat(equalizer_presets, + import_winamp_eqf(file)); + equalizerwin_write_preset_file(equalizer_presets, "eq.preset"); + + vfs_fclose(file); +} + +static void +save_preset_file(const gchar * filename) +{ + RcFile *rcfile; + gint i; + + rcfile = bmp_rcfile_new(); + bmp_rcfile_write_float(rcfile, "Equalizer preset", "Preamp", + eqslider_get_position(equalizerwin_preamp)); + + for (i = 0; i < 10; i++) { + gchar tmp[7]; + g_snprintf(tmp, sizeof(tmp), "Band%d", i); + bmp_rcfile_write_float(rcfile, "Equalizer preset", tmp, + eqslider_get_position(equalizerwin_bands[i])); + } + + bmp_rcfile_write(rcfile, filename); + bmp_rcfile_free(rcfile); +} + +static void +save_winamp_file(const gchar * filename) +{ + VFSFile *file; + + gchar name[257]; + gint i; + guchar bands[11]; + gchar *tmp; + + if (!(file = vfs_fopen(filename, "wb"))) { + tmp = g_strconcat("Failed to save WinAmp file: ",filename,"\n",NULL); + report_error(tmp); + g_free(tmp); + return; + } + + vfs_fwrite("Winamp EQ library file v1.1\x1a!--", 1, 31, file); + + memset(name, 0, 257); + strcpy(name, "Entry1"); + vfs_fwrite(name, 1, 257, file); + + for (i = 0; i < 10; i++) + bands[i] = 63 - (((eqslider_get_position(equalizerwin_bands[i]) + 20) * 63) / 40); + bands[10] = 63 - (((eqslider_get_position(equalizerwin_preamp) + 20) * 63) / 40); + vfs_fwrite(bands, 1, 11, file); + + vfs_fclose(file); +} + +static gint +equalizerwin_list_sort_func(GtkCList * clist, + gconstpointer ptr1, gconstpointer ptr2) +{ + GtkCListRow *row1 = (GtkCListRow *) ptr1; + GtkCListRow *row2 = (GtkCListRow *) ptr2; + + return strcasecmp(GTK_CELL_TEXT(row1->cell[clist->sort_column])->text, + GTK_CELL_TEXT(row2->cell[clist->sort_column])->text); +} + + +static GtkListStore * +preset_list_store_new(GList * preset) +{ + GtkListStore *store; + GtkTreeIter iter; + GList *node; + + store = gtk_list_store_new(PRESET_VIEW_N_COLS, G_TYPE_STRING); + + for (node = preset; node; node = g_list_next(node)) { + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, + PRESET_VIEW_COL_NAME, + &((EqualizerPreset *) node->data)->name, -1); + } + + return store; +} + + +GtkWidget * +preset_view_new(GList * preset) +{ + GtkWidget *treeview; + GtkTreeModel *model; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkListStore *store; + + store = preset_list_store_new(preset); + + model = gtk_tree_model_sort_new_with_model(GTK_TREE_MODEL(store)); + gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(model), + PRESET_VIEW_COL_NAME, + GTK_SORT_ASCENDING); + + treeview = gtk_tree_view_new_with_model(model); + gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(treeview), TRUE); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE); + + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("Preset"), renderer, + "text", + PRESET_VIEW_COL_NAME, + NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + return treeview; +} + + +static GtkWidget * +equalizerwin_create_list_window(GList * preset_list, + const gchar * title, + GtkWidget ** window, + GtkSelectionMode sel_mode, + GtkWidget ** entry, + const gchar * btn2_stock_name, + const gchar * btn3_stock_name, + const gchar * btn1_stock_name, + GCallback btn2_func, + GCallback btn3_func, + GCallback select_row_func) +{ + GtkWidget *vbox, *scrolled_window, *bbox, *btn1, *btn2, *btn3, *clist; + gchar *preset_text[1]; + GList *node; + + *window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(*window), title); + gtk_window_set_type_hint(GTK_WINDOW(*window), GDK_WINDOW_TYPE_HINT_DIALOG); + gtk_window_set_default_size(GTK_WINDOW(*window), 350, 300); + gtk_window_set_position(GTK_WINDOW(*window), GTK_WIN_POS_CENTER); + gtk_container_set_border_width(GTK_CONTAINER(*window), 10); + gtk_window_set_transient_for(GTK_WINDOW(*window), + GTK_WINDOW(equalizerwin)); + g_signal_connect(*window, "destroy", + G_CALLBACK(gtk_widget_destroyed), window); + + vbox = gtk_vbox_new(FALSE, 10); + gtk_container_add(GTK_CONTAINER(*window), vbox); + + scrolled_window = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), + GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); + + preset_text[0] = _("Presets"); + clist = gtk_clist_new_with_titles(1, preset_text); + if (select_row_func) + g_signal_connect(clist, "select_row", + G_CALLBACK(select_row_func), NULL); + gtk_clist_column_titles_passive(GTK_CLIST(clist)); + gtk_clist_set_selection_mode(GTK_CLIST(clist), sel_mode); + + for (node = preset_list; node; node = g_list_next(node)) { + gtk_clist_append(GTK_CLIST(clist), + &((EqualizerPreset *) node->data)->name); + } + gtk_clist_set_compare_func(GTK_CLIST(clist), equalizerwin_list_sort_func); + gtk_clist_sort(GTK_CLIST(clist)); + + gtk_container_add(GTK_CONTAINER(scrolled_window), clist); + gtk_box_pack_start(GTK_BOX(vbox), scrolled_window, TRUE, TRUE, 0); + + if (entry) { + *entry = gtk_entry_new(); + g_signal_connect(*entry, "activate", btn2_func, NULL); + gtk_box_pack_start(GTK_BOX(vbox), *entry, FALSE, FALSE, 0); + } + + bbox = gtk_hbutton_box_new(); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); + gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5); + gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0); + + btn1 = gtk_button_new_from_stock(btn1_stock_name); + g_signal_connect_swapped(btn1, "clicked", + G_CALLBACK(gtk_widget_destroy), + GTK_OBJECT(*window)); + gtk_box_pack_start(GTK_BOX(bbox), btn1, TRUE, TRUE, 0); + + if (btn3_stock_name) { + btn3 = gtk_button_new_from_stock(btn3_stock_name); + g_signal_connect(btn3, "clicked", G_CALLBACK(btn3_func), clist); + gtk_box_pack_start(GTK_BOX(bbox), btn3, TRUE, TRUE, 0); + } + + btn2 = gtk_button_new_from_stock(btn2_stock_name); + g_signal_connect(btn2, "clicked", G_CALLBACK(btn2_func), clist); + GTK_WIDGET_SET_FLAGS(btn2, GTK_CAN_DEFAULT); + + gtk_box_pack_start(GTK_BOX(bbox), btn2, TRUE, TRUE, 0); + + gtk_widget_grab_default(btn2); + + + gtk_widget_show_all(*window); + + return *window; +} + +void +equalizerwin_presets_menu_cb(gpointer cb_data, guint action, GtkWidget * w) +{ + GtkWidget *dialog; + + switch (action) { + case EQUALIZER_PRESETS_LOAD_PRESET: + if (!equalizerwin_load_window) + equalizerwin_create_list_window(equalizer_presets, + _("Load preset"), + &equalizerwin_load_window, + GTK_SELECTION_SINGLE, NULL, + GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_CANCEL, + G_CALLBACK + (equalizerwin_load_ok), + G_CALLBACK + (equalizerwin_load_apply), + G_CALLBACK + (equalizerwin_load_select)); + else + gtk_window_present(GTK_WINDOW(equalizerwin_load_window)); + break; + case EQUALIZER_PRESETS_LOAD_AUTOPRESET: + if (!equalizerwin_load_auto_window) + equalizerwin_create_list_window(equalizer_auto_presets, + _("Load auto-preset"), + &equalizerwin_load_auto_window, + GTK_SELECTION_SINGLE, NULL, + GTK_STOCK_OK, NULL, GTK_STOCK_CANCEL, + G_CALLBACK + (equalizerwin_load_auto_ok), + NULL, + G_CALLBACK + (equalizerwin_load_auto_select)); + else + gtk_window_present(GTK_WINDOW(equalizerwin_load_auto_window)); + break; + case EQUALIZER_PRESETS_LOAD_DEFAULT: + equalizerwin_load_preset(equalizer_presets, "Default"); + break; + case EQUALIZER_PRESETS_LOAD_ZERO: + { + gint i; + + eqslider_set_position(equalizerwin_preamp, 0); + for (i = 0; i < 10; i++) + eqslider_set_position(equalizerwin_bands[i], 0); + equalizerwin_eq_changed(); + break; + } + case EQUALIZER_PRESETS_LOAD_FROM_FILE: + dialog = make_filebrowser(_("Load equalizer preset"), FALSE); + g_signal_connect(dialog , "response", + G_CALLBACK(equalizerwin_file_chooser_on_response), + load_preset_file); + break; + case EQUALIZER_PRESETS_LOAD_FROM_WINAMPFILE: + dialog = make_filebrowser(_("Load equalizer preset"), FALSE); + g_signal_connect(dialog, "response", + G_CALLBACK(equalizerwin_file_chooser_on_response), + load_winamp_file); + break; + + case EQUALIZER_PRESETS_IMPORT_WINAMPFILE: + dialog = make_filebrowser(_("Load equalizer preset"), FALSE); + g_signal_connect(dialog, "response", + G_CALLBACK(equalizerwin_file_chooser_on_response), + import_winamp_file); + break; + + case EQUALIZER_PRESETS_SAVE_PRESET: + if (!equalizerwin_save_window) + equalizerwin_create_list_window(equalizer_presets, + _("Save preset"), + &equalizerwin_save_window, + GTK_SELECTION_SINGLE, + &equalizerwin_save_entry, + GTK_STOCK_OK, NULL, GTK_STOCK_CANCEL, + G_CALLBACK + (equalizerwin_save_ok), + NULL, + G_CALLBACK + (equalizerwin_save_select)); + else + gtk_window_present(GTK_WINDOW(equalizerwin_save_window)); + break; + + case EQUALIZER_PRESETS_SAVE_AUTOPRESET: + { + gchar *name; + + if (!equalizerwin_save_auto_window) + equalizerwin_create_list_window(equalizer_auto_presets, + _("Save auto-preset"), + &equalizerwin_save_auto_window, + GTK_SELECTION_SINGLE, + &equalizerwin_save_auto_entry, + GTK_STOCK_OK, + NULL, + GTK_STOCK_CANCEL, + G_CALLBACK + (equalizerwin_save_auto_ok), + NULL, + G_CALLBACK + (equalizerwin_save_auto_select)); + else + gtk_window_present(GTK_WINDOW(equalizerwin_save_auto_window)); + + name = playlist_get_filename(playlist_get_position()); + + if (name) { + gtk_entry_set_text(GTK_ENTRY(equalizerwin_save_auto_entry), + g_basename(name)); + g_free(name); + } + break; + } + + case EQUALIZER_PRESETS_SAVE_DEFAULT: + equalizer_presets = + equalizerwin_save_preset(equalizer_presets, "Default", + "eq.preset"); + break; + + case EQUALIZER_PRESETS_SAVE_TO_FILE: + { + gchar *songname; + + dialog = make_filebrowser(_("Save equalizer preset"), TRUE); + g_signal_connect(dialog, "response", + G_CALLBACK(equalizerwin_file_chooser_on_response), + save_preset_file); + + songname = playlist_get_filename(playlist_get_position()); + if (songname) { + gchar *eqname = g_strdup_printf("%s.%s", songname, + cfg.eqpreset_extension); + g_free(songname); + gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), + eqname); + g_free(eqname); + } + + break; + } + + case EQUALIZER_PRESETS_SAVE_TO_WINAMPFILE: + dialog = make_filebrowser(_("Save equalizer preset"), TRUE); + g_signal_connect(dialog, "response", + G_CALLBACK(equalizerwin_file_chooser_on_response), + save_winamp_file); + break; + + case EQUALIZER_PRESETS_DELETE_PRESET: + if (!equalizerwin_delete_window) + equalizerwin_create_list_window(equalizer_presets, + _("Delete preset"), + &equalizerwin_delete_window, + GTK_SELECTION_EXTENDED, NULL, + GTK_STOCK_DELETE, + NULL, + GTK_STOCK_CLOSE, + G_CALLBACK + (equalizerwin_delete_delete), + NULL, + NULL); + else + gtk_window_present(GTK_WINDOW(equalizerwin_delete_window)); + + break; + + case EQUALIZER_PRESETS_DELETE_AUTOPRESET: + if (!equalizerwin_delete_auto_window) + equalizerwin_create_list_window(equalizer_auto_presets, + _("Delete auto-preset"), + &equalizerwin_delete_auto_window, + GTK_SELECTION_EXTENDED, NULL, + GTK_STOCK_DELETE, + NULL, + GTK_STOCK_CLOSE, + G_CALLBACK + (equalizerwin_delete_auto_delete), + NULL, + NULL); + else + gtk_window_present(GTK_WINDOW(equalizerwin_delete_auto_window)); + + break; + } +} + +void +equalizerwin_load_auto_preset(const gchar * filename) +{ + gchar *presetfilename, *directory; + RcFile *rcfile; + + g_return_if_fail(filename != NULL); + + if (!cfg.equalizer_autoload) + return; + + presetfilename = g_strconcat(filename, ".", cfg.eqpreset_extension, NULL); + + /* First try to find a per file preset file */ + if (strlen(cfg.eqpreset_extension) > 0 && + (rcfile = bmp_rcfile_open(presetfilename)) != NULL) { + g_free(presetfilename); + equalizerwin_read_bmp_preset(rcfile); + bmp_rcfile_free(rcfile); + return; + } + + g_free(presetfilename); + + directory = g_path_get_dirname(filename); + presetfilename = g_build_filename(directory, cfg.eqpreset_default_file, + NULL); + g_free(directory); + + /* Try to find a per directory preset file */ + if (strlen(cfg.eqpreset_default_file) > 0 && + (rcfile = bmp_rcfile_open(presetfilename)) != NULL) { + equalizerwin_read_bmp_preset(rcfile); + bmp_rcfile_free(rcfile); + } + else if (!equalizerwin_load_preset + (equalizer_auto_presets, g_basename(filename))) { + /* Fall back to the oldstyle auto presets */ + equalizerwin_load_preset(equalizer_presets, "Default"); + } + + g_free(presetfilename); +} + +void +equalizerwin_set_preamp(gfloat preamp) +{ + eqslider_set_position(equalizerwin_preamp, preamp); + equalizerwin_eq_changed(); +} + +void +equalizerwin_set_band(gint band, gfloat value) +{ + g_return_if_fail(band >= 0 && band < 10); + eqslider_set_position(equalizerwin_bands[band], value); +} + +gfloat +equalizerwin_get_preamp(void) +{ + return eqslider_get_position(equalizerwin_preamp); +} + +gfloat +equalizerwin_get_band(gint band) +{ + g_return_val_if_fail(band >= 0 && band < 10, 0); + return eqslider_get_position(equalizerwin_bands[band]); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/equalizer.h Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,60 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef EQUALIZER_H +#define EQUALIZER_H + +#include <glib.h> +#include <gtk/gtk.h> + +#include "widgets/widgetcore.h" + +#define EQUALIZER_HEIGHT (gint)(cfg.equalizer_shaded ? 14 : 116) +#define EQUALIZER_WIDTH (gint)275 + +#define EQUALIZER_DEFAULT_POS_X 20 +#define EQUALIZER_DEFAULT_POS_Y 136 + +#define EQUALIZER_DEFAULT_DIR_PRESET "dir_default.preset" +#define EQUALIZER_DEFAULT_PRESET_EXT "preset" + +void equalizerwin_set_shade_menu_cb(gboolean shaded); +void draw_equalizer_window(gboolean force); +void equalizerwin_create(void); +void equalizerwin_show(gboolean show); +void equalizerwin_real_show(void); +void equalizerwin_real_hide(void); +void equalizerwin_load_auto_preset(const gchar * filename); +void equalizerwin_set_volume_slider(gint percent); +void equalizerwin_set_balance_slider(gint percent); +void equalizerwin_eq_changed(void); +void equalizerwin_set_preamp(gfloat preamp); +void equalizerwin_set_band(gint band, gfloat value); +gfloat equalizerwin_get_preamp(void); +gfloat equalizerwin_get_band(gint band); + +gboolean equalizerwin_has_focus(void); + +extern GtkWidget *equalizerwin; +extern PButton *equalizerwin_close; +extern gboolean equalizerwin_focus; + +#endif
--- a/audacious/genevent.c Thu Sep 07 22:00:35 2006 -0700 +++ b/audacious/genevent.c Thu Sep 07 22:26:54 2006 -0700 @@ -52,13 +52,16 @@ #include "controlsocket.h" #include "dnd.h" #include "effect.h" +#include "equalizer.h" #include "general.h" #include "hints.h" #include "input.h" #include "logger.h" +#include "mainwin.h" #include "output.h" #include "playback.h" #include "playlist.h" +#include "ui_playlist.h" #include "pluginenum.h" #include "prefswin.h" #include "skinwin.h" @@ -67,8 +70,6 @@ gboolean ev_waiting = FALSE; -#if 0 -/* *** TO WA2GUI *** */ static gboolean idle_func_change_song(gboolean waiting) { @@ -120,7 +121,6 @@ return waiting; } -#endif gint audcore_generic_events(void) @@ -135,9 +135,7 @@ switch (time) { case -1: /* no song playing */ -#if 0 ev_waiting = idle_func_change_song(ev_waiting); -#endif break; default:
--- a/audacious/genevent.h Thu Sep 07 22:00:35 2006 -0700 +++ b/audacious/genevent.h Thu Sep 07 22:26:54 2006 -0700 @@ -25,6 +25,8 @@ #ifndef GENEVENT_H #define GENEVENT_H +#include "mainwin.h" + extern gboolean ev_waiting; gint audcore_generic_events(void);
--- a/audacious/input.c Thu Sep 07 22:00:35 2006 -0700 +++ b/audacious/input.c Thu Sep 07 22:26:54 2006 -0700 @@ -31,12 +31,13 @@ #include "fft.h" #include "input.h" #include "main.h" +#include "mainwin.h" #include "output.h" #include "util.h" #include "visualization.h" #include "playback.h" +#include "widgets/widgetcore.h" #include "pluginenum.h" -#include "interface.h" #include "libaudacious/titlestring.h" #include "libaudacious/util.h" @@ -314,7 +315,7 @@ GtkCellRenderer *renderer; dialog = - gtk_message_dialog_new_with_markup(GTK_WINDOW(current_interface->parentwin), + gtk_message_dialog_new_with_markup(GTK_WINDOW(mainwin), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, @@ -737,13 +738,15 @@ gchar * input_get_info_text(void) { - return current_interface->get_status_text(); + return g_strdup(input_info_text); } void input_set_info_text(const gchar * text) { - current_interface->set_status_text(text); + g_free(input_info_text); + input_info_text = g_strdup(text); + mainwin_set_info_text(); } void @@ -755,15 +758,11 @@ if (!get_current_input_plugin()) return; - current_interface->buffering_notify(ip_data.buffering); - -#if 0 ip_data.buffering = status; if (ip_data.buffering == TRUE && mainwin_playstatus->ps_status == STATUS_STOP) mainwin_playstatus->ps_status = STATUS_PLAY; playstatus_set_status_buffering(mainwin_playstatus, ip_data.buffering); -#endif } void
--- a/audacious/interface.h Thu Sep 07 22:00:35 2006 -0700 +++ b/audacious/interface.h Thu Sep 07 22:26:54 2006 -0700 @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2006 William Pitcock <nenolod -at- nenolod.net> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef _INTERFACE_H_ -#define _INTERFACE_H_ - -#include <glib.h> - -struct _InterfacePlugin { - gchar *name; - GtkWidget *parentwin; - - void (*init) (void); - void (*cleanup) (void); - void (*about) (void); - void (*configure) (void); - void (*disable_plugin) (struct _VisPlugin *); - void (*playback_start) (void); - void (*playback_stop) (void); - void (*clear_pcm_data) (void); - void (*send_pcm_data) (gint16 pcm_data[2][512], gint nch, gint length); - void (*redraw) (void); - gboolean (*idle_callback) (gboolean waiting); - void (*playstatus_notify) (gint status); - void (*buffering_notify) (gint status); - void (*set_status_text) (gchar *text); - gchar *(*get_status_text) (void); - void (*set_song_info) (gint, gint, gint); - void (*present) (void); -}; - -typedef struct _InterfacePlugin InterfacePlugin; - -extern void register_interface_plugin(InterfacePlugin *); -extern void start_interface_plugin(InterfacePlugin *); - -extern InterfacePlugin *current_interface; - -#endif
--- a/audacious/main.c Thu Sep 07 22:00:35 2006 -0700 +++ b/audacious/main.c Thu Sep 07 22:26:54 2006 -0700 @@ -54,14 +54,17 @@ #include "controlsocket.h" #include "dnd.h" #include "effect.h" +#include "equalizer.h" #include "general.h" #include "genevent.h" #include "hints.h" #include "input.h" #include "logger.h" +#include "mainwin.h" #include "output.h" #include "playback.h" #include "playlist.h" +#include "ui_playlist.h" #include "pluginenum.h" #include "prefswin.h" #include "skinwin.h" @@ -80,6 +83,7 @@ const gchar *application_name = N_("Audacious"); + struct _BmpCmdLineOpt { GList *filenames; gint session; @@ -532,10 +536,10 @@ } if (!cfg.playlist_font) - cfg.playlist_font = g_strdup("Sans Bold 8"); + cfg.playlist_font = g_strdup(PLAYLISTWIN_DEFAULT_FONT); if (!cfg.mainwin_font) - cfg.mainwin_font = g_strdup("Sans Bold 9"); + cfg.mainwin_font = g_strdup(MAINWIN_DEFAULT_FONT); if (!cfg.gentitle_format) cfg.gentitle_format = g_strdup("%{p:%p - %}%{a:%a - %}%t"); @@ -553,10 +557,10 @@ } if (!cfg.eqpreset_default_file) - cfg.eqpreset_default_file = g_strdup("dir_default.preset"); + cfg.eqpreset_default_file = g_strdup(EQUALIZER_DEFAULT_DIR_PRESET); if (!cfg.eqpreset_extension) - cfg.eqpreset_extension = g_strdup("preset"); + cfg.eqpreset_extension = g_strdup(EQUALIZER_DEFAULT_PRESET_EXT); if (!cfg.cover_name_include) cfg.cover_name_include = g_strdup(""); @@ -1057,14 +1061,10 @@ gtk_accel_map_load(bmp_paths[BMP_PATH_ACCEL_FILE]); - current_interface->init(); -#if 0 - /* *** TO WA2GUI *** */ mainwin_create(); playlistwin_create(); equalizerwin_create(); -#endif if (!init_skins(cfg.skin)) { run_load_skin_error_dialog(cfg.skin); @@ -1079,14 +1079,11 @@ playlist_load(bmp_paths[BMP_PATH_PLAYLIST_FILE]); playlist_set_position(cfg.playlist_position); -#if 0 - /* *** TO WA2GUI *** */ /* this needs to be called after all 3 windows are created and * input plugins are setup'ed * but not if we're running headless --nenolod */ mainwin_setup_menus(); -#endif if (options.headless != 1) GDK_THREADS_LEAVE(); @@ -1109,7 +1106,6 @@ create_fileinfo_window(); create_filepopup_window(); -#if 0 if (cfg.player_visible) mainwin_show(TRUE); else if (!cfg.playlist_visible && !cfg.equalizer_visible) @@ -1122,9 +1118,6 @@ playlistwin_show(); hint_set_always(cfg.always_on_top); -#endif - - current_interface->present(); playlist_start_get_info_thread(); mainwin_attach_idle_func();
--- a/audacious/main.h Thu Sep 07 22:00:35 2006 -0700 +++ b/audacious/main.h Thu Sep 07 22:26:54 2006 -0700 @@ -22,8 +22,8 @@ #ifndef MAIN_H #define MAIN_H -#include <glib.h> -#include <glib/gi18n.h> +#include "widgets/widgetcore.h" +#include "mainwin.h" #include <sys/types.h> #include <sys/stat.h>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/mainwin.c Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,3570 @@ +/* Audacious - Cross-platform multimedia player + * Copyright (C) 2005-2006 Audacious development team. + * + * BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + + +#include <glib.h> +#include <glib/gi18n.h> +#include <glib/gprintf.h> +#include <gtk/gtk.h> +#include <gtk/gtkmessagedialog.h> + +/* GDK including */ +#include "platform/smartinclude.h" + +#include <math.h> +#include <stdlib.h> +#include <string.h> + +#include <X11/Xlib.h> + +#include "widgets/widgetcore.h" +#include "mainwin.h" +#include "pixmaps.h" + +#include "main.h" + +#include "controlsocket.h" +#include "pluginenum.h" + +#include "credits.h" +#include "dnd.h" +#include "dock.h" +#include "equalizer.h" +#include "hints.h" +#include "input.h" +#include "ui_playlist.h" +#include "prefswin.h" +#include "skinwin.h" +#include "genevent.h" +#include "playback.h" +#include "playlist.h" +#include "urldecode.h" +#include "util.h" +#include "visualization.h" +#include "libaudacious/configdb.h" + +static GTimeVal cb_time; /* click delay for tristate is defined by TRISTATE_THRESHOLD */ + +#define ITEM_SEPARATOR {"/-", NULL, NULL, 0, "<Separator>"} +#define TRISTATE_THRESHOLD 200 + +/* + * If you change the menu above change these defines also + */ + +#define MAINWIN_VIS_MENU_VIS_MODE 1 +#define MAINWIN_VIS_MENU_NUM_VIS_MODE 3 +#define MAINWIN_VIS_MENU_ANALYZER_MODE 5 +#define MAINWIN_VIS_MENU_NUM_ANALYZER_MODE 3 +#define MAINWIN_VIS_MENU_ANALYZER_TYPE 9 +#define MAINWIN_VIS_MENU_NUM_ANALYZER_TYPE 2 +#define MAINWIN_VIS_MENU_ANALYZER_PEAKS 12 +#define MAINWIN_VIS_MENU_SCOPE_MODE 14 +#define MAINWIN_VIS_MENU_NUM_SCOPE_MODE 3 +#define MAINWIN_VIS_MENU_WSHADEVU_MODE 18 +#define MAINWIN_VIS_MENU_NUM_WSHADEVU_MODE 2 +#define MAINWIN_VIS_MENU_REFRESH_RATE 21 +#define MAINWIN_VIS_MENU_NUM_REFRESH_RATE 4 +#define MAINWIN_VIS_MENU_AFALLOFF 26 +#define MAINWIN_VIS_MENU_NUM_AFALLOFF 5 +#define MAINWIN_VIS_MENU_PFALLOFF 32 +#define MAINWIN_VIS_MENU_NUM_PFALLOFF 5 + +#define VOLSET_DISP_TIMES 5 + +enum { + MAINWIN_SEEK_REV = -1, + MAINWIN_SEEK_NIL, + MAINWIN_SEEK_FWD +}; + +enum { + MAINWIN_SONGNAME_FILEINFO, + MAINWIN_SONGNAME_JTF, + MAINWIN_SONGNAME_JTT, + MAINWIN_SONGNAME_SCROLL, + MAINWIN_SONGNAME_STOPAFTERSONG +}; + +enum { + MAINWIN_OPT_SKIN, MAINWIN_OPT_RELOADSKIN, + MAINWIN_OPT_REPEAT, MAINWIN_OPT_SHUFFLE, MAINWIN_OPT_NPA, + MAINWIN_OPT_TELAPSED, MAINWIN_OPT_TREMAINING, + MAINWIN_OPT_ALWAYS, + MAINWIN_OPT_STICKY, + MAINWIN_OPT_WS, + MAINWIN_OPT_PWS, + MAINWIN_OPT_EQWS +}; + +enum { + MAINWIN_VIS_ANALYZER, MAINWIN_VIS_SCOPE, MAINWIN_VIS_OFF, + MAINWIN_VIS_ANALYZER_NORMAL, MAINWIN_VIS_ANALYZER_FIRE, + MAINWIN_VIS_ANALYZER_VLINES, + MAINWIN_VIS_ANALYZER_LINES, MAINWIN_VIS_ANALYZER_BARS, + MAINWIN_VIS_ANALYZER_PEAKS, + MAINWIN_VIS_SCOPE_DOT, MAINWIN_VIS_SCOPE_LINE, MAINWIN_VIS_SCOPE_SOLID, + MAINWIN_VIS_VU_NORMAL, MAINWIN_VIS_VU_SMOOTH, + MAINWIN_VIS_REFRESH_FULL, MAINWIN_VIS_REFRESH_HALF, + MAINWIN_VIS_REFRESH_QUARTER, MAINWIN_VIS_REFRESH_EIGHTH, + MAINWIN_VIS_AFALLOFF_SLOWEST, MAINWIN_VIS_AFALLOFF_SLOW, + MAINWIN_VIS_AFALLOFF_MEDIUM, MAINWIN_VIS_AFALLOFF_FAST, + MAINWIN_VIS_AFALLOFF_FASTEST, + MAINWIN_VIS_PFALLOFF_SLOWEST, MAINWIN_VIS_PFALLOFF_SLOW, + MAINWIN_VIS_PFALLOFF_MEDIUM, MAINWIN_VIS_PFALLOFF_FAST, + MAINWIN_VIS_PFALLOFF_FASTEST, + MAINWIN_VIS_PLUGINS +}; + +enum { + MAINWIN_VIS_ACTIVE_MAINWIN, MAINWIN_VIS_ACTIVE_PLAYLISTWIN +}; + + +typedef struct _PlaybackInfo PlaybackInfo; + +struct _PlaybackInfo { + gchar *title; + gint bitrate; + gint frequency; + gint n_channels; +}; + + +GtkWidget *mainwin = NULL; +GtkWidget *err = NULL; /* an error dialog for miscellaneous error messages */ + +static GdkBitmap *nullmask; +static gint balance; + +GtkWidget *mainwin_jtf = NULL; +static GtkWidget *mainwin_jtt = NULL; + +GtkItemFactory *mainwin_songname_menu, *mainwin_vis_menu; +GtkItemFactory *mainwin_general_menu, *mainwin_play_menu, *mainwin_add_menu; +GtkItemFactory *mainwin_view_menu; + +gint seek_state = MAINWIN_SEEK_NIL; +gint seek_initial_pos = 0; + +GdkGC *mainwin_gc; +static GdkPixmap *mainwin_bg = NULL; + +GtkAccelGroup *mainwin_accel = NULL; + +static PButton *mainwin_menubtn; +static PButton *mainwin_minimize, *mainwin_shade, *mainwin_close; + +static PButton *mainwin_rew, *mainwin_fwd; +static PButton *mainwin_eject; +static PButton *mainwin_play, *mainwin_pause, *mainwin_stop; + +TButton *mainwin_shuffle, *mainwin_repeat, *mainwin_eq, *mainwin_pl; +TextBox *mainwin_info; +TextBox *mainwin_stime_min, *mainwin_stime_sec; + +static TextBox *mainwin_rate_text, *mainwin_freq_text, + *mainwin_othertext; + +PlayStatus *mainwin_playstatus; + +Number *mainwin_minus_num, *mainwin_10min_num, *mainwin_min_num; +Number *mainwin_10sec_num, *mainwin_sec_num; + +static gboolean setting_volume = FALSE; + +Vis *active_vis; +Vis *mainwin_vis; +SVis *mainwin_svis; + +HSlider *mainwin_sposition = NULL; + +static MenuRow *mainwin_menurow; +static HSlider *mainwin_volume, *mainwin_balance, *mainwin_position; +static MonoStereo *mainwin_monostereo; +static SButton *mainwin_srew, *mainwin_splay, *mainwin_spause; +static SButton *mainwin_sstop, *mainwin_sfwd, *mainwin_seject, *mainwin_about; + +static GList *mainwin_wlist = NULL; + +static gint mainwin_timeout_id; + +G_LOCK_DEFINE_STATIC(mainwin_title); + +static gboolean mainwin_force_redraw = FALSE; +static gchar *mainwin_title_text = NULL; +static gboolean mainwin_info_text_locked = FALSE; + + +static void mainwin_songname_menu_callback(gpointer user_data, + guint action, + GtkWidget * widget); + +static void mainwin_vis_menu_callback(gpointer user_data, + guint action, + GtkWidget * widget); + +static void mainwin_view_menu_callback(gpointer user_data, + guint action, + GtkWidget * widget); + +static void mainwin_play_menu_callback(gpointer user_data, + guint action, + GtkWidget * widget); + +/* Song name area menu */ + +GtkItemFactoryEntry mainwin_songname_menu_entries[] = { + {N_("/View Track Details"), "<alt>i", mainwin_general_menu_callback, + MAINWIN_GENERAL_FILEINFO, "<ImageItem>", my_pixbuf}, + {N_("/Jump to File"), "J", mainwin_songname_menu_callback, + MAINWIN_SONGNAME_JTF, "<StockItem>", GTK_STOCK_JUMP_TO}, + {"/-", NULL, NULL, 0, "<Separator>", NULL}, + {N_("/Autoscroll Songname"), NULL, mainwin_songname_menu_callback, + MAINWIN_SONGNAME_SCROLL, "<ToggleItem>", NULL}, + {N_("/Stop After Current Song"), "<control>M", mainwin_songname_menu_callback, + MAINWIN_SONGNAME_STOPAFTERSONG, "<ToggleItem>", NULL}, +}; + +static gint mainwin_songname_menu_entries_num = + G_N_ELEMENTS(mainwin_songname_menu_entries); + +/* Mini-visualizer area menu */ + +GtkItemFactoryEntry mainwin_vis_menu_entries[] = { + {N_("/Visualization Mode"), NULL, NULL, 0, "<Branch>", NULL}, + {N_("/Visualization Mode/Analyzer"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_ANALYZER, "<RadioItem>", NULL}, + {N_("/Visualization Mode/Scope"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_SCOPE, "/Visualization Mode/Analyzer", NULL}, + {N_("/Visualization Mode/Off"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_OFF, "/Visualization Mode/Analyzer", NULL}, + {N_("/Analyzer Mode"), NULL, NULL, 0, "<Branch>", NULL}, + {N_("/Analyzer Mode/Normal"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_ANALYZER_NORMAL, "<RadioItem>", NULL}, + {N_("/Analyzer Mode/Fire"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_ANALYZER_FIRE, "/Analyzer Mode/Normal", NULL}, + {N_("/Analyzer Mode/Vertical Lines"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_ANALYZER_VLINES, "/Analyzer Mode/Normal", NULL}, + {"/Analyzer Mode/-", NULL, NULL, 0, "<Separator>", NULL}, + {N_("/Analyzer Mode/Lines"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_ANALYZER_LINES, "<RadioItem>", NULL}, + {N_("/Analyzer Mode/Bars"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_ANALYZER_BARS, "/Analyzer Mode/Lines", NULL}, + {"/Analyzer Mode/-", NULL, NULL, 0, "<Separator>", NULL}, + {N_("/Analyzer Mode/Peaks"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_ANALYZER_PEAKS, "<ToggleItem>", NULL}, + {N_("/Scope Mode"), NULL, NULL, 0, "<Branch>", NULL}, + {N_("/Scope Mode/Dot Scope"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_SCOPE_DOT, "<RadioItem>", NULL}, + {N_("/Scope Mode/Line Scope"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_SCOPE_LINE, "/Scope Mode/Dot Scope", NULL}, + {N_("/Scope Mode/Solid Scope"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_SCOPE_SOLID, "/Scope Mode/Dot Scope", NULL}, + {N_("/WindowShade VU Mode"), NULL, NULL, 0, "<Branch>", NULL}, + {N_("/WindowShade VU Mode/Normal"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_VU_NORMAL, "<RadioItem>", NULL}, + {N_("/WindowShade VU Mode/Smooth"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_VU_SMOOTH, "/WindowShade VU Mode/Normal", NULL}, + {N_("/Refresh Rate"), NULL, NULL, 0, "<Branch>", NULL}, + {N_("/Refresh Rate/Full (~50 fps)"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_REFRESH_FULL, "<RadioItem>", NULL}, + {N_("/Refresh Rate/Half (~25 fps)"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_REFRESH_HALF, "/Refresh Rate/Full (~50 fps)", NULL}, + {N_("/Refresh Rate/Quarter (~13 fps)"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_REFRESH_QUARTER, "/Refresh Rate/Full (~50 fps)", NULL}, + {N_("/Refresh Rate/Eighth (~6 fps)"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_REFRESH_EIGHTH, "/Refresh Rate/Full (~50 fps)", NULL}, + {N_("/Analyzer Falloff"), NULL, NULL, 0, "<Branch>", NULL}, + {N_("/Analyzer Falloff/Slowest"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_AFALLOFF_SLOWEST, "<RadioItem>", NULL}, + {N_("/Analyzer Falloff/Slow"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_AFALLOFF_SLOW, "/Analyzer Falloff/Slowest", NULL}, + {N_("/Analyzer Falloff/Medium"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_AFALLOFF_MEDIUM, "/Analyzer Falloff/Slowest", NULL}, + {N_("/Analyzer Falloff/Fast"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_AFALLOFF_FAST, "/Analyzer Falloff/Slowest", NULL}, + {N_("/Analyzer Falloff/Fastest"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_AFALLOFF_FASTEST, "/Analyzer Falloff/Slowest", NULL}, + {N_("/Peaks Falloff"), NULL, NULL, 0, "<Branch>", NULL}, + {N_("/Peaks Falloff/Slowest"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_PFALLOFF_SLOWEST, "<RadioItem>", NULL}, + {N_("/Peaks Falloff/Slow"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_PFALLOFF_SLOW, "/Peaks Falloff/Slowest", NULL}, + {N_("/Peaks Falloff/Medium"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_PFALLOFF_MEDIUM, "/Peaks Falloff/Slowest", NULL}, + {N_("/Peaks Falloff/Fast"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_PFALLOFF_FAST, "/Peaks Falloff/Slowest", NULL}, + {N_("/Peaks Falloff/Fastest"), NULL, mainwin_vis_menu_callback, + MAINWIN_VIS_PFALLOFF_FASTEST, "/Peaks Falloff/Slowest", NULL} +}; + +static const gint mainwin_vis_menu_entries_num = + G_N_ELEMENTS(mainwin_vis_menu_entries); + +/* Playback menu (now used only for accelerators) */ + +GtkItemFactoryEntry mainwin_playback_menu_entries[] = { + {N_("/Play CD"), "<alt>C", mainwin_general_menu_callback, + MAINWIN_GENERAL_PLAYCD, "<StockItem>", GTK_STOCK_CDROM}, + {"/-", NULL, NULL, 0, "<Separator>", NULL}, + {N_("/Repeat"), "R", mainwin_play_menu_callback, + MAINWIN_OPT_REPEAT, "<ToggleItem>", NULL}, + {N_("/Shuffle"), "S", mainwin_play_menu_callback, + MAINWIN_OPT_SHUFFLE, "<ToggleItem>", NULL}, + {N_("/No Playlist Advance"), "<control>N", mainwin_play_menu_callback, + MAINWIN_OPT_NPA, "<ToggleItem>", NULL}, + {N_("/Stop After Current Song"), "<control>M", mainwin_songname_menu_callback, + MAINWIN_SONGNAME_STOPAFTERSONG, "<ToggleItem>", NULL}, + {"/-", NULL, NULL, 0, "<Separator>", NULL}, + {N_("/Play"), "x", mainwin_general_menu_callback, + MAINWIN_GENERAL_PLAY, "<StockItem>", GTK_STOCK_MEDIA_PLAY}, + {N_("/Pause"), "c", mainwin_general_menu_callback, + MAINWIN_GENERAL_PAUSE, "<StockItem>", GTK_STOCK_MEDIA_PAUSE}, + {N_("/Stop"), "v", mainwin_general_menu_callback, + MAINWIN_GENERAL_STOP, "<StockItem>", GTK_STOCK_MEDIA_STOP}, + {N_("/Previous"), "z", mainwin_general_menu_callback, + MAINWIN_GENERAL_PREV, "<StockItem>", GTK_STOCK_MEDIA_PREVIOUS}, + {N_("/Next"), "b", mainwin_general_menu_callback, + MAINWIN_GENERAL_NEXT, "<StockItem>", GTK_STOCK_MEDIA_NEXT}, + {"/-", NULL, NULL, 0, "<Separator>", NULL}, + {N_("/Jump to Playlist Start"), "<control>Z", mainwin_general_menu_callback, + MAINWIN_GENERAL_START, "<StockItem>", GTK_STOCK_GOTO_TOP}, + {"/-", NULL, NULL, 0, "<Separator>", NULL}, + {N_("/Jump to File"), "J", mainwin_general_menu_callback, + MAINWIN_GENERAL_JTF, "<StockItem>", GTK_STOCK_JUMP_TO}, + {N_("/Jump to Time"), "<control>J", mainwin_general_menu_callback, + MAINWIN_GENERAL_JTT, "<StockItem>", GTK_STOCK_JUMP_TO}, + {"/-", NULL, NULL, 0, "<Separator>", NULL}, + {N_("/View Track Details"), "<alt>I", mainwin_general_menu_callback, + MAINWIN_GENERAL_FILEINFO, "<ImageItem>", my_pixbuf} +}; + +static const gint mainwin_playback_menu_entries_num = + G_N_ELEMENTS(mainwin_playback_menu_entries); + +/* Main menu */ + +GtkItemFactoryEntry mainwin_general_menu_entries[] = { + {N_("/About Audacious"), NULL, mainwin_general_menu_callback, + MAINWIN_GENERAL_ABOUT, "<StockItem>", GTK_STOCK_DIALOG_INFO}, + {"/-", NULL, NULL, 0, "<Separator>", NULL}, + {N_("/Play File"), "L", mainwin_general_menu_callback, + MAINWIN_GENERAL_PLAYFILE, "<StockItem>", GTK_STOCK_OPEN}, + {N_("/Play Location"), "<control>L", mainwin_general_menu_callback, + MAINWIN_GENERAL_PLAYLOCATION, "<StockItem>", GTK_STOCK_NETWORK}, + {"/-", NULL, NULL, 0, "<Separator>", NULL}, + {N_("/V_isualization"), NULL, NULL, 0, "<Item>", NULL}, + {N_("/_Playback"), NULL, NULL, 0, "<Item>", NULL}, + {N_("/_View"), NULL, NULL, 0, "<Item>", NULL}, + {"/-", NULL, NULL, 0, "<Separator>", NULL}, + {N_("/Preferences"), "<control>P", mainwin_general_menu_callback, + MAINWIN_GENERAL_PREFS, "<StockItem>", GTK_STOCK_PREFERENCES}, + {N_("/_Quit"), NULL, mainwin_general_menu_callback, + MAINWIN_GENERAL_EXIT, "<StockItem>", GTK_STOCK_QUIT} +}; + +static const gint mainwin_general_menu_entries_num = + G_N_ELEMENTS(mainwin_general_menu_entries); + +/* Add submenu */ + +GtkItemFactoryEntry mainwin_add_menu_entries[] = { + {N_("/Files..."), "f", mainwin_general_menu_callback, + MAINWIN_GENERAL_PLAYFILE, "<StockItem>", GTK_STOCK_OPEN}, + {N_("/Internet location..."), "<control>h", mainwin_general_menu_callback, + MAINWIN_GENERAL_PLAYLOCATION, "<StockItem>", GTK_STOCK_NETWORK}, +}; + +static const gint mainwin_add_menu_entries_num = + G_N_ELEMENTS(mainwin_add_menu_entries); + +/* View submenu */ + +GtkItemFactoryEntry mainwin_view_menu_entries[] = { + {N_("/Show Player"), "<alt>M", mainwin_general_menu_callback, + MAINWIN_GENERAL_SHOWMWIN, "<ToggleItem>", NULL}, + {N_("/Show Playlist Editor"), "<alt>E", mainwin_general_menu_callback, + MAINWIN_GENERAL_SHOWPLWIN, "<ToggleItem>", NULL}, + {N_("/Show Equalizer"), "<alt>G", mainwin_general_menu_callback, + MAINWIN_GENERAL_SHOWEQWIN, "<ToggleItem>", NULL}, + {"/-", NULL, NULL, 0, "<Separator>", NULL}, + {N_("/Time Elapsed"), "<control>E", mainwin_view_menu_callback, + MAINWIN_OPT_TELAPSED, "<RadioItem>", NULL}, + {N_("/Time Remaining"), "<control>R", mainwin_view_menu_callback, + MAINWIN_OPT_TREMAINING, "/Time Elapsed", NULL}, + {"/-", NULL, NULL, 0, "<Separator>", NULL}, + {N_("/Always On Top"), "<control>o", mainwin_view_menu_callback, + MAINWIN_OPT_ALWAYS, "<ToggleItem>", NULL}, + {N_("/Put on All Workspaces"), "<control>S", + mainwin_view_menu_callback, MAINWIN_OPT_STICKY, "<ToggleItem>", NULL}, + {"/-", NULL, NULL, 0, "<Separator>", NULL}, + {N_("/Roll up Player"), "<control>W", mainwin_view_menu_callback, + MAINWIN_OPT_WS, "<ToggleItem>", NULL}, + {N_("/Roll up Playlist Editor"), "<control><shift>W", mainwin_view_menu_callback, + MAINWIN_OPT_PWS, "<ToggleItem>", NULL}, + {N_("/Roll up Equalizer"), "<control><alt>W", mainwin_view_menu_callback, + MAINWIN_OPT_EQWS, "<ToggleItem>", NULL} +}; + +static const gint mainwin_view_menu_entries_num = + G_N_ELEMENTS(mainwin_view_menu_entries); + + +static PlaybackInfo playback_info = { NULL, 0, 0, 0 }; + + +static gint mainwin_idle_func(gpointer data); + +static void set_timer_mode_menu_cb(TimerMode mode); +static void set_timer_mode(TimerMode mode); + +static void mainwin_refresh_hints(void); + +void mainwin_position_motion_cb(gint pos); +void mainwin_position_release_cb(gint pos); + + +/* FIXME: placed here for now */ +void +playback_get_sample_params(gint * bitrate, + gint * frequency, + gint * n_channels) +{ + if (bitrate) + *bitrate = playback_info.bitrate; + + if (frequency) + *frequency = playback_info.frequency; + + if (n_channels) + *n_channels = playback_info.n_channels; +} + +static void +playback_set_sample_params(gint bitrate, + gint frequency, + gint n_channels) +{ + if (bitrate >= 0) + playback_info.bitrate = bitrate; + + if (frequency >= 0) + playback_info.frequency = frequency; + + if (n_channels >= 0) + playback_info.n_channels = n_channels; +} + +static void +mainwin_set_title_scroll(gboolean scroll) +{ + cfg.autoscroll = scroll; + textbox_set_scroll(mainwin_info, cfg.autoscroll); +} + + +void +mainwin_set_always_on_top(gboolean always) +{ + GtkWidget *widget = gtk_item_factory_get_widget(mainwin_view_menu, + "/Always On Top"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), + mainwin_menurow->mr_always_selected); +} + +static void +mainwin_set_shape_mask(void) +{ + GdkBitmap *mask; + + if (!cfg.player_visible) + return; + + mask = skin_get_mask(bmp_active_skin, SKIN_MASK_MAIN + cfg.player_shaded); + gtk_widget_shape_combine_mask(mainwin, mask, 0, 0); +} + +static void +mainwin_set_shade(gboolean shaded) +{ + GtkWidget *widget; + widget = gtk_item_factory_get_widget(mainwin_view_menu, + "/Roll up Player"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), shaded); +} + +static void +mainwin_set_shade_menu_cb(gboolean shaded) +{ + cfg.player_shaded = shaded; + + mainwin_set_shape_mask(); + + if (shaded) { + dock_shade(dock_window_list, GTK_WINDOW(mainwin), + MAINWIN_SHADED_HEIGHT); + + widget_show(WIDGET(mainwin_svis)); + vis_clear_data(mainwin_vis); + + widget_show(WIDGET(mainwin_srew)); + widget_show(WIDGET(mainwin_splay)); + widget_show(WIDGET(mainwin_spause)); + widget_show(WIDGET(mainwin_sstop)); + widget_show(WIDGET(mainwin_sfwd)); + widget_show(WIDGET(mainwin_seject)); + + textbox_set_scroll(mainwin_info, FALSE); + if (bmp_playback_get_playing()) + { + widget_show(WIDGET(mainwin_sposition)); + widget_show(WIDGET(mainwin_stime_min)); + widget_show(WIDGET(mainwin_stime_sec)); + } + else + { + widget_hide(WIDGET(mainwin_sposition)); + widget_hide(WIDGET(mainwin_stime_min)); + widget_hide(WIDGET(mainwin_stime_sec)); + } + + mainwin_shade->pb_ny = mainwin_shade->pb_py = 27; + } + else { + dock_shade(dock_window_list, GTK_WINDOW(mainwin), MAINWIN_HEIGHT); + + widget_hide(WIDGET(mainwin_svis)); + svis_clear_data(mainwin_svis); + + widget_hide(WIDGET(mainwin_srew)); + widget_hide(WIDGET(mainwin_splay)); + widget_hide(WIDGET(mainwin_spause)); + widget_hide(WIDGET(mainwin_sstop)); + widget_hide(WIDGET(mainwin_sfwd)); + widget_hide(WIDGET(mainwin_seject)); + + widget_hide(WIDGET(mainwin_stime_min)); + widget_hide(WIDGET(mainwin_stime_sec)); + widget_hide(WIDGET(mainwin_sposition)); + + textbox_set_scroll(mainwin_info, TRUE); + mainwin_shade->pb_ny = mainwin_shade->pb_py = 18; + } + + draw_main_window(TRUE); +} + +static void +mainwin_vis_set_active_vis(gint new_vis) +{ + active_vis = mainwin_vis; +} + +static void +mainwin_vis_set_refresh(RefreshRate rate) +{ + cfg.vis_refresh = rate; +} + +static void +mainwin_vis_set_afalloff(FalloffSpeed speed) +{ + cfg.analyzer_falloff = speed; +} + +static void +mainwin_vis_set_pfalloff(FalloffSpeed speed) +{ + cfg.peaks_falloff = speed; +} + +static void +mainwin_vis_set_analyzer_mode(AnalyzerMode mode) +{ + cfg.analyzer_mode = mode; +} + +static void +mainwin_vis_set_analyzer_type(AnalyzerType mode) +{ + cfg.analyzer_type = mode; +} + +void +mainwin_vis_set_type(VisType mode) +{ + gchar *path = + mainwin_vis_menu_entries[MAINWIN_VIS_MENU_VIS_MODE + mode].path; + GtkWidget *widget = gtk_item_factory_get_widget(mainwin_vis_menu, path); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), TRUE); +} + +static void +mainwin_vis_set_type_menu_cb(VisType mode) +{ + cfg.vis_type = mode; + + if (mode == VIS_OFF) { + if (cfg.player_shaded && cfg.player_visible) + svis_clear(mainwin_svis); + else + vis_clear(active_vis); + } + if (mode == VIS_ANALYZER || mode == VIS_SCOPE) { + vis_clear_data(active_vis); + svis_clear_data(mainwin_svis); + } +} + +static void +mainwin_menubtn_cb(void) +{ + gint x, y; + gtk_window_get_position(GTK_WINDOW(mainwin), &x, &y); + util_item_factory_popup(mainwin_general_menu, + x + 6, + y + MAINWIN_SHADED_HEIGHT, + 1, GDK_CURRENT_TIME); +} + +void +mainwin_minimize_cb(void) +{ + if (!mainwin) + return; + + gtk_window_iconify(GTK_WINDOW(mainwin)); +} + +static void +mainwin_shade_toggle(void) +{ + mainwin_set_shade(!cfg.player_shaded); +} + +void +mainwin_quit_cb(void) +{ + gtk_widget_hide(equalizerwin); + gtk_widget_hide(playlistwin); + gtk_widget_hide(mainwin); + gdk_flush(); + + g_source_remove(mainwin_timeout_id); + + util_set_cursor(NULL); + + bmp_config_save(); + gtk_accel_map_save(bmp_paths[BMP_PATH_ACCEL_FILE]); + + ctrlsocket_cleanup(); + + playlist_stop_get_info_thread(); + playlist_clear(); + + plugin_system_cleanup(); + + gtk_main_quit(); + + exit(EXIT_SUCCESS); +} + +static void +mainwin_destroy(GtkWidget * widget, gpointer data) +{ + mainwin_quit_cb(); +} + +static void +mainwin_draw_titlebar(gboolean focus) +{ + skin_draw_mainwin_titlebar(bmp_active_skin, mainwin_bg, mainwin_gc, + cfg.player_shaded, focus || !cfg.dim_titlebar); +} + +void +draw_main_window(gboolean force) +{ + GList *wl; + Widget *w; + gboolean redraw; + + if (!cfg.player_visible) + return; + + if (force) + mainwin_refresh_hints(); + + widget_list_lock(mainwin_wlist); + + if (force) { + if (!cfg.player_shaded) + skin_draw_pixmap(bmp_active_skin, mainwin_bg, mainwin_gc, + SKIN_MAIN, 0, 0, 0, 0, MAINWIN_WIDTH, + MAINWIN_HEIGHT); + mainwin_draw_titlebar(gtk_window_has_toplevel_focus + (GTK_WINDOW(mainwin))); + } + + widget_list_draw(mainwin_wlist, &redraw, force); + + if (redraw || force) { + if (force) { + gdk_window_clear(mainwin->window); + } + else { + for (wl = mainwin_wlist; wl; wl = g_list_next(wl)) { + w = WIDGET(wl->data); + + if (!w->redraw || !w->visible) + continue; + + gdk_window_clear_area(mainwin->window, w->x, w->y, + w->width, w->height); + w->redraw = FALSE; + } + } + + gdk_flush(); + } + + widget_list_unlock(mainwin_wlist); +} + + +void +mainwin_set_info_text(void) +{ + gchar *text; + + if (mainwin_info_text_locked) + return; + + if ((text = input_get_info_text()) != NULL) { + textbox_set_text(mainwin_info, text); + g_free(text); + } + else if ((text = playlist_get_info_text()) != NULL) { + textbox_set_text(mainwin_info, text); + g_free(text); + } +} + +void +mainwin_lock_info_text(const gchar * text) +{ + mainwin_info_text_locked = TRUE; + textbox_set_text(mainwin_info, text); +} + +void +mainwin_release_info_text(void) +{ + mainwin_info_text_locked = FALSE; + mainwin_set_info_text(); +} + + +static gchar * +make_mainwin_title(const gchar * title) +{ + if (title) + return g_strdup_printf(_("%s - Audacious"), title); + else + return g_strdup(_("Audacious")); +} + +void +mainwin_set_song_title(const gchar * title) +{ + G_LOCK(mainwin_title); + g_free(mainwin_title_text); + mainwin_title_text = make_mainwin_title(title); + G_UNLOCK(mainwin_title); +} + +static void +mainwin_refresh_hints(void) +{ + if (bmp_active_skin && bmp_active_skin->properties.mainwin_othertext + == TRUE) + { + widget_hide(WIDGET(mainwin_rate_text)); + widget_hide(WIDGET(mainwin_freq_text)); + widget_hide(WIDGET(mainwin_monostereo)); + widget_show(WIDGET(mainwin_othertext)); + } + else + { + widget_show(WIDGET(mainwin_rate_text)); + widget_show(WIDGET(mainwin_freq_text)); + widget_show(WIDGET(mainwin_monostereo)); + widget_hide(WIDGET(mainwin_othertext)); + } +} + +void +mainwin_set_song_info(gint bitrate, + gint frequency, + gint n_channels) +{ + gchar text[512]; + gchar *title; + + playback_set_sample_params(bitrate, frequency, n_channels); + + if (bitrate != -1) { + bitrate /= 1000; + + if (bitrate < 1000) { + /* Show bitrate in 1000s */ + g_snprintf(text, sizeof(text), "%3d", bitrate); + textbox_set_text(mainwin_rate_text, text); + } + else { + /* Show bitrate in 100,000s */ + g_snprintf(text, sizeof(text), "%2dH", bitrate / 100); + textbox_set_text(mainwin_rate_text, text); + } + } + else + textbox_set_text(mainwin_rate_text, _("VBR")); + + /* Show sampling frequency in kHz */ + g_snprintf(text, sizeof(text), "%2d", frequency / 1000); + textbox_set_text(mainwin_freq_text, text); + + monostereo_set_num_channels(mainwin_monostereo, n_channels); + + if (cfg.player_shaded) + { + widget_show(WIDGET(mainwin_stime_min)); + widget_show(WIDGET(mainwin_stime_sec)); + } + + widget_show(WIDGET(mainwin_minus_num)); + widget_show(WIDGET(mainwin_10min_num)); + widget_show(WIDGET(mainwin_min_num)); + widget_show(WIDGET(mainwin_10sec_num)); + widget_show(WIDGET(mainwin_sec_num)); + + if (!bmp_playback_get_paused()) + playstatus_set_status(mainwin_playstatus, STATUS_PLAY); + + if (playlist_get_current_length() != -1) { + if (cfg.player_shaded) + widget_show(WIDGET(mainwin_sposition)); + widget_show(WIDGET(mainwin_position)); + } + else { + widget_hide(WIDGET(mainwin_position)); + widget_hide(WIDGET(mainwin_sposition)); + mainwin_force_redraw = TRUE; + } + + if (bmp_active_skin && bmp_active_skin->properties.mainwin_othertext + == TRUE) + { + if (bitrate != -1) + g_snprintf(text, 512, "%d kbps, %0.1f kHz, %s", + bitrate, + (gfloat) frequency / 1000, + (n_channels > 1) ? _("stereo") : _("mono")); + else + g_snprintf(text, 512, "VBR, %0.1f kHz, %s", + (gfloat) frequency / 1000, + (n_channels > 1) ? _("stereo") : _("mono")); + + textbox_set_text(mainwin_othertext, text); + + widget_hide(WIDGET(mainwin_rate_text)); + widget_hide(WIDGET(mainwin_freq_text)); + widget_hide(WIDGET(mainwin_monostereo)); + widget_show(WIDGET(mainwin_othertext)); + } + else + { + widget_show(WIDGET(mainwin_rate_text)); + widget_show(WIDGET(mainwin_freq_text)); + widget_show(WIDGET(mainwin_monostereo)); + widget_hide(WIDGET(mainwin_othertext)); + } + + title = playlist_get_info_text(); + mainwin_set_song_title(title); + g_free(title); +} + +void +mainwin_clear_song_info(void) +{ + if (!mainwin) + return; + + /* clear title */ + G_LOCK(mainwin_title); + g_free(mainwin_title_text); + mainwin_title_text = NULL; + G_UNLOCK(mainwin_title); + + /* clear sampling parameters */ + playback_set_sample_params(0, 0, 0); + + mainwin_position->hs_pressed = FALSE; + mainwin_sposition->hs_pressed = FALSE; + + /* clear sampling parameter displays */ + textbox_set_text(mainwin_rate_text, " "); + textbox_set_text(mainwin_freq_text, " "); + monostereo_set_num_channels(mainwin_monostereo, 0); + + playstatus_set_status(mainwin_playstatus, STATUS_STOP); + + /* hide playback time */ + widget_hide(WIDGET(mainwin_minus_num)); + widget_hide(WIDGET(mainwin_10min_num)); + widget_hide(WIDGET(mainwin_min_num)); + widget_hide(WIDGET(mainwin_10sec_num)); + widget_hide(WIDGET(mainwin_sec_num)); + + widget_hide(WIDGET(mainwin_stime_min)); + widget_hide(WIDGET(mainwin_stime_sec)); + + widget_hide(WIDGET(mainwin_position)); + widget_hide(WIDGET(mainwin_sposition)); + + widget_hide(WIDGET(mainwin_othertext)); + + playlistwin_hide_timer(); + draw_main_window(TRUE); + + vis_clear(active_vis); +} + +void +mainwin_disable_seekbar(void) +{ + if (!mainwin) + return; + + /* + * We dont call draw_main_window() here so this will not + * remove them visually. It will only prevent us from sending + * any seek calls to the input plugin before the input plugin + * calls ->set_info(). + */ + widget_hide(WIDGET(mainwin_position)); + widget_hide(WIDGET(mainwin_sposition)); +} + +static gboolean +mainwin_mouse_button_release(GtkWidget * widget, + GdkEventButton * event, + gpointer callback_data) +{ + gdk_pointer_ungrab(GDK_CURRENT_TIME); + + /* + * The gdk_flush() is just for making sure that the pointer really + * gets ungrabbed before calling any button callbacks + * + */ + + gdk_flush(); + + if (dock_is_moving(GTK_WINDOW(mainwin))) { + dock_move_release(GTK_WINDOW(mainwin)); + draw_playlist_window(TRUE); + } + + if (mainwin_menurow->mr_doublesize_selected) { + event->x /= 2; + event->y /= 2; + } + + handle_release_cb(mainwin_wlist, widget, event); + + draw_main_window(FALSE); + + return FALSE; +} + +static gboolean +mainwin_motion(GtkWidget * widget, + GdkEventMotion * event, + gpointer callback_data) +{ + int x, y; + GdkModifierType state; + + if (event->is_hint != FALSE) + { + gdk_window_get_pointer(GDK_WINDOW(mainwin->window), + &x, &y, &state); + + /* If it's a hint, we had to query X, so override the + * information we we're given... it's probably useless... --nenolod + */ + event->x = x; + event->y = y; + event->state = state; + } + else + { + x = event->x; + y = event->y; + state = event->state; + } + + if (dock_is_moving(GTK_WINDOW(mainwin))) { + dock_move_motion(GTK_WINDOW(mainwin), event); + } + else { + handle_motion_cb(mainwin_wlist, widget, event); + draw_main_window(FALSE); + } + + gdk_flush(); + + return FALSE; +} + +static gboolean +inside_sensitive_widgets(gint x, gint y) +{ + return (widget_contains(WIDGET(mainwin_menubtn), x, y) + || widget_contains(WIDGET(mainwin_minimize), x, y) + || widget_contains(WIDGET(mainwin_shade), x, y) + || widget_contains(WIDGET(mainwin_close), x, y) + || widget_contains(WIDGET(mainwin_rew), x, y) + || widget_contains(WIDGET(mainwin_play), x, y) + || widget_contains(WIDGET(mainwin_pause), x, y) + || widget_contains(WIDGET(mainwin_stop), x, y) + || widget_contains(WIDGET(mainwin_fwd), x, y) + || widget_contains(WIDGET(mainwin_eject), x, y) + || widget_contains(WIDGET(mainwin_shuffle), x, y) + || widget_contains(WIDGET(mainwin_repeat), x, y) + || widget_contains(WIDGET(mainwin_pl), x, y) + || widget_contains(WIDGET(mainwin_eq), x, y) + || widget_contains(WIDGET(mainwin_info), x, y) + || widget_contains(WIDGET(mainwin_menurow), x, y) + || widget_contains(WIDGET(mainwin_volume), x, y) + || widget_contains(WIDGET(mainwin_balance), x, y) + || (widget_contains(WIDGET(mainwin_position), x, y) && + widget_is_visible(WIDGET(mainwin_position))) + || widget_contains(WIDGET(mainwin_minus_num), x, y) + || widget_contains(WIDGET(mainwin_10min_num), x, y) + || widget_contains(WIDGET(mainwin_min_num), x, y) + || widget_contains(WIDGET(mainwin_10sec_num), x, y) + || widget_contains(WIDGET(mainwin_sec_num), x, y) + || widget_contains(WIDGET(mainwin_vis), x, y) + || widget_contains(WIDGET(mainwin_minimize), x, y) + || widget_contains(WIDGET(mainwin_shade), x, y) + || widget_contains(WIDGET(mainwin_close), x, y) + || widget_contains(WIDGET(mainwin_menubtn), x, y) + || widget_contains(WIDGET(mainwin_sposition), x, y) + || widget_contains(WIDGET(mainwin_stime_min), x, y) + || widget_contains(WIDGET(mainwin_stime_sec), x, y) + || widget_contains(WIDGET(mainwin_srew), x, y) + || widget_contains(WIDGET(mainwin_splay), x, y) + || widget_contains(WIDGET(mainwin_spause), x, y) + || widget_contains(WIDGET(mainwin_sstop), x, y) + || widget_contains(WIDGET(mainwin_sfwd), x, y) + || widget_contains(WIDGET(mainwin_seject), x, y) + || widget_contains(WIDGET(mainwin_svis), x, y) + || widget_contains(WIDGET(mainwin_about), x, y)); +} + +void +mainwin_scrolled(GtkWidget * widget, + GdkEventScroll * event, + gpointer callback_data) +{ + gint d = cfg.mouse_change; + if (event->direction == GDK_SCROLL_DOWN) + d *= -1; + mainwin_set_volume_diff(d); +} + + +static gboolean +mainwin_mouse_button_press(GtkWidget * widget, + GdkEventButton * event, + gpointer callback_data) +{ + + gboolean grab = TRUE; + + if (event->button == 1 && event->type == GDK_BUTTON_PRESS && + !inside_sensitive_widgets(event->x, event->y) && event->y < 14) { + if (0 && hint_move_resize_available()) { + hint_move_resize(mainwin, event->x_root, event->y_root, TRUE); + grab = FALSE; + } + else { + gtk_window_present(GTK_WINDOW(mainwin)); + dock_move_press(dock_window_list, GTK_WINDOW(mainwin), event, + TRUE); + } + } + else if (event->button == 1 && event->type == GDK_2BUTTON_PRESS && + event->y < 14 && !inside_sensitive_widgets(event->x, event->y)) { + mainwin_set_shade(!cfg.player_shaded); + if (dock_is_moving(GTK_WINDOW(mainwin))) + dock_move_release(GTK_WINDOW(mainwin)); + } + else if (event->button == 1 && event->type == GDK_2BUTTON_PRESS && + widget_contains(WIDGET(mainwin_info), event->x, event->y)) { + playlist_fileinfo_current(); + } + else { + handle_press_cb(mainwin_wlist, widget, event); + draw_main_window(FALSE); + } + + if ((event->button == 1) && event->type != GDK_2BUTTON_PRESS && + (widget_contains(WIDGET(mainwin_vis), event->x, event->y) || + widget_contains(WIDGET(mainwin_svis), event->x, event->y))) { + + cfg.vis_type++; + + if (cfg.vis_type > VIS_OFF) + cfg.vis_type = VIS_ANALYZER; + + mainwin_vis_set_type(cfg.vis_type); + } + + if (event->button == 3) { + if (widget_contains(WIDGET(mainwin_info), event->x, event->y)) { + util_item_factory_popup(mainwin_songname_menu, + event->x_root, event->y_root, + 3, event->time); + grab = FALSE; + } + else if (widget_contains(WIDGET(mainwin_vis), event->x, event->y) || + widget_contains(WIDGET(mainwin_svis), event->x, event->y)) { + util_item_factory_popup(mainwin_vis_menu, event->x_root, + event->y_root, 3, event->time); + grab = FALSE; + } + else if ( (event->y > 70) && (event->x < 128) ) + { + + util_item_factory_popup(mainwin_play_menu, + event->x_root, + event->y_root, 3, event->time); + grab = FALSE; + } else { + /* + * Pop up the main menu a few pixels down. + * This will avoid that anything is selected + * if one right-clicks to focus the window + * without raising it. + * + ***MD I think the above is stupid, people don't expect this + * + */ + util_item_factory_popup(mainwin_general_menu, + event->x_root, + event->y_root, 3, event->time); + grab = FALSE; + } + } + if (event->button == 1) { + if ((event->x > 35 && event->x < 100 && + event->y > 25 && event->y < 40) || + widget_contains(WIDGET(mainwin_stime_min), event->x, event->y) || + widget_contains(WIDGET(mainwin_stime_sec), event->x, event->y)) { + + if (cfg.timer_mode == TIMER_ELAPSED) + set_timer_mode(TIMER_REMAINING); + else + set_timer_mode(TIMER_ELAPSED); + } + + } + + if (grab) + gdk_pointer_grab(mainwin->window, FALSE, + GDK_BUTTON_MOTION_MASK | + GDK_BUTTON_RELEASE_MASK, + GDK_WINDOW(GDK_NONE), NULL, GDK_CURRENT_TIME); + + return FALSE; +} + +static gboolean +mainwin_focus_in(GtkWidget * window, + GdkEventFocus * event, + gpointer data) +{ + mainwin_menubtn->pb_allow_draw = TRUE; + mainwin_minimize->pb_allow_draw = TRUE; + mainwin_shade->pb_allow_draw = TRUE; + mainwin_close->pb_allow_draw = TRUE; + draw_main_window(TRUE); + + return TRUE; +} + + +static gboolean +mainwin_focus_out(GtkWidget * widget, + GdkEventFocus * event, + gpointer callback_data) +{ + mainwin_menubtn->pb_allow_draw = FALSE; + mainwin_minimize->pb_allow_draw = FALSE; + mainwin_shade->pb_allow_draw = FALSE; + mainwin_close->pb_allow_draw = FALSE; + draw_main_window(TRUE); + + return TRUE; +} + +static gboolean +mainwin_keypress(GtkWidget * grab_widget, + GdkEventKey * event, + gpointer data) +{ + + switch (event->keyval) { + + case GDK_Up: + case GDK_KP_Up: + case GDK_KP_8: + mainwin_set_volume_diff(2); + break; + case GDK_Down: + case GDK_KP_Down: + case GDK_KP_2: + mainwin_set_volume_diff(-2); + break; + case GDK_Left: + case GDK_KP_Left: + case GDK_KP_7: + if (playlist_get_current_length() != -1) + bmp_playback_seek(CLAMP + (bmp_playback_get_time() - 1000, 0, + playlist_get_current_length()) / 1000); + break; + case GDK_Right: + case GDK_KP_Right: + case GDK_KP_9: + if (playlist_get_current_length() != -1) + bmp_playback_seek(CLAMP + (bmp_playback_get_time() + 1000, 0, + playlist_get_current_length()) / 1000); + break; + case GDK_KP_4: + playlist_prev(); + break; + case GDK_KP_6: + playlist_next(); + break; + case GDK_KP_Insert: + mainwin_jump_to_file(); + break; + case GDK_KP_5: + mainwin_play_pushed(); + break; + case GDK_Escape: + mainwin_minimize_cb(); + break; + default: + return FALSE; + } + + return TRUE; +} + +static void +mainwin_jump_to_time_cb(GtkWidget * widget, + GtkWidget * entry) +{ + guint min = 0, sec = 0, params; + gint time; + + params = sscanf(gtk_entry_get_text(GTK_ENTRY(entry)), "%u:%u", + &min, &sec); + if (params == 2) + time = (min * 60) + sec; + else if (params == 1) + time = min; + else + return; + + if (playlist_get_current_length() > -1 && + time <= (playlist_get_current_length() / 1000)) { + bmp_playback_seek(time); + gtk_widget_destroy(mainwin_jtt); + } +} + + +void +mainwin_jump_to_time(void) +{ + GtkWidget *vbox, *hbox_new, *hbox_total; + GtkWidget *time_entry, *label, *bbox, *jump, *cancel; + guint tindex; + gchar time_str[10]; + + if (!bmp_playback_get_playing()) { + report_error("JIT can't be launched when no track is being played.\n"); + return; + } + + if (mainwin_jtt) { + gtk_window_present(GTK_WINDOW(mainwin_jtt)); + return; + } + + mainwin_jtt = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_type_hint(GTK_WINDOW(mainwin_jtt), + GDK_WINDOW_TYPE_HINT_DIALOG); + + gtk_window_set_title(GTK_WINDOW(mainwin_jtt), _("Jump to Time")); + gtk_window_set_position(GTK_WINDOW(mainwin_jtt), GTK_WIN_POS_CENTER); + gtk_window_set_transient_for(GTK_WINDOW(mainwin_jtt), + GTK_WINDOW(mainwin)); + + g_signal_connect(mainwin_jtt, "destroy", + G_CALLBACK(gtk_widget_destroyed), &mainwin_jtt); + gtk_container_border_width(GTK_CONTAINER(mainwin_jtt), 10); + + vbox = gtk_vbox_new(FALSE, 5); + gtk_container_add(GTK_CONTAINER(mainwin_jtt), vbox); + + hbox_new = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox_new, TRUE, TRUE, 5); + + time_entry = gtk_entry_new(); + gtk_box_pack_start(GTK_BOX(hbox_new), time_entry, FALSE, FALSE, 5); + g_signal_connect(time_entry, "activate", + G_CALLBACK(mainwin_jump_to_time_cb), time_entry); + + gtk_widget_set_size_request(time_entry, 70, -1); + label = gtk_label_new(_("minutes:seconds")); + gtk_box_pack_start(GTK_BOX(hbox_new), label, FALSE, FALSE, 5); + + hbox_total = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(vbox), hbox_total, TRUE, TRUE, 5); + gtk_widget_show(hbox_total); + + /* FIXME: Disable display of current track length. It's not + updated when track changes */ +#if 0 + label = gtk_label_new(_("Track length:")); + gtk_box_pack_start(GTK_BOX(hbox_total), label, FALSE, FALSE, 5); + + len = playlist_get_current_length() / 1000; + g_snprintf(time_str, sizeof(time_str), "%u:%2.2u", len / 60, len % 60); + label = gtk_label_new(time_str); + + gtk_box_pack_start(GTK_BOX(hbox_total), label, FALSE, FALSE, 10); +#endif + + bbox = gtk_hbutton_box_new(); + gtk_box_pack_start(GTK_BOX(vbox), bbox, TRUE, TRUE, 0); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); + gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5); + + cancel = gtk_button_new_from_stock(GTK_STOCK_CANCEL); + GTK_WIDGET_SET_FLAGS(cancel, GTK_CAN_DEFAULT); + gtk_container_add(GTK_CONTAINER(bbox), cancel); + g_signal_connect_swapped(cancel, "clicked", + G_CALLBACK(gtk_widget_destroy), mainwin_jtt); + + jump = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO); + GTK_WIDGET_SET_FLAGS(jump, GTK_CAN_DEFAULT); + gtk_container_add(GTK_CONTAINER(bbox), jump); + g_signal_connect(jump, "clicked", + G_CALLBACK(mainwin_jump_to_time_cb), time_entry); + + tindex = bmp_playback_get_time() / 1000; + g_snprintf(time_str, sizeof(time_str), "%u:%2.2u", tindex / 60, + tindex % 60); + gtk_entry_set_text(GTK_ENTRY(time_entry), time_str); + + gtk_entry_select_region(GTK_ENTRY(time_entry), 0, strlen(time_str)); + + gtk_widget_show_all(mainwin_jtt); + + gtk_widget_grab_focus(time_entry); + gtk_widget_grab_default(jump); +} + +static void +change_song(guint pos) +{ + if (bmp_playback_get_playing()) + bmp_playback_stop(); + + playlist_set_position(pos); + bmp_playback_initiate(); +} + +static void +mainwin_jump_to_file_jump(GtkTreeView * treeview) +{ + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkTreeIter iter; + gchar *pos_str; + guint pos; + + model = gtk_tree_view_get_model(treeview); + selection = gtk_tree_view_get_selection(treeview); + + if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) + return; + + gtk_tree_model_get(model, &iter, 0, &pos_str, -1); + pos = g_ascii_strtoull(pos_str, NULL, 10) - 1; + g_free(pos_str); + + change_song(pos); + + /* FIXME: should only hide window */ + gtk_widget_destroy(mainwin_jtf); + mainwin_jtf = NULL; +} + +static void +mainwin_jump_to_file_jump_cb(GtkTreeView * treeview, + gpointer data) +{ + mainwin_jump_to_file_jump(treeview); +} + +static void +mainwin_jump_to_file_set_queue_button_label(GtkButton * button, + guint pos) +{ + if (playlist_is_position_queued(pos)) + gtk_button_set_label(button, _("Un_queue")); + else + gtk_button_set_label(button, _("_Queue")); +} + +static void +mainwin_jump_to_file_queue_cb(GtkButton * button, + gpointer data) +{ + GtkTreeView *treeview; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkTreeIter iter; + gchar *pos_str; + guint pos; + + treeview = GTK_TREE_VIEW(data); + model = gtk_tree_view_get_model(treeview); + selection = gtk_tree_view_get_selection(treeview); + + if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) + return; + + gtk_tree_model_get(model, &iter, 0, &pos_str, -1); + pos = g_ascii_strtoull(pos_str, NULL, 10) - 1; + + playlist_queue_position(pos); + + mainwin_jump_to_file_set_queue_button_label(button, pos); +} + +static void +mainwin_jump_to_file_selection_changed_cb(GtkTreeSelection *treesel, + gpointer data) +{ + GtkTreeView *treeview; + GtkTreeModel *model; + GtkTreeSelection *selection; + GtkTreeIter iter; + gchar *pos_str; + guint pos; + + treeview = gtk_tree_selection_get_tree_view(treesel); + model = gtk_tree_view_get_model(treeview); + selection = gtk_tree_view_get_selection(treeview); + + if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) + return; + + gtk_tree_model_get(model, &iter, 0, &pos_str, -1); + pos = g_ascii_strtoull(pos_str, NULL, 10) - 1; + g_free(pos_str); + + mainwin_jump_to_file_set_queue_button_label(GTK_BUTTON(data), pos); +} + +static gboolean +mainwin_jump_to_file_edit_keypress_cb(GtkWidget * object, + GdkEventKey * event, + gpointer data) +{ + switch (event->keyval) { + case GDK_Return: + if (gtk_im_context_filter_keypress (GTK_ENTRY (object)->im_context, event)) { + GTK_ENTRY (object)->need_im_reset = TRUE; + return TRUE; + } else { + mainwin_jump_to_file_jump(GTK_TREE_VIEW(data)); + return TRUE; + } + default: + return FALSE; + } +} + +static gboolean +mainwin_jump_to_file_keypress_cb(GtkWidget * object, + GdkEventKey * event, + gpointer data) +{ + switch (event->keyval) { + case GDK_Escape: + /* FIXME: show only hide window */ + gtk_widget_destroy(mainwin_jtf); + mainwin_jtf = NULL; + return TRUE; + default: + return FALSE; + }; +} + +static gboolean +mainwin_jump_to_file_match(const gchar * song, gchar ** keys) +{ + gint i = 0; + gchar *key; + gchar *song_lc; + + song_lc = g_ascii_strdown(song, -1); + + while (keys[i]) { + key = g_ascii_strdown(keys[i], -1); + if (!g_strrstr(song_lc, key)) { + g_free(key); + g_free(song_lc); + return FALSE; + } + + g_free(key); + i++; + } + + g_free(song_lc); + + return TRUE; +} + +/* FIXME: Clear the entry when the list gets updated */ +static void +mainwin_update_jtf(GtkWidget * widget, gpointer user_data) +{ + /* FIXME: Is not in sync with playlist due to delayed extinfo + * reading */ + gint row; + GList *playlist; + gchar *desc_buf = NULL; + gchar *row_str; + GtkTreeIter iter; + GtkTreeSelection *selection; + + GtkTreeModel *store; + + if (!mainwin_jtf) + return; + + store = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data)); + gtk_list_store_clear(GTK_LIST_STORE(store)); + + row = 1; + for (playlist = playlist_get(); playlist; + playlist = g_list_next(playlist)) { + PlaylistEntry *entry = PLAYLIST_ENTRY(playlist->data); + + if (entry->title) + desc_buf = g_strdup(entry->title); + else if (strchr(entry->filename, '/')) + desc_buf = str_to_utf8(strrchr(entry->filename, '/') + 1); + else + desc_buf = str_to_utf8(entry->filename); + + row_str = g_strdup_printf("%d", row++); + + gtk_list_store_append(GTK_LIST_STORE(store), &iter); + gtk_list_store_set(GTK_LIST_STORE(store), &iter, + 0, row_str, 1, desc_buf, -1); + + if(desc_buf) { + g_free(desc_buf); + desc_buf = NULL; + } + + g_free(row_str); + } + + gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter); + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(user_data)); + gtk_tree_selection_select_iter(selection, &iter); +} + +static void +mainwin_jump_to_file_edit_cb(GtkEntry * entry, gpointer user_data) +{ + GtkTreeView *treeview = GTK_TREE_VIEW(user_data); + GtkTreeSelection *selection; + GtkTreeIter iter; + + GtkListStore *store; + + gint song_index = 0; + gchar **words; + GList *playlist; + + gboolean match = FALSE; + + /* Chop the key string into ' '-separated key words */ + words = g_strsplit(gtk_entry_get_text(entry), " ", 0); + + /* FIXME: Remove the connected signals before clearing + * (row-selected will still eventually arrive once) */ + store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview)); + gtk_list_store_clear(store); + + PLAYLIST_LOCK(); + + for (playlist = playlist_get(); playlist; + playlist = g_list_next(playlist)) { + + PlaylistEntry *entry = PLAYLIST_ENTRY(playlist->data); + const gchar *title; + gchar *filename = NULL; + + title = entry->title; + if (!title) { + filename = str_to_utf8(entry->filename); + + if (strchr(filename, '/')) + title = strrchr(filename, '/') + 1; + else + title = filename; + } + + /* Compare the key words to the string - if all the words + match, add to the ListStore */ + + /* + * FIXME: The search string should be adapted to the + * current display setting, e.g. if the user has set it to + * "%p - %t" then build the match string like that too, or + * even better, search for each of the tags seperatly. + * + * In any case the string to match should _never_ contain + * something the user can't actually see in the playlist. + */ + if (words[0]) + match = mainwin_jump_to_file_match(title, words); + else + match = TRUE; + + if (match) { + gchar *song_index_str = g_strdup_printf("%d", song_index + 1); + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, 0, song_index_str, 1, title, -1); + g_free(song_index_str); + } + + song_index++; + if (filename) { + g_free(filename); + filename = NULL; + } + } + + PLAYLIST_UNLOCK(); + + g_strfreev(words); + + if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) { + selection = gtk_tree_view_get_selection(treeview); + gtk_tree_selection_select_iter(selection, &iter); + } +} + +void +mainwin_jump_to_file(void) +{ + GtkWidget *scrollwin; + GtkWidget *vbox, *bbox, *sep; + GtkWidget *jump, *queue, *cancel; + GtkWidget *rescan, *edit; + GtkWidget *search_label, *hbox; + GList *playlist; + gchar *desc_buf = NULL; + gchar *row_str; + gint row; + + GtkWidget *treeview; + GtkListStore *jtf_store; + + GtkTreeIter iter; + GtkCellRenderer *renderer; + GtkTreeViewColumn *column; + + if (mainwin_jtf) { + gtk_window_present(GTK_WINDOW(mainwin_jtf)); + return; + } + + mainwin_jtf = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_type_hint(GTK_WINDOW(mainwin_jtf), + GDK_WINDOW_TYPE_HINT_DIALOG); + + gtk_window_set_title(GTK_WINDOW(mainwin_jtf), _("Jump to Track")); + + gtk_window_set_position(GTK_WINDOW(mainwin_jtf), GTK_WIN_POS_CENTER); + g_signal_connect(mainwin_jtf, "destroy", + G_CALLBACK(gtk_widget_destroyed), &mainwin_jtf); + + gtk_container_border_width(GTK_CONTAINER(mainwin_jtf), 10); + gtk_window_set_default_size(GTK_WINDOW(mainwin_jtf), 550, 350); + + vbox = gtk_vbox_new(FALSE, 5); + gtk_container_add(GTK_CONTAINER(mainwin_jtf), vbox); + + jtf_store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); + treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(jtf_store)); + g_object_unref(jtf_store); + + gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE); + + column = gtk_tree_view_column_new(); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), FALSE); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(column, renderer, FALSE); + gtk_tree_view_column_set_attributes(column, renderer, "text", 0, NULL); + gtk_tree_view_column_set_spacing(column, 4); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(column, renderer, FALSE); + gtk_tree_view_column_set_attributes(column, renderer, "text", 1, NULL); + gtk_tree_view_column_set_spacing(column, 4); + gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column); + + gtk_tree_view_set_search_column(GTK_TREE_VIEW(treeview), 1); + + g_signal_connect(treeview, "row-activated", + G_CALLBACK(mainwin_jump_to_file_jump), NULL); + + hbox = gtk_hbox_new(FALSE, 3); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3); + + search_label = gtk_label_new(_("Filter: ")); + gtk_label_set_markup_with_mnemonic(GTK_LABEL(search_label), "_Filter:"); + gtk_box_pack_start(GTK_BOX(hbox), search_label, FALSE, FALSE, 0); + + edit = gtk_entry_new(); + gtk_entry_set_editable(GTK_ENTRY(edit), TRUE); + gtk_label_set_mnemonic_widget(GTK_LABEL(search_label), edit); + g_signal_connect(edit, "changed", + G_CALLBACK(mainwin_jump_to_file_edit_cb), treeview); + + g_signal_connect(edit, "key_press_event", + G_CALLBACK(mainwin_jump_to_file_edit_keypress_cb), treeview); + + g_signal_connect(mainwin_jtf, "key_press_event", + G_CALLBACK(mainwin_jump_to_file_keypress_cb), treeview); + + gtk_box_pack_start(GTK_BOX(hbox), edit, TRUE, TRUE, 3); + + scrollwin = gtk_scrolled_window_new(NULL, NULL); + gtk_container_add(GTK_CONTAINER(scrollwin), treeview); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), + GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrollwin), + GTK_SHADOW_IN); + gtk_box_pack_start(GTK_BOX(vbox), scrollwin, TRUE, TRUE, 0); + + sep = gtk_hseparator_new(); + gtk_box_pack_start(GTK_BOX(vbox), sep, FALSE, FALSE, 0); + + bbox = gtk_hbutton_box_new(); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); + gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5); + gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0); + + queue = gtk_button_new_with_mnemonic(_("_Queue")); + gtk_box_pack_start(GTK_BOX(bbox), queue, FALSE, FALSE, 0); + GTK_WIDGET_SET_FLAGS(queue, GTK_CAN_DEFAULT); + g_signal_connect(queue, "clicked", + G_CALLBACK(mainwin_jump_to_file_queue_cb), + treeview); + g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview)), "changed", + G_CALLBACK(mainwin_jump_to_file_selection_changed_cb), + queue); + + rescan = gtk_button_new_from_stock(GTK_STOCK_REFRESH); + gtk_box_pack_start(GTK_BOX(bbox), rescan, FALSE, FALSE, 0); + g_signal_connect(rescan, "clicked", + G_CALLBACK(mainwin_update_jtf), treeview); + GTK_WIDGET_SET_FLAGS(rescan, GTK_CAN_DEFAULT); + gtk_widget_grab_default(rescan); + + jump = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO); + gtk_box_pack_start(GTK_BOX(bbox), jump, FALSE, FALSE, 0); + + g_signal_connect_swapped(jump, "clicked", + G_CALLBACK(mainwin_jump_to_file_jump_cb), + treeview); + + GTK_WIDGET_SET_FLAGS(jump, GTK_CAN_DEFAULT); + gtk_widget_grab_default(jump); + + cancel = gtk_button_new_from_stock(GTK_STOCK_CLOSE); + gtk_box_pack_start(GTK_BOX(bbox), cancel, FALSE, FALSE, 0); + g_signal_connect_swapped(cancel, "clicked", + G_CALLBACK(gtk_widget_destroy), + mainwin_jtf); + GTK_WIDGET_SET_FLAGS(cancel, GTK_CAN_DEFAULT); + + gtk_list_store_clear(jtf_store); + + row = 1; + + PLAYLIST_LOCK(); + + for (playlist = playlist_get(); playlist; + playlist = g_list_next(playlist)) { + + PlaylistEntry *entry = PLAYLIST_ENTRY(playlist->data); + + if (entry->title) + desc_buf = g_strdup(entry->title); + else if (strchr(entry->filename, '/')) + desc_buf = str_to_utf8(strrchr(entry->filename, '/') + 1); + else + desc_buf = str_to_utf8(entry->filename); + + row_str = g_strdup_printf("%d", row++); + + gtk_list_store_append(GTK_LIST_STORE(jtf_store), &iter); + gtk_list_store_set(GTK_LIST_STORE(jtf_store), &iter, + 0, row_str, 1, desc_buf, -1); + + if (desc_buf) { + g_free(desc_buf); + desc_buf = NULL; + } + g_free(row_str); + } + + PLAYLIST_UNLOCK(); + + gtk_widget_show_all(mainwin_jtf); +} + +static gboolean +mainwin_configure(GtkWidget * window, + GdkEventConfigure * event, + gpointer data) +{ + if (!GTK_WIDGET_VISIBLE(window)) + return FALSE; + + if (cfg.show_wm_decorations) + gdk_window_get_root_origin(window->window, + &cfg.player_x, &cfg.player_y); + else + gdk_window_get_deskrelative_origin(window->window, + &cfg.player_x, &cfg.player_y); + return FALSE; +} + +void +mainwin_set_back_pixmap(void) +{ + gdk_window_set_back_pixmap(mainwin->window, mainwin_bg, 0); + gdk_window_clear(mainwin->window); +} + +void +mainwin_drag_data_received(GtkWidget * widget, + GdkDragContext * context, + gint x, + gint y, + GtkSelectionData * selection_data, + guint info, + guint time, + gpointer user_data) +{ + gchar **iter, **sourcelist, *path; + gchar *decoded; + gboolean not_font = FALSE; + + if (!selection_data->data) + { + g_warning("DND data string is NULL"); + return; + } + + iter = sourcelist = g_strsplit((gchar *)(selection_data->data),"\n",-1); + + for (path = *sourcelist; *path; path = *(++sourcelist)) + { + if (path == NULL) /* damn konqueror */ + break; + + if (str_has_prefix_nocase(path, "fonts:///")) + { + path += 8; + + /* plain, since we already stripped the first URI part */ + decoded = xmms_urldecode_plain(path); + + /* Get the old font's size, and add it to the dropped + * font's name + */ + cfg.playlist_font = g_strconcat(decoded + 1, + strrchr(cfg.playlist_font, ' '), + NULL); + playlist_list_set_font(cfg.playlist_font); + playlistwin_update_list(); + + g_free(decoded); + return; + } + + if (str_has_prefix_nocase(path,"file:///")) + { + if (not_font == FALSE) + { + playlist_clear(); + not_font = TRUE; + } + playlist_add_url(path); + } + } + + g_strfreev(iter); + + if (not_font) + bmp_playback_initiate(); +} + +static void +on_add_url_add_clicked(GtkWidget * widget, + GtkWidget * entry) +{ + const gchar *text = gtk_entry_get_text(GTK_ENTRY(entry)); + if (text && *text) + playlist_add_url(text); +} + +static void +on_add_url_ok_clicked(GtkWidget * widget, + GtkWidget * entry) +{ + const gchar *text = gtk_entry_get_text(GTK_ENTRY(entry)); + if (text && *text) + { + playlist_clear(); + playlist_add_url(text); + bmp_playback_initiate(); + } +} + +void +mainwin_show_add_url_window(void) +{ + static GtkWidget *url_window = NULL; + + if (!url_window) { + url_window = + util_add_url_dialog_new(_("Enter location to play:"), + G_CALLBACK(on_add_url_ok_clicked), + G_CALLBACK(on_add_url_add_clicked)); + gtk_window_set_transient_for(GTK_WINDOW(url_window), + GTK_WINDOW(mainwin)); + g_signal_connect(url_window, "destroy", + G_CALLBACK(gtk_widget_destroyed), + &url_window); + } + + gtk_window_present(GTK_WINDOW(url_window)); +} + +static void +check_set(GtkItemFactory * factory, + const gchar * path, + gboolean active) +{ + GtkWidget *item = gtk_item_factory_get_widget(factory, path); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), active); +} + +void +mainwin_eject_pushed(void) +{ + util_run_filebrowser(PLAY_BUTTON); +} + +void +mainwin_rev_pushed(void) +{ + g_get_current_time(&cb_time); + + seek_initial_pos = hslider_get_position(mainwin_position); + seek_state = MAINWIN_SEEK_REV; +} + +void +mainwin_rev_release(void) +{ + GTimeVal now_time; + GTimeVal delta_time; + gulong now_dur; + + g_get_current_time(&now_time); + + delta_time.tv_usec = now_time.tv_usec - cb_time.tv_usec; + delta_time.tv_sec = now_time.tv_sec - cb_time.tv_sec; + + now_dur = labs((delta_time.tv_sec * 1000) + (glong) (delta_time.tv_usec / 1000)); + + if ( now_dur <= TRISTATE_THRESHOLD ) + { + /* interpret as 'skip to previous song' */ + playlist_prev(); + } + else + { + /* interpret as 'seek' */ + mainwin_position_release_cb( hslider_get_position(mainwin_position) ); + } + + seek_state = MAINWIN_SEEK_NIL; +} + +void +mainwin_fwd_pushed(void) +{ + g_get_current_time(&cb_time); + + seek_initial_pos = hslider_get_position(mainwin_position); + seek_state = MAINWIN_SEEK_FWD; +} + +void +mainwin_fwd_release(void) +{ + GTimeVal now_time; + GTimeVal delta_time; + gulong now_dur; + + g_get_current_time(&now_time); + + delta_time.tv_usec = now_time.tv_usec - cb_time.tv_usec; + delta_time.tv_sec = now_time.tv_sec - cb_time.tv_sec; + + now_dur = labs((delta_time.tv_sec * 1000) + (glong) (delta_time.tv_usec / 1000)); + + if ( now_dur <= TRISTATE_THRESHOLD ) + { + /* interpret as 'skip to previous song' */ + playlist_next(); + } + else + { + /* interpret as 'seek' */ + mainwin_position_release_cb( hslider_get_position(mainwin_position) ); + } + + seek_state = MAINWIN_SEEK_NIL; +} + +void +mainwin_play_pushed(void) +{ + if (bmp_playback_get_paused()) { + bmp_playback_pause(); + return; + } + + if (playlist_get_length()) + bmp_playback_initiate(); + else + mainwin_eject_pushed(); +} + +void +mainwin_stop_pushed(void) +{ + ip_data.stop = TRUE; + mainwin_clear_song_info(); + bmp_playback_stop(); + ip_data.stop = FALSE; +} + +void +mainwin_shuffle_pushed(gboolean toggled) +{ + check_set(mainwin_play_menu, "/Shuffle", toggled); +} + +void +mainwin_repeat_pushed(gboolean toggled) +{ + check_set(mainwin_play_menu, "/Repeat", toggled); +} + +void +mainwin_pl_pushed(gboolean toggled) +{ + if (toggled) + playlistwin_show(); + else + playlistwin_hide(); +} + +gint +mainwin_spos_frame_cb(gint pos) +{ + if (mainwin_sposition) { + if (pos < 6) + mainwin_sposition->hs_knob_nx = mainwin_sposition->hs_knob_px = + 17; + else if (pos < 9) + mainwin_sposition->hs_knob_nx = mainwin_sposition->hs_knob_px = + 20; + else + mainwin_sposition->hs_knob_nx = mainwin_sposition->hs_knob_px = + 23; + } + return 1; +} + +void +mainwin_spos_motion_cb(gint pos) +{ + gint time; + gchar *time_msg; + + pos--; + + time = ((playlist_get_current_length() / 1000) * pos) / 12; + + if (cfg.timer_mode == TIMER_REMAINING) { + time = (playlist_get_current_length() / 1000) - time; + time_msg = g_strdup_printf("-%2.2d", time / 60); + textbox_set_text(mainwin_stime_min, time_msg); + g_free(time_msg); + } + else { + time_msg = g_strdup_printf(" %2.2d", time / 60); + textbox_set_text(mainwin_stime_min, time_msg); + g_free(time_msg); + } + + time_msg = g_strdup_printf("%2.2d", time % 60); + textbox_set_text(mainwin_stime_sec, time_msg); + g_free(time_msg); +} + +void +mainwin_spos_release_cb(gint pos) +{ + bmp_playback_seek(((playlist_get_current_length() / 1000) * + (pos - 1)) / 12); +} + +void +mainwin_position_motion_cb(gint pos) +{ + gint length, time; + gchar *seek_msg; + + length = playlist_get_current_length() / 1000; + time = (length * pos) / 219; + seek_msg = g_strdup_printf(_("SEEK TO: %d:%-2.2d/%d:%-2.2d (%d%%)"), + time / 60, time % 60, + length / 60, length % 60, + (length != 0) ? (time * 100) / length : 0); + mainwin_lock_info_text(seek_msg); + g_free(seek_msg); +} + +void +mainwin_position_release_cb(gint pos) +{ + gint length, time; + + length = playlist_get_current_length() / 1000; + time = (length * pos) / 219; + bmp_playback_seek(time); + mainwin_release_info_text(); +} + +gint +mainwin_volume_frame_cb(gint pos) +{ + return (gint) rint((pos / 52.0) * 28); +} + +void +mainwin_adjust_volume_motion(gint v) +{ + gchar *volume_msg; + + setting_volume = TRUE; + + volume_msg = g_strdup_printf(_("VOLUME: %d%%"), v); + mainwin_lock_info_text(volume_msg); + g_free(volume_msg); + + if (balance < 0) + input_set_volume(v, (v * (100 - abs(balance))) / 100); + else if (balance > 0) + input_set_volume((v * (100 - abs(balance))) / 100, v); + else + input_set_volume(v, v); +} + +void +mainwin_adjust_volume_release(void) +{ + mainwin_release_info_text(); + setting_volume = FALSE; + read_volume(VOLUME_ADJUSTED); +} + +void +mainwin_adjust_balance_motion(gint b) +{ + gchar *balance_msg; + gint v, pvl, pvr; + + setting_volume = TRUE; + balance = b; + input_get_volume(&pvl, &pvr); + v = MAX(pvl, pvr); + if (b < 0) { + balance_msg = g_strdup_printf(_("BALANCE: %d%% LEFT"), -b); + input_set_volume(v, (gint) rint(((100 + b) / 100.0) * v)); + } + else if (b == 0) { + balance_msg = g_strdup_printf(_("BALANCE: CENTER")); + input_set_volume(v, v); + } + else { /* b > 0 */ + balance_msg = g_strdup_printf(_("BALANCE: %d%% RIGHT"), b); + input_set_volume((gint) rint(((100 - b) / 100.0) * v), v); + } + mainwin_lock_info_text(balance_msg); + g_free(balance_msg); +} + +void +mainwin_adjust_balance_release(void) +{ + mainwin_release_info_text(); + setting_volume = FALSE; + read_volume(VOLUME_ADJUSTED); +} + +void +mainwin_set_volume_slider(gint percent) +{ + hslider_set_position(mainwin_volume, (gint) rint((percent * 51) / 100.0)); +} + +void +mainwin_set_balance_slider(gint percent) +{ + hslider_set_position(mainwin_balance, + (gint) rint(((percent * 12) / 100.0) + 12)); +} + +void +mainwin_volume_motion_cb(gint pos) +{ + gint vol = (pos * 100) / 51; + mainwin_adjust_volume_motion(vol); + equalizerwin_set_volume_slider(vol); +} + +void +mainwin_volume_release_cb(gint pos) +{ + mainwin_adjust_volume_release(); +} + +gint +mainwin_balance_frame_cb(gint pos) +{ + return ((abs(pos - 12) * 28) / 13); +} + +void +mainwin_balance_motion_cb(gint pos) +{ + gint bal = ((pos - 12) * 100) / 12; + mainwin_adjust_balance_motion(bal); + equalizerwin_set_balance_slider(bal); +} + +void +mainwin_balance_release_cb(gint pos) +{ + mainwin_adjust_volume_release(); +} + +void +mainwin_set_volume_diff(gint diff) +{ + gint vl, vr, vol; + + input_get_volume(&vl, &vr); + vol = MAX(vl, vr); + vol = CLAMP(vol + diff, 0, 100); + + mainwin_adjust_volume_motion(vol); + setting_volume = FALSE; + mainwin_set_volume_slider(vol); + equalizerwin_set_volume_slider(vol); + read_volume(VOLUME_SET); +} + +void +mainwin_set_balance_diff(gint diff) +{ + gint b; + b = CLAMP(balance + diff, -100, 100); + mainwin_adjust_balance_motion(b); + setting_volume = FALSE; + mainwin_set_balance_slider(b); + equalizerwin_set_balance_slider(b); + read_volume(VOLUME_SET); +} + +void +mainwin_show(gboolean show) +{ + if (show) + mainwin_real_show(); + else + mainwin_real_hide(); +} + +void +mainwin_real_show(void) +{ + cfg.player_visible = TRUE; + + check_set(mainwin_view_menu, "/Show Player", TRUE); + + if (cfg.player_shaded) + vis_clear_data(active_vis); + + mainwin_vis_set_active_vis(MAINWIN_VIS_ACTIVE_MAINWIN); + mainwin_set_shape_mask(); + + if (cfg.show_wm_decorations) { + if (!pposition_broken && cfg.player_x != -1 + && cfg.save_window_position) + gtk_window_move(GTK_WINDOW(mainwin), cfg.player_x, cfg.player_y); + + gtk_widget_show(mainwin); + + if (pposition_broken && cfg.player_x != -1 + && cfg.save_window_position) + gtk_window_move(GTK_WINDOW(mainwin), cfg.player_x, cfg.player_y); + + return; + } + + gtk_widget_show_all(mainwin); + + if (!nullmask) + return; + + g_object_unref(nullmask); + nullmask = NULL; + + gdk_window_set_hints(mainwin->window, 0, 0, + PLAYER_WIDTH, PLAYER_HEIGHT, + PLAYER_WIDTH, PLAYER_HEIGHT, + GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE); + gtk_window_resize(GTK_WINDOW(mainwin), PLAYER_WIDTH, PLAYER_HEIGHT); + + if (cfg.player_x != -1 && cfg.player_y != -1) + gtk_window_move(GTK_WINDOW(mainwin), cfg.player_x, cfg.player_y); + + draw_main_window(TRUE); + + gtk_window_present(GTK_WINDOW(mainwin)); +} + +void +mainwin_real_hide(void) +{ + GdkGC *gc; + GdkColor pattern; + + check_set(mainwin_view_menu, "/Show Player", FALSE); + + if (cfg.player_shaded) + svis_clear_data(mainwin_svis); + + if (!cfg.show_wm_decorations) { + nullmask = gdk_pixmap_new(mainwin->window, 20, 20, 1); + gc = gdk_gc_new(nullmask); + pattern.pixel = 0; + gdk_gc_set_foreground(gc, &pattern); + gdk_draw_rectangle(nullmask, gc, TRUE, 0, 0, 20, 20); + gdk_gc_destroy(gc); + gtk_widget_shape_combine_mask(mainwin, nullmask, 0, 0); + + gdk_window_set_hints(mainwin->window, 0, 0, 0, 0, 0, 0, + GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE); + gdk_window_resize(mainwin->window, 0, 0); + } + + gtk_widget_hide(mainwin); + + mainwin_vis_set_active_vis(MAINWIN_VIS_ACTIVE_PLAYLISTWIN); + cfg.player_visible = FALSE; +} + +static void +mainwin_songname_menu_callback(gpointer data, + guint action, + GtkWidget * item) +{ + GtkCheckMenuItem *check; + + switch (action) { + case MAINWIN_SONGNAME_FILEINFO: + playlist_fileinfo_current(); + break; + case MAINWIN_SONGNAME_JTF: + mainwin_jump_to_file(); + break; + case MAINWIN_SONGNAME_JTT: + mainwin_jump_to_time(); + break; + case MAINWIN_SONGNAME_SCROLL: + check = GTK_CHECK_MENU_ITEM(item); + mainwin_set_title_scroll(gtk_check_menu_item_get_active(check)); + break; + case MAINWIN_SONGNAME_STOPAFTERSONG: + check = GTK_CHECK_MENU_ITEM(item); + cfg.stopaftersong = gtk_check_menu_item_get_active(check); + check_set(mainwin_songname_menu, "/Stop After Current Song", cfg.stopaftersong); + check_set(mainwin_play_menu, "/Stop After Current Song", cfg.stopaftersong); + break; + } +} + +void +mainwin_set_stopaftersong(gboolean stop) +{ + cfg.stopaftersong = stop; + check_set(mainwin_songname_menu, "/Stop After Current Song", cfg.stopaftersong); +} + +static void +mainwin_play_menu_callback(gpointer data, + guint action, + GtkWidget * item) +{ + GtkCheckMenuItem *check; + + switch (action) { + case MAINWIN_OPT_SHUFFLE: + check = GTK_CHECK_MENU_ITEM(item); + cfg.shuffle = gtk_check_menu_item_get_active(check); + playlist_set_shuffle(cfg.shuffle); + tbutton_set_toggled(mainwin_shuffle, cfg.shuffle); + break; + case MAINWIN_OPT_REPEAT: + check = GTK_CHECK_MENU_ITEM(item); + cfg.repeat = gtk_check_menu_item_get_active(check); + tbutton_set_toggled(mainwin_repeat, cfg.repeat); + break; + case MAINWIN_OPT_NPA: + check = GTK_CHECK_MENU_ITEM(item); + cfg.no_playlist_advance = gtk_check_menu_item_get_active(check); + break; + } +} + + +static void +mainwin_view_menu_callback(gpointer data, + guint action, + GtkWidget * item) +{ + switch (action) { + case MAINWIN_OPT_TELAPSED: + set_timer_mode_menu_cb(TIMER_ELAPSED); + break; + case MAINWIN_OPT_TREMAINING: + set_timer_mode_menu_cb(TIMER_REMAINING); + break; + case MAINWIN_OPT_ALWAYS: + mainwin_menurow->mr_always_selected = GTK_CHECK_MENU_ITEM(item)->active; + cfg.always_on_top = mainwin_menurow->mr_always_selected; + widget_draw(WIDGET(mainwin_menurow)); + + if (starting_up == FALSE) + hint_set_always(cfg.always_on_top); + + break; + case MAINWIN_OPT_STICKY: + cfg.sticky = GTK_CHECK_MENU_ITEM(item)->active; + hint_set_sticky(cfg.sticky); + break; + case MAINWIN_OPT_WS: + mainwin_set_shade_menu_cb(GTK_CHECK_MENU_ITEM(item)->active); + break; + case MAINWIN_OPT_PWS: + playlistwin_set_shade(GTK_CHECK_MENU_ITEM(item)->active); + break; + case MAINWIN_OPT_EQWS: + equalizerwin_set_shade_menu_cb(GTK_CHECK_MENU_ITEM(item)->active); + break; + } +} + +void +mainwin_vis_menu_callback(gpointer data, + guint action, + GtkWidget * item) +{ + switch (action) { + case MAINWIN_VIS_ANALYZER: + case MAINWIN_VIS_SCOPE: + case MAINWIN_VIS_OFF: + mainwin_vis_set_type_menu_cb(action - MAINWIN_VIS_ANALYZER); + break; + case MAINWIN_VIS_ANALYZER_NORMAL: + case MAINWIN_VIS_ANALYZER_FIRE: + case MAINWIN_VIS_ANALYZER_VLINES: + mainwin_vis_set_analyzer_mode(action - MAINWIN_VIS_ANALYZER_NORMAL); + break; + case MAINWIN_VIS_ANALYZER_LINES: + case MAINWIN_VIS_ANALYZER_BARS: + mainwin_vis_set_analyzer_type(action - MAINWIN_VIS_ANALYZER_LINES); + break; + case MAINWIN_VIS_ANALYZER_PEAKS: + cfg.analyzer_peaks = GTK_CHECK_MENU_ITEM(item)->active; + break; + case MAINWIN_VIS_SCOPE_DOT: + case MAINWIN_VIS_SCOPE_LINE: + case MAINWIN_VIS_SCOPE_SOLID: + cfg.scope_mode = action - MAINWIN_VIS_SCOPE_DOT; + break; + case MAINWIN_VIS_VU_NORMAL: + case MAINWIN_VIS_VU_SMOOTH: + cfg.vu_mode = action - MAINWIN_VIS_VU_NORMAL; + break; + case MAINWIN_VIS_REFRESH_FULL: + case MAINWIN_VIS_REFRESH_HALF: + case MAINWIN_VIS_REFRESH_QUARTER: + case MAINWIN_VIS_REFRESH_EIGHTH: + mainwin_vis_set_refresh(action - MAINWIN_VIS_REFRESH_FULL); + break; + case MAINWIN_VIS_AFALLOFF_SLOWEST: + case MAINWIN_VIS_AFALLOFF_SLOW: + case MAINWIN_VIS_AFALLOFF_MEDIUM: + case MAINWIN_VIS_AFALLOFF_FAST: + case MAINWIN_VIS_AFALLOFF_FASTEST: + mainwin_vis_set_afalloff(action - MAINWIN_VIS_AFALLOFF_SLOWEST); + break; + case MAINWIN_VIS_PFALLOFF_SLOWEST: + case MAINWIN_VIS_PFALLOFF_SLOW: + case MAINWIN_VIS_PFALLOFF_MEDIUM: + case MAINWIN_VIS_PFALLOFF_FAST: + case MAINWIN_VIS_PFALLOFF_FASTEST: + mainwin_vis_set_pfalloff(action - MAINWIN_VIS_PFALLOFF_SLOWEST); + break; + } +} + +void +mainwin_general_menu_callback(gpointer data, + guint action, + GtkWidget * item) +{ + switch (action) { + case MAINWIN_GENERAL_PREFS: + show_prefs_window(); + break; + case MAINWIN_GENERAL_ABOUT: + show_about_window(); + break; + case MAINWIN_GENERAL_PLAYFILE: + util_run_filebrowser(NO_PLAY_BUTTON); + break; + case MAINWIN_GENERAL_PLAYCD: + play_medium(); + break; + case MAINWIN_GENERAL_ADDCD: + add_medium(); + break; + case MAINWIN_GENERAL_PLAYLOCATION: + mainwin_show_add_url_window(); + break; + case MAINWIN_GENERAL_FILEINFO: + playlist_fileinfo_current(); + break; + case MAINWIN_GENERAL_FOCUSPLWIN: + gtk_window_present(GTK_WINDOW(playlistwin)); + break; + case MAINWIN_GENERAL_SHOWMWIN: + mainwin_show(GTK_CHECK_MENU_ITEM(item)->active); + break; + case MAINWIN_GENERAL_SHOWPLWIN: + if (GTK_CHECK_MENU_ITEM(item)->active) + playlistwin_show(); + else + playlistwin_hide(); + break; + case MAINWIN_GENERAL_SHOWEQWIN: + if (GTK_CHECK_MENU_ITEM(item)->active) + equalizerwin_real_show(); + else + equalizerwin_real_hide(); + break; + case MAINWIN_GENERAL_PREV: + playlist_prev(); + break; + case MAINWIN_GENERAL_PLAY: + mainwin_play_pushed(); + break; + case MAINWIN_GENERAL_PAUSE: + bmp_playback_pause(); + break; + case MAINWIN_GENERAL_STOP: + mainwin_stop_pushed(); + break; + case MAINWIN_GENERAL_NEXT: + playlist_next(); + break; + case MAINWIN_GENERAL_BACK5SEC: + if (bmp_playback_get_playing() + && playlist_get_current_length() != -1) + bmp_playback_seek_relative(-5); + break; + case MAINWIN_GENERAL_FWD5SEC: + if (bmp_playback_get_playing() + && playlist_get_current_length() != -1) + bmp_playback_seek_relative(5); + break; + case MAINWIN_GENERAL_START: + playlist_set_position(0); + break; + case MAINWIN_GENERAL_JTT: + mainwin_jump_to_time(); + break; + case MAINWIN_GENERAL_JTF: + mainwin_jump_to_file(); + break; + case MAINWIN_GENERAL_EXIT: + mainwin_quit_cb(); + break; + } +} + +static void +mainwin_mr_change(MenuRowItem i) +{ + switch (i) { + case MENUROW_NONE: + mainwin_set_info_text(); + break; + case MENUROW_OPTIONS: + mainwin_lock_info_text(_("OPTIONS MENU")); + break; + case MENUROW_ALWAYS: + if (mainwin_menurow->mr_always_selected) + mainwin_lock_info_text(_("DISABLE ALWAYS ON TOP")); + else + mainwin_lock_info_text(_("ENABLE ALWAYS ON TOP")); + break; + case MENUROW_FILEINFOBOX: + mainwin_lock_info_text(_("FILE INFO BOX")); + break; + case MENUROW_DOUBLESIZE: + mainwin_lock_info_text(_("** DOUBLESIZE HAS BEEN REMOVED **")); + break; + case MENUROW_VISUALIZATION: + mainwin_lock_info_text(_("VISUALIZATION MENU")); + break; + } +} + +static void +mainwin_mr_release(MenuRowItem i) +{ + GdkModifierType modmask; + GtkWidget *widget; + gint x, y; + + switch (i) { + case MENUROW_OPTIONS: + gdk_window_get_pointer(NULL, &x, &y, &modmask); + util_item_factory_popup(mainwin_view_menu, x, y, 1, + GDK_CURRENT_TIME); + break; + case MENUROW_ALWAYS: + widget = + gtk_item_factory_get_widget(mainwin_view_menu, + "/Always On Top"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(widget), + mainwin_menurow->mr_always_selected); + break; + case MENUROW_FILEINFOBOX: + playlist_fileinfo_current(); + break; + case MENUROW_DOUBLESIZE: + /* double size removed, do nothing */ + break; + case MENUROW_VISUALIZATION: + gdk_window_get_pointer(NULL, &x, &y, &modmask); + util_item_factory_popup(mainwin_vis_menu, x, y, 1, GDK_CURRENT_TIME); + break; + case MENUROW_NONE: + break; + } + mainwin_release_info_text(); +} + +static void +run_no_audiocd_dialog(void) +{ + const gchar *markup = + N_("<b><big>No playable CD found.</big></b>\n\n" + "No CD inserted, or inserted CD is not an audio CD.\n"); + + GtkWidget *dialog = + gtk_message_dialog_new_with_markup(GTK_WINDOW(mainwin), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _(markup)); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); +} + +static void +run_no_output_device_dialog(void) +{ + const gchar *markup = + N_("<b><big>Couldn't open audio.</big></b>\n\n" + "Please check that:\n" + "1. You have the correct output plugin selected.\n" + "2. No other programs is blocking the soundcard.\n" + "3. Your soundcard is configured properly.\n"); + + GtkWidget *dialog = + gtk_message_dialog_new_with_markup(GTK_WINDOW(mainwin), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _(markup)); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); +} + + +void +add_medium(void) +{ + GList *list, *node; + gchar *filename; + gchar *path; + ConfigDb *db; + + db = bmp_cfg_db_open(); + + bmp_cfg_db_get_string(db, "CDDA", "directory", &path); + bmp_cfg_db_close(db); + + if (!(list = input_scan_dir(path))) { + run_no_audiocd_dialog(); + return; + } + + for (node = list; node; node = g_list_next(node)) { + filename = g_build_filename(path, node->data, NULL); + playlist_add(filename); + g_free(filename); + g_free(node->data); + } + + g_free(path); + g_list_free(list); + +} + +void +play_medium(void) +{ + GList *list, *node; + gchar *filename; + gchar *path; + ConfigDb *db; + + db = bmp_cfg_db_open(); + bmp_cfg_db_get_string(db, "CDDA", "directory", &path); + bmp_cfg_db_close(db); + + if (!(list = input_scan_dir(path))) { + run_no_audiocd_dialog(); + return; + } + + playlist_clear(); + + for (node = list; node; node = g_list_next(node)) { + filename = g_build_filename(path, node->data, NULL); + playlist_add(filename); + g_free(filename); + g_free(node->data); + } + + g_free(path); + g_list_free(list); + + playlist_set_position(0); + bmp_playback_initiate(); +} + +void +read_volume(gint when) +{ + static gint pvl = 0, pvr = 0; + static gint times = VOLSET_DISP_TIMES; + static gboolean changing = FALSE; + + gint vl, vr, b, v; + + input_get_volume(&vl, &vr); + + switch (when) { + case VOLSET_STARTUP: + vl = CLAMP(vl, 0, 100); + vr = CLAMP(vr, 0, 100); + pvl = vl; + pvr = vr; + v = MAX(vl, vr); + if (vl > vr) + b = (gint) rint(((gdouble) vr / vl) * 100) - 100; + else if (vl < vr) + b = 100 - (gint) rint(((gdouble) vl / vr) * 100); + else + b = 0; + + balance = b; + mainwin_set_volume_slider(v); + equalizerwin_set_volume_slider(v); + mainwin_set_balance_slider(b); + equalizerwin_set_balance_slider(b); + return; + + case VOLSET_UPDATE: + if (vl == -1 || vr == -1) + return; + + if (setting_volume) { + pvl = vl; + pvr = vr; + return; + } + + if (pvr == vr && pvl == vl && changing) { + if (times < VOLSET_DISP_TIMES) + times++; + else { + mainwin_release_info_text(); + changing = FALSE; + } + } + else if (pvr != vr || pvl != vl) { + gchar *tmp; + + v = MAX(vl, vr); + if (vl > vr) + b = (gint) rint(((gdouble) vr / vl) * 100) - 100; + else if (vl < vr) + b = 100 - (gint) rint(((gdouble) vl / vr) * 100); + else + b = 0; + + if (MAX(vl, vr) != MAX(pvl, pvr)) + tmp = g_strdup_printf(_("VOLUME: %d%%"), v); + else { + if (vl > vr) { + tmp = g_strdup_printf(_("BALANCE: %d%% LEFT"), -b); + } + else if (vr == vl) + tmp = g_strdup_printf(_("BALANCE: CENTER")); + else { /* (vl < vr) */ + tmp = g_strdup_printf(_("BALANCE: %d%% RIGHT"), b); + } + } + mainwin_lock_info_text(tmp); + g_free(tmp); + + pvr = vr; + pvl = vl; + times = 0; + changing = TRUE; + mainwin_set_volume_slider(v); + equalizerwin_set_volume_slider(v); + + /* Don't change the balance slider if the volume has been + * set to zero. The balance can be anything, and our best + * guess is what is was before. */ + if (v > 0) { + balance = b; + mainwin_set_balance_slider(b); + equalizerwin_set_balance_slider(b); + } + } + break; + + case VOLUME_ADJUSTED: + pvl = vl; + pvr = vr; + break; + + case VOLUME_SET: + times = 0; + changing = TRUE; + pvl = vl; + pvr = vr; + break; + } +} + + +/* TODO: HAL! */ +gboolean +can_play_cd(void) +{ + GList *ilist; + + for (ilist = get_input_list(); ilist; ilist = g_list_next(ilist)) { + InputPlugin *ip = INPUT_PLUGIN(ilist->data); + + if (!g_ascii_strcasecmp(g_basename(ip->filename), + PLUGIN_FILENAME("cdaudio"))) { + return TRUE; + } + } + + return FALSE; +} + + +static void +set_timer_mode(TimerMode mode) +{ + if (mode == TIMER_ELAPSED) + check_set(mainwin_view_menu, "/Time Elapsed", TRUE); + else + check_set(mainwin_view_menu, "/Time Remaining", TRUE); +} + +static void +set_timer_mode_menu_cb(TimerMode mode) +{ + cfg.timer_mode = mode; +} + + +void +mainwin_setup_menus(void) +{ + set_timer_mode(cfg.timer_mode); + + /* View menu */ + + check_set(mainwin_view_menu, "/Always On Top", cfg.always_on_top); + check_set(mainwin_view_menu, "/Put on All Workspaces", cfg.sticky); + check_set(mainwin_view_menu, "/Roll up Player", cfg.player_shaded); + check_set(mainwin_view_menu, "/Roll up Playlist Editor", cfg.playlist_shaded); + check_set(mainwin_view_menu, "/Roll up Equalizer", cfg.equalizer_shaded); + + /* Songname menu */ + + check_set(mainwin_songname_menu, "/Autoscroll Songname", cfg.autoscroll); + check_set(mainwin_songname_menu, "/Stop After Current Song", cfg.stopaftersong); + + /* Playback menu */ + + check_set(mainwin_play_menu, "/Repeat", cfg.repeat); + check_set(mainwin_play_menu, "/Shuffle", cfg.shuffle); + check_set(mainwin_play_menu, "/No Playlist Advance", cfg.no_playlist_advance); + + /* Visualization menu */ + + check_set(mainwin_vis_menu, + mainwin_vis_menu_entries[MAINWIN_VIS_MENU_VIS_MODE + + cfg.vis_type].path, TRUE); + check_set(mainwin_vis_menu, + mainwin_vis_menu_entries[MAINWIN_VIS_MENU_ANALYZER_MODE + + cfg.analyzer_mode].path, TRUE); + check_set(mainwin_vis_menu, + mainwin_vis_menu_entries[MAINWIN_VIS_MENU_ANALYZER_TYPE + + cfg.analyzer_type].path, TRUE); + check_set(mainwin_vis_menu, + mainwin_vis_menu_entries[MAINWIN_VIS_MENU_ANALYZER_PEAKS]. + path, cfg.analyzer_peaks); + check_set(mainwin_vis_menu, + mainwin_vis_menu_entries[MAINWIN_VIS_MENU_SCOPE_MODE + + cfg.scope_mode].path, TRUE); + check_set(mainwin_vis_menu, + mainwin_vis_menu_entries[MAINWIN_VIS_MENU_WSHADEVU_MODE + + cfg.vu_mode].path, TRUE); + check_set(mainwin_vis_menu, + mainwin_vis_menu_entries[MAINWIN_VIS_MENU_REFRESH_RATE + + cfg.vis_refresh].path, TRUE); + check_set(mainwin_vis_menu, + mainwin_vis_menu_entries[MAINWIN_VIS_MENU_AFALLOFF + + cfg.analyzer_falloff].path, TRUE); + check_set(mainwin_vis_menu, + mainwin_vis_menu_entries[MAINWIN_VIS_MENU_PFALLOFF + + cfg.peaks_falloff].path, TRUE); +} + +static void +mainwin_create_widgets(void) +{ + mainwin_menubtn = + create_pbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 6, 3, 9, 9, + 0, 0, 0, 9, mainwin_menubtn_cb, SKIN_TITLEBAR); + mainwin_menubtn->pb_allow_draw = FALSE; + mainwin_minimize = + create_pbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 244, 3, 9, + 9, 9, 0, 9, 9, mainwin_minimize_cb, SKIN_TITLEBAR); + mainwin_minimize->pb_allow_draw = FALSE; + mainwin_shade = + create_pbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 254, 3, 9, + 9, 0, cfg.player_shaded ? 27 : 18, 9, + cfg.player_shaded ? 27 : 18, mainwin_shade_toggle, + SKIN_TITLEBAR); + mainwin_shade->pb_allow_draw = FALSE; + mainwin_close = + create_pbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 264, 3, 9, + 9, 18, 0, 18, 9, mainwin_quit_cb, SKIN_TITLEBAR); + mainwin_close->pb_allow_draw = FALSE; + + mainwin_rew = + create_pbutton_ex(&mainwin_wlist, mainwin_bg, mainwin_gc, 16, 88, 23, + 18, 0, 0, 0, 18, mainwin_rev_pushed, mainwin_rev_release, + SKIN_CBUTTONS, SKIN_CBUTTONS); + mainwin_play = + create_pbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 39, 88, 23, + 18, 23, 0, 23, 18, mainwin_play_pushed, SKIN_CBUTTONS); + mainwin_pause = + create_pbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 62, 88, 23, + 18, 46, 0, 46, 18, bmp_playback_pause, SKIN_CBUTTONS); + mainwin_stop = + create_pbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 85, 88, 23, + 18, 69, 0, 69, 18, mainwin_stop_pushed, SKIN_CBUTTONS); +#if 0 + mainwin_fwd = + create_pbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 108, 88, 22, + 18, 92, 0, 92, 18, playlist_next, SKIN_CBUTTONS); +#endif + mainwin_fwd = + create_pbutton_ex(&mainwin_wlist, mainwin_bg, mainwin_gc, 108, 88, 22, + 18, 92, 0, 92, 18, mainwin_fwd_pushed, mainwin_fwd_release, + SKIN_CBUTTONS, SKIN_CBUTTONS); + + mainwin_eject = + create_pbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 136, 89, 22, + 16, 114, 0, 114, 16, mainwin_eject_pushed, + SKIN_CBUTTONS); + + mainwin_srew = + create_sbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 169, 4, 8, + 7, playlist_prev); + mainwin_splay = + create_sbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 177, 4, 10, + 7, mainwin_play_pushed); + mainwin_spause = + create_sbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 187, 4, 10, + 7, bmp_playback_pause); + mainwin_sstop = + create_sbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 197, 4, 9, + 7, mainwin_stop_pushed); + mainwin_sfwd = + create_sbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 206, 4, 8, + 7, playlist_next); + mainwin_seject = + create_sbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 216, 4, 9, + 7, mainwin_eject_pushed); + + mainwin_shuffle = + create_tbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 164, 89, 46, + 15, 28, 0, 28, 15, 28, 30, 28, 45, + mainwin_shuffle_pushed, SKIN_SHUFREP); + + mainwin_repeat = + create_tbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 210, 89, 28, + 15, 0, 0, 0, 15, 0, 30, 0, 45, + mainwin_repeat_pushed, SKIN_SHUFREP); + + mainwin_eq = + create_tbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 219, 58, 23, + 12, 0, 61, 46, 61, 0, 73, 46, 73, equalizerwin_show, + SKIN_SHUFREP); + tbutton_set_toggled(mainwin_eq, cfg.equalizer_visible); + mainwin_pl = + create_tbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 242, 58, 23, + 12, 23, 61, 69, 61, 23, 73, 69, 73, + mainwin_pl_pushed, SKIN_SHUFREP); + tbutton_set_toggled(mainwin_pl, cfg.playlist_visible); + + mainwin_info = + create_textbox(&mainwin_wlist, mainwin_bg, mainwin_gc, 112, 27, + 153, 1, SKIN_TEXT); + textbox_set_scroll(mainwin_info, cfg.autoscroll); + textbox_set_xfont(mainwin_info, cfg.mainwin_use_xfont, cfg.mainwin_font); + + mainwin_othertext = + create_textbox(&mainwin_wlist, mainwin_bg, mainwin_gc, 112, 43, + 153, 1, SKIN_TEXT); + + mainwin_rate_text = + create_textbox(&mainwin_wlist, mainwin_bg, mainwin_gc, 111, 43, 15, + 0, SKIN_TEXT); + mainwin_freq_text = + create_textbox(&mainwin_wlist, mainwin_bg, mainwin_gc, 156, 43, 10, + 0, SKIN_TEXT); + + mainwin_menurow = + create_menurow(&mainwin_wlist, mainwin_bg, mainwin_gc, 10, 22, 304, + 0, 304, 44, mainwin_mr_change, mainwin_mr_release, + SKIN_TITLEBAR); + mainwin_menurow->mr_doublesize_selected = FALSE; + mainwin_menurow->mr_always_selected = cfg.always_on_top; + + mainwin_volume = + create_hslider(&mainwin_wlist, mainwin_bg, mainwin_gc, 107, 57, 68, + 13, 15, 422, 0, 422, 14, 11, 15, 0, 0, 51, + mainwin_volume_frame_cb, mainwin_volume_motion_cb, + mainwin_volume_release_cb, SKIN_VOLUME); + mainwin_balance = + create_hslider(&mainwin_wlist, mainwin_bg, mainwin_gc, 177, 57, 38, + 13, 15, 422, 0, 422, 14, 11, 15, 9, 0, 24, + mainwin_balance_frame_cb, mainwin_balance_motion_cb, + mainwin_balance_release_cb, SKIN_BALANCE); + + mainwin_monostereo = + create_monostereo(&mainwin_wlist, mainwin_bg, mainwin_gc, 212, 41, + SKIN_MONOSTEREO); + + mainwin_playstatus = + create_playstatus(&mainwin_wlist, mainwin_bg, mainwin_gc, 24, 28); + + mainwin_minus_num = + create_number(&mainwin_wlist, mainwin_bg, mainwin_gc, 36, 26, + SKIN_NUMBERS); + widget_hide(WIDGET(mainwin_minus_num)); + mainwin_10min_num = + create_number(&mainwin_wlist, mainwin_bg, mainwin_gc, 48, 26, + SKIN_NUMBERS); + widget_hide(WIDGET(mainwin_10min_num)); + + mainwin_min_num = + create_number(&mainwin_wlist, mainwin_bg, mainwin_gc, 60, 26, + SKIN_NUMBERS); + widget_hide(WIDGET(mainwin_min_num)); + + mainwin_10sec_num = + create_number(&mainwin_wlist, mainwin_bg, mainwin_gc, 78, 26, + SKIN_NUMBERS); + widget_hide(WIDGET(mainwin_10sec_num)); + + mainwin_sec_num = + create_number(&mainwin_wlist, mainwin_bg, mainwin_gc, 90, 26, + SKIN_NUMBERS); + widget_hide(WIDGET(mainwin_sec_num)); + + mainwin_about = + create_sbutton(&mainwin_wlist, mainwin_bg, mainwin_gc, 247, 83, 20, + 25, show_about_window); + + mainwin_vis = + create_vis(&mainwin_wlist, mainwin_bg, mainwin->window, mainwin_gc, + 24, 43, 76); + mainwin_svis = create_svis(&mainwin_wlist, mainwin_bg, mainwin_gc, 79, 5); + active_vis = mainwin_vis; + + mainwin_position = + create_hslider(&mainwin_wlist, mainwin_bg, mainwin_gc, 16, 72, 248, + 10, 248, 0, 278, 0, 29, 10, 10, 0, 0, 219, NULL, + mainwin_position_motion_cb, + mainwin_position_release_cb, SKIN_POSBAR); + widget_hide(WIDGET(mainwin_position)); + + mainwin_sposition = + create_hslider(&mainwin_wlist, mainwin_bg, mainwin_gc, 226, 4, 17, + 7, 17, 36, 17, 36, 3, 7, 36, 0, 1, 13, + mainwin_spos_frame_cb, mainwin_spos_motion_cb, + mainwin_spos_release_cb, SKIN_TITLEBAR); + widget_hide(WIDGET(mainwin_sposition)); + + mainwin_stime_min = + create_textbox(&mainwin_wlist, mainwin_bg, mainwin_gc, 130, 4, 15, + FALSE, SKIN_TEXT); + mainwin_stime_sec = + create_textbox(&mainwin_wlist, mainwin_bg, mainwin_gc, 147, 4, 10, + FALSE, SKIN_TEXT); + + if (!cfg.player_shaded) { + widget_hide(WIDGET(mainwin_svis)); + widget_hide(WIDGET(mainwin_srew)); + widget_hide(WIDGET(mainwin_splay)); + widget_hide(WIDGET(mainwin_spause)); + widget_hide(WIDGET(mainwin_sstop)); + widget_hide(WIDGET(mainwin_sfwd)); + widget_hide(WIDGET(mainwin_seject)); + widget_hide(WIDGET(mainwin_stime_min)); + widget_hide(WIDGET(mainwin_stime_sec)); + } + + err = gtk_message_dialog_new(GTK_WINDOW(mainwin), GTK_DIALOG_DESTROY_WITH_PARENT|GTK_DIALOG_MODAL, + GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,"Error in Audacious."); + + + gtk_window_set_position(GTK_WINDOW(err), GTK_WIN_POS_CENTER); + /* Dang well better set an error message or you'll see this */ + gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(err), + "Boo! Bad stuff! Booga Booga!"); + +} + +static void +mainwin_create_window(void) +{ + gint width, height; + + mainwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(mainwin), _("Audacious")); + gtk_window_set_role(GTK_WINDOW(mainwin), "player"); + gtk_window_set_resizable(GTK_WINDOW(mainwin), FALSE); + + width = MAINWIN_WIDTH; + height = cfg.player_shaded ? MAINWIN_SHADED_HEIGHT : MAINWIN_HEIGHT; + + gtk_widget_set_size_request(mainwin, width, height); + gtk_widget_set_app_paintable(mainwin, TRUE); + + dock_window_list = dock_window_set_decorated(dock_window_list, + GTK_WINDOW(mainwin), + cfg.show_wm_decorations); + + if (cfg.player_x != -1 && cfg.save_window_position) + gtk_window_move(GTK_WINDOW(mainwin), cfg.player_x, cfg.player_y); + + gtk_widget_add_events(mainwin, + GDK_FOCUS_CHANGE_MASK | GDK_BUTTON_MOTION_MASK | + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_SCROLL_MASK | GDK_KEY_PRESS_MASK | + GDK_VISIBILITY_NOTIFY_MASK); + gtk_widget_realize(mainwin); + + util_set_cursor(mainwin); + + g_signal_connect(mainwin, "destroy", G_CALLBACK(mainwin_destroy), NULL); + g_signal_connect(mainwin, "button_press_event", + G_CALLBACK(mainwin_mouse_button_press), NULL); + g_signal_connect(mainwin, "scroll_event", + G_CALLBACK(mainwin_scrolled), NULL); + g_signal_connect(mainwin, "button_release_event", + G_CALLBACK(mainwin_mouse_button_release), NULL); + g_signal_connect(mainwin, "motion_notify_event", + G_CALLBACK(mainwin_motion), NULL); + g_signal_connect_after(mainwin, "focus_in_event", + G_CALLBACK(mainwin_focus_in), NULL); + g_signal_connect_after(mainwin, "focus_out_event", + G_CALLBACK(mainwin_focus_out), NULL); + g_signal_connect(mainwin, "configure_event", + G_CALLBACK(mainwin_configure), NULL); + g_signal_connect(mainwin, "style_set", + G_CALLBACK(mainwin_set_back_pixmap), NULL); + + bmp_drag_dest_set(mainwin); + + g_signal_connect(mainwin, "key_press_event", + G_CALLBACK(mainwin_keypress), NULL); +} + +static void +mainwin_create_menus(void) +{ + mainwin_general_menu = create_menu(mainwin_general_menu_entries, + mainwin_general_menu_entries_num, + mainwin_accel); + + mainwin_play_menu = create_menu(mainwin_playback_menu_entries, + mainwin_playback_menu_entries_num, + mainwin_accel); + + mainwin_view_menu = create_menu(mainwin_view_menu_entries, + mainwin_view_menu_entries_num, + mainwin_accel); + + mainwin_songname_menu = create_menu(mainwin_songname_menu_entries, + mainwin_songname_menu_entries_num, + mainwin_accel); + + mainwin_add_menu = create_menu(mainwin_add_menu_entries, + mainwin_add_menu_entries_num, + mainwin_accel); + + mainwin_vis_menu = create_menu(mainwin_vis_menu_entries, + mainwin_vis_menu_entries_num, + mainwin_accel); + + make_submenu(mainwin_general_menu, "/View", mainwin_view_menu); + make_submenu(mainwin_general_menu, "/Playback", mainwin_play_menu); + make_submenu(mainwin_general_menu, "/Visualization", mainwin_vis_menu); + + gtk_window_add_accel_group(GTK_WINDOW(mainwin), mainwin_accel); +} + +void +mainwin_create(void) +{ + mainwin_create_window(); + + mainwin_accel = gtk_accel_group_new(); + mainwin_create_menus(); + + mainwin_gc = gdk_gc_new(mainwin->window); + mainwin_bg = gdk_pixmap_new(mainwin->window, + MAINWIN_WIDTH, MAINWIN_HEIGHT, -1); + mainwin_set_back_pixmap(); + mainwin_create_widgets(); + + vis_set_window(mainwin_vis, mainwin->window); +} + +void +mainwin_attach_idle_func(void) +{ + mainwin_timeout_id = g_timeout_add(MAINWIN_UPDATE_INTERVAL, + mainwin_idle_func, NULL); +} + +static void +idle_func_update_song_info(gint time) +{ + gint length, t; + gchar stime_prefix; + + length = playlist_get_current_length(); + if (bmp_playback_get_playing()) + playlistwin_set_time(time, length, cfg.timer_mode); + else + playlistwin_hide_timer(); + input_update_vis(time); + + if (cfg.timer_mode == TIMER_REMAINING) { + if (length != -1) { + number_set_number(mainwin_minus_num, 11); + t = length - time; + stime_prefix = '-'; + } + else { + number_set_number(mainwin_minus_num, 10); + t = time; + stime_prefix = ' '; + } + } + else { + number_set_number(mainwin_minus_num, 10); + t = time; + stime_prefix = ' '; + } + t /= 1000; + + /* Show the time in the format HH:MM when we have more than 100 + * minutes. */ + if (t >= 100 * 60) + t /= 60; + number_set_number(mainwin_10min_num, t / 600); + number_set_number(mainwin_min_num, (t / 60) % 10); + number_set_number(mainwin_10sec_num, (t / 10) % 6); + number_set_number(mainwin_sec_num, t % 10); + + if (!mainwin_sposition->hs_pressed) { + gchar *time_str; + + time_str = g_strdup_printf("%c%2.2d", stime_prefix, t / 60); + textbox_set_text(mainwin_stime_min, time_str); + g_free(time_str); + + time_str = g_strdup_printf("%2.2d", t % 60); + textbox_set_text(mainwin_stime_sec, time_str); + g_free(time_str); + } + + time /= 1000; + length /= 1000; + if (length > 0) { + if (time > length) { + hslider_set_position(mainwin_position, 219); + hslider_set_position(mainwin_sposition, 13); + } + /* update the slider position ONLY if there is not a seek in progress */ + else if (seek_state == MAINWIN_SEEK_NIL) { + hslider_set_position(mainwin_position, (time * 219) / length); + hslider_set_position(mainwin_sposition, + ((time * 12) / length) + 1); + } + } + else { + hslider_set_position(mainwin_position, 0); + hslider_set_position(mainwin_sposition, 1); + } +} + +static gboolean +mainwin_idle_func(gpointer data) +{ + static gint count = 0; + gint time = 0; + + /* run audcore events, then run our own. --nenolod */ + switch((time = audcore_generic_events())) + { + case -2: + /* no usable output device */ + GDK_THREADS_ENTER(); + run_no_output_device_dialog(); + mainwin_stop_pushed(); + GDK_THREADS_LEAVE(); + ev_waiting = FALSE; + break; + + default: + idle_func_update_song_info(time); + /* nothing at this time */ + } + + GDK_THREADS_ENTER(); + + if (bmp_playback_get_playing()) + vis_playback_start(); + else + vis_playback_stop(); + + draw_main_window(mainwin_force_redraw); + + if (!count) { + read_volume(VOLSET_UPDATE); + count = 10; + } + else + count--; + + mainwin_force_redraw = FALSE; + draw_playlist_window(FALSE); + draw_equalizer_window(FALSE); + + if (mainwin_title_text) { + G_LOCK(mainwin_title); + gtk_window_set_title(GTK_WINDOW(mainwin), mainwin_title_text); + g_free(mainwin_title_text); + mainwin_title_text = NULL; + G_UNLOCK(mainwin_title); + + mainwin_set_info_text(); + playlistwin_update_list(); + } + + /* tristate buttons seek */ + if ( seek_state != MAINWIN_SEEK_NIL ) + { + GTimeVal now_time; + GTimeVal delta_time; + gulong now_dur; + g_get_current_time(&now_time); + + delta_time.tv_usec = now_time.tv_usec - cb_time.tv_usec; + delta_time.tv_sec = now_time.tv_sec - cb_time.tv_sec; + + now_dur = labs((delta_time.tv_sec * 1000) + (glong) (delta_time.tv_usec / 1000)); + + if ( now_dur > TRISTATE_THRESHOLD ) + { + gint np; + if (seek_state == MAINWIN_SEEK_REV) + np = seek_initial_pos - labs((gulong)(now_dur/100)); /* seek back */ + else + np = seek_initial_pos + labs((gulong)(now_dur/100)); /* seek forward */ + + /* boundaries check */ + if (np < 0 ) + np = 0; + else if ( np > 219 ) + np = 219; + + hslider_set_position( mainwin_position , np ); + mainwin_position_motion_cb( np ); + } + } + + GDK_THREADS_LEAVE(); + + /* + if (seek_state == MAINWIN_SEEK_REV) + bmp_playback_seek(CLAMP(bmp_playback_get_time() - 1000, 0, + playlist_get_current_length()) / 1000); + else if (seek_state == MAINWIN_SEEK_FWD) + bmp_playback_seek(CLAMP(bmp_playback_get_time() + 1000, 0, + playlist_get_current_length()) / 1000); + */ + + return TRUE; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/mainwin.h Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,193 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef MAINWIN_H +#define MAINWIN_H + +#include <gtk/gtk.h> + +#include "widgets/widgetcore.h" + +/* yes, main window size is fixed */ +#define MAINWIN_WIDTH (gint)275 +#define MAINWIN_HEIGHT (gint)116 +#define MAINWIN_TITLEBAR_HEIGHT (gint)14 +#define MAINWIN_SHADED_HEIGHT MAINWIN_TITLEBAR_HEIGHT + +#define MAINWIN_UPDATE_INTERVAL 10 + +#define MAINWIN_DEFAULT_POS_X 20 +#define MAINWIN_DEFAULT_POS_Y 20 + +#define MAINWIN_DEFAULT_FONT "Sans Bold 9" + + +typedef enum { + TIMER_ELAPSED, + TIMER_REMAINING +} TimerMode; + +enum { + MAINWIN_GENERAL_ABOUT, + + MAINWIN_GENERAL_PLAYFILE, + MAINWIN_GENERAL_PLAYLOCATION, + + MAINWIN_GENERAL_FILEINFO, + MAINWIN_GENERAL_PREFS, + + MAINWIN_GENERAL_SHOWMWIN, + MAINWIN_GENERAL_SHOWPLWIN, + + MAINWIN_GENERAL_FOCUSMWIN, + MAINWIN_GENERAL_FOCUSPLWIN, + + MAINWIN_GENERAL_SHOWEQWIN, + MAINWIN_GENERAL_PLAYCD, + MAINWIN_GENERAL_EXIT, + + MAINWIN_GENERAL_ADDCD, + + MAINWIN_GENERAL_PREV, + MAINWIN_GENERAL_PLAY, + MAINWIN_GENERAL_PAUSE, + MAINWIN_GENERAL_STOP, + MAINWIN_GENERAL_NEXT, + MAINWIN_GENERAL_STOPFADE, + MAINWIN_GENERAL_BACK5SEC, + MAINWIN_GENERAL_FWD5SEC, + MAINWIN_GENERAL_START, + MAINWIN_GENERAL_BACK10, + MAINWIN_GENERAL_FWD10, + MAINWIN_GENERAL_JTT, + MAINWIN_GENERAL_JTF, + MAINWIN_GENERAL_QUEUE, + MAINWIN_GENERAL_CQUEUE, + MAINWIN_GENERAL_VOLUP, + MAINWIN_GENERAL_VOLDOWN +}; + +extern GtkWidget *mainwin; +extern GtkWidget *err; +extern GdkGC *mainwin_gc; + +extern GtkAccelGroup *mainwin_accel; + +extern gboolean mainwin_moving; +extern gboolean mainwin_focus; + +extern GtkWidget *mainwin_jtf; + +extern GtkItemFactory *mainwin_general_menu; +extern GtkItemFactory *mainwin_vis_menu; +extern GtkItemFactory *mainwin_play_menu, *mainwin_view_menu; + +extern TextBox *mainwin_stime_min, *mainwin_stime_sec; +extern TextBox *mainwin_info; +extern TButton *mainwin_shuffle, *mainwin_repeat, *mainwin_eq, *mainwin_pl; + +extern Vis *active_vis; +extern Vis *mainwin_vis; +extern SVis *mainwin_svis; + +extern PlayStatus *mainwin_playstatus; + +extern Number *mainwin_minus_num, *mainwin_10min_num, *mainwin_min_num; +extern Number *mainwin_10sec_num, *mainwin_sec_num; + +extern HSlider *mainwin_sposition; + +void mainwin_create(void); +void read_volume(gint when); +void play_medium(void); +void add_medium(void); + +void draw_main_window(gboolean); + +void mainwin_quit_cb(void); +void mainwin_lock_info_text(const gchar * text); +void mainwin_release_info_text(void); +void mainwin_play_pushed(void); +void mainwin_stop_pushed(void); +void mainwin_eject_pushed(void); + +void mainwin_rev_pushed(void); +void mainwin_rev_release(void); +void mainwin_fwd_pushed(void); +void mainwin_fwd_release(void); + +void mainwin_set_back_pixmap(void); + +void mainwin_adjust_volume_motion(gint v); +void mainwin_adjust_volume_release(void); +void mainwin_adjust_balance_motion(gint b); +void mainwin_adjust_balance_release(void); +void mainwin_set_volume_slider(gint percent); +void mainwin_set_balance_slider(gint percent); + +void mainwin_vis_set_type(VisType mode); + +void mainwin_set_info_text(void); +void mainwin_set_song_info(gint rate, gint freq, gint nch); +void mainwin_clear_song_info(void); +void mainwin_set_stopaftersong(gboolean stop); + +void mainwin_set_always_on_top(gboolean always); +void mainwin_set_volume_diff(gint diff); +void mainwin_set_balance_diff(gint diff); + +void mainwin_show(gboolean); +void mainwin_real_show(void); +void mainwin_real_hide(void); +void mainwin_move(gint x, gint y); +void mainwin_shuffle_pushed(gboolean toggled); +void mainwin_repeat_pushed(gboolean toggled); +void mainwin_disable_seekbar(void); +void mainwin_set_title(const gchar * text); +void mainwin_show_add_url_window(void); +void mainwin_minimize_cb(void); +void mainwin_general_menu_callback(gpointer cb_data, + guint action, + GtkWidget * widget); + +void mainwin_attach_idle_func(void); +void mainwin_drag_data_received(GtkWidget * widget, + GdkDragContext * context, + gint x, + gint y, + GtkSelectionData * selection_data, + guint info, + guint time, + gpointer user_data); + +void mainwin_setup_menus(void); + +void mainwin_jump_to_file(void); +void mainwin_jump_to_time(void); + +void mainwin_ewmh_activate(void); + +/* FIXME: placed here for now */ +void playback_get_sample_params(gint * bitrate, + gint * frequency, + gint * numchannels); + +#endif
--- a/audacious/playback.c Thu Sep 07 22:00:35 2006 -0700 +++ b/audacious/playback.c Thu Sep 07 22:26:54 2006 -0700 @@ -40,15 +40,31 @@ #include "input.h" #include "main.h" +#include "mainwin.h" +#include "equalizer.h" #include "output.h" #include "playlist.h" +#include "ui_playlist.h" #include "skinwin.h" #include "urldecode.h" #include "util.h" #include "playback.h" -#include "interface.h" + + +/* FIXME: yuck!! this shouldn't be here... */ +void +bmp_playback_set_random_skin(void) +{ + SkinNode *node; + guint32 randval; + + /* Get a random value to select the skin to use */ + randval = g_random_int_range(0, g_list_length(skinlist)); + node = g_list_nth(skinlist, randval)->data; + bmp_active_skin_load(node->path); +} gint bmp_playback_get_time(void) @@ -73,11 +89,9 @@ if (bmp_playback_get_playing()) bmp_playback_stop(); -#if 0 vis_clear_data(mainwin_vis); svis_clear_data(mainwin_svis); mainwin_disable_seekbar(); -#endif entry = playlist_get_entry_to_play(); @@ -88,9 +102,7 @@ return; if (bmp_playback_get_time() != -1) { -#if 0 equalizerwin_load_auto_preset(entry->filename); -#endif input_set_eq(cfg.equalizer_active, cfg.equalizer_preamp, cfg.equalizer_bands); output_set_eq(cfg.equalizer_active, cfg.equalizer_preamp, @@ -112,12 +124,10 @@ ip_data.paused = !ip_data.paused; -#if 0 if (ip_data.paused) playstatus_set_status(mainwin_playstatus, STATUS_PAUSE); else playstatus_set_status(mainwin_playstatus, STATUS_PLAY); -#endif if (get_current_input_plugin()->pause) get_current_input_plugin()->pause(ip_data.paused); @@ -146,7 +156,7 @@ } ip_data.buffering = FALSE; - current_interface->buffering_notify(FALSE); + playstatus_set_status_buffering(mainwin_playstatus, FALSE); ip_data.playing = FALSE; } @@ -181,7 +191,7 @@ "You have not selected an output plugin."); GtkWidget *dialog = - gtk_message_dialog_new_with_markup(GTK_WINDOW(current_interface->parentwin), + gtk_message_dialog_new_with_markup(GTK_WINDOW(mainwin), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
--- a/audacious/playlist.c Thu Sep 07 22:00:35 2006 -0700 +++ b/audacious/playlist.c Thu Sep 07 22:26:54 2006 -0700 @@ -42,16 +42,18 @@ #include "input.h" #include "main.h" +#include "mainwin.h" #include "libaudacious/util.h" #include "libaudacious/configdb.h" #include "libaudacious/vfs.h" +#include "equalizer.h" #include "playback.h" #include "playlist.h" #include "playlist_container.h" +#include "ui_playlist.h" #include "urldecode.h" #include "util.h" #include "ui_fileinfo.h" -#include "interface.h" #include "debug.h" @@ -835,11 +837,9 @@ playlist_recalc_total_time(); - current_interface->set_song_info(rate, freq, nch); + mainwin_set_song_info(rate, freq, nch); } -/* *** TO WA2GUI *** */ -#if 0 void playlist_check_pos_current(void) { @@ -864,7 +864,6 @@ PLAYLIST_UNLOCK(); playlistwin_set_toprow(row); } -#endif void playlist_next(void)
--- a/audacious/pluginenum.c Thu Sep 07 22:00:35 2006 -0700 +++ b/audacious/pluginenum.c Thu Sep 07 22:26:54 2006 -0700 @@ -36,6 +36,7 @@ #include "controlsocket.h" #include "main.h" +#include "mainwin.h" #include "playback.h" #include "playlist.h" #include "util.h"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/ui_playlist.c Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,2029 @@ +/* Audacious - Cross-platform multimedia player + * Copyright (C) 2005-2006 Audacious development team. + * + * BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "ui_playlist.h" + +#include <glib.h> +#include <glib/gi18n.h> +#include <gdk/gdk.h> +#include <gdk/gdkkeysyms.h> +#include <gtk/gtk.h> +#include <string.h> + +#include "platform/smartinclude.h" + +#include <unistd.h> +#include <errno.h> + +#include "libaudacious/util.h" + +#include "dnd.h" +#include "dock.h" +#include "equalizer.h" +#include "hints.h" +#include "input.h" +#include "main.h" +#include "mainwin.h" +#include "playback.h" +#include "playlist.h" +#include "playlist_container.h" +#include "util.h" + +#include "pixmaps.h" +#include "images/audacious_playlist.xpm" + + +#define ITEM_SEPARATOR {"/-", NULL, NULL, 0, "<Separator>", NULL} + + +enum { + ADD_URL, ADD_DIR, ADD_FILES, + SUB_MISC, SUB_ALL, SUB_CROP, SUB_SELECTED, + SUB_DUPLICATE_BYTITLE, SUB_DUPLICATE_BYFILENAME, SUB_DUPLICATE_BYPATH, + SEL_INV, SEL_ZERO, SEL_ALL, + MISC_SORT, MISC_FILEINFO, MISC_MISCOPTS, + PLIST_NEW, PLIST_SAVE_AS, PLIST_LOAD, + SEL_LOOKUP, CLOSE_PL_WINDOW, MOVE_UP, PLIST_SAVE, + MISC_QUEUE, PLIST_CQUEUE, PLIST_JTF, PLIST_JTT, + PLAYLISTWIN_REMOVE_DEAD_FILES, + PLAYLISTWIN_REFRESH, PLIST_DEFAULTSAVE, MISC_FILEPOPUP +}; + +enum { + PLAYLISTWIN_SORT_BYTITLE, PLAYLISTWIN_SORT_BYFILENAME, + PLAYLISTWIN_SORT_BYPATH, PLAYLISTWIN_SORT_BYDATE, + PLAYLISTWIN_SORT_BYARTIST, PLAYLISTWIN_SORT_SEL_BYARTIST, + PLAYLISTWIN_SORT_SEL_BYTITLE, PLAYLISTWIN_SORT_SEL_BYFILENAME, + PLAYLISTWIN_SORT_SEL_BYPATH, PLAYLISTWIN_SORT_SEL_BYDATE, + PLAYLISTWIN_SORT_RANDOMIZE, PLAYLISTWIN_SORT_REVERSE, + PLAYLISTWIN_SORT_BYTRACK, PLAYLISTWIN_SORT_SEL_BYTRACK, + PLAYLISTWIN_SORT_BYPLAYLIST, PLAYLISTWIN_SORT_SEL_BYPLAYLIST +}; + +GtkWidget *playlistwin; + +PlayList_List *playlistwin_list = NULL; +PButton *playlistwin_shade, *playlistwin_close; + +static gboolean playlistwin_resizing = FALSE; + +static GtkItemFactory *playlistwin_popup_menu; +static GtkItemFactory *pladd_menu, *pldel_menu; +static GtkItemFactory *plsel_menu, *plsort_menu; +static GtkItemFactory *pllist_menu; + +static GdkPixmap *playlistwin_bg; +static GdkBitmap *playlistwin_mask = NULL; +static GdkGC *playlistwin_gc; + +static GtkAccelGroup *playlistwin_accel; + +static gboolean playlistwin_hint_flag = FALSE; + +static PlaylistSlider *playlistwin_slider = NULL; +static TextBox *playlistwin_time_min, *playlistwin_time_sec; +static TextBox *playlistwin_info, *playlistwin_sinfo; +static SButton *playlistwin_srew, *playlistwin_splay; +static SButton *playlistwin_spause, *playlistwin_sstop; +static SButton *playlistwin_sfwd, *playlistwin_seject; +static SButton *playlistwin_sscroll_up, *playlistwin_sscroll_down; + +static GList *playlistwin_wlist = NULL; + +static void plsort_menu_callback(gpointer cb_data, guint action, + GtkWidget * w); +static void playlistwin_sub_menu_callback(gpointer cb_data, guint action, + GtkWidget * w); +static void playlistwin_popup_menu_callback(gpointer cb_data, guint action, + GtkWidget * w); + +static GtkItemFactoryEntry playlistwin_popup_menu_entries[] = { + {N_("/View Track Details"), NULL, + playlistwin_popup_menu_callback, + MISC_FILEINFO, "<ImageItem>", my_pixbuf}, + + {N_("/Show Popup Info"), NULL, + playlistwin_popup_menu_callback, + MISC_FILEPOPUP, "<ToggleItem>", NULL}, + + ITEM_SEPARATOR, + + {N_("/Remove Selected"), "Delete", + playlistwin_sub_menu_callback, + SUB_SELECTED, "<StockItem>", GTK_STOCK_REMOVE}, + + {N_("/Remove Unselected"), NULL, + playlistwin_sub_menu_callback, + SUB_CROP, "<StockItem>", GTK_STOCK_REMOVE}, + + {N_("/Remove All"), NULL, + playlistwin_sub_menu_callback, + SUB_ALL, "<StockItem>", GTK_STOCK_CLEAR}, + + ITEM_SEPARATOR, + + {N_("/Queue Toggle"), "q", + playlistwin_popup_menu_callback, + MISC_QUEUE, "<ImageItem>", queuetoggle_pixbuf}, +}; + +static GtkItemFactoryEntry pladd_menu_entries[] = { + {N_("/Add CD..."), "<shift>c", + mainwin_general_menu_callback, + MAINWIN_GENERAL_ADDCD, "<StockItem>", GTK_STOCK_CDROM}, + + {N_("/Add Internet Address..."), "<control>h", + mainwin_general_menu_callback, + MAINWIN_GENERAL_PLAYLOCATION, "<StockItem>", GTK_STOCK_NETWORK}, + + {N_("/Add Files..."), "f", + mainwin_general_menu_callback, + MAINWIN_GENERAL_PLAYFILE, "<StockItem>", GTK_STOCK_ADD}, +}; + +static GtkItemFactoryEntry pldel_menu_entries[] = { + {N_("/Clear Queue"), "<shift>Q", + playlistwin_popup_menu_callback, + PLIST_CQUEUE, "<StockItem>", GTK_STOCK_CANCEL}, + + ITEM_SEPARATOR, + + {N_("/Remove Unavailable Files"), NULL, + playlistwin_sub_menu_callback, + PLAYLISTWIN_REMOVE_DEAD_FILES, "<ImageItem>", removeunavail_pixbuf}, + + {N_("/Remove Duplicates"), NULL, NULL, 0, "<Branch>", NULL}, + {N_("/Remove Duplicates/By Title"), NULL, + playlistwin_sub_menu_callback, + SUB_DUPLICATE_BYTITLE, "<ImageItem>", removedups_pixbuf}, + {N_("/Remove Duplicates/By Filename"), NULL, + playlistwin_sub_menu_callback, + SUB_DUPLICATE_BYFILENAME, "<ImageItem>", removedups_pixbuf}, + {N_("/Remove Duplicates/By Path + Filename"), NULL, + playlistwin_sub_menu_callback, + SUB_DUPLICATE_BYPATH, "<ImageItem>", removedups_pixbuf}, + + ITEM_SEPARATOR, + + {N_("/Remove All"), NULL, + playlistwin_sub_menu_callback, + SUB_ALL, "<StockItem>", GTK_STOCK_CLEAR}, + + {N_("/Remove Unselected"), NULL, + playlistwin_sub_menu_callback, + SUB_CROP, "<StockItem>", GTK_STOCK_REMOVE}, + + {N_("/Remove Selected"), "Delete", + playlistwin_sub_menu_callback, + SUB_SELECTED, "<StockItem>", GTK_STOCK_REMOVE} +}; + +static GtkItemFactoryEntry pllist_menu_entries[] = { + {N_("/New List"), "<shift>N", + playlistwin_sub_menu_callback, + PLIST_NEW, "<StockItem>", GTK_STOCK_NEW}, + + ITEM_SEPARATOR, + + {N_("/Load List"), "o", + playlistwin_sub_menu_callback, + PLIST_LOAD, "<StockItem>", GTK_STOCK_OPEN}, + + {N_("/Save List"), "<shift>S", + playlistwin_sub_menu_callback, + PLIST_SAVE, "<StockItem>", GTK_STOCK_SAVE}, + + {N_("/Save Default List"), "<alt>S", + playlistwin_sub_menu_callback, + PLIST_DEFAULTSAVE, "<StockItem>", GTK_STOCK_SAVE}, + + ITEM_SEPARATOR, + + {N_("/Update View"), "F5", + playlistwin_sub_menu_callback, + PLAYLISTWIN_REFRESH, "<StockItem>", GTK_STOCK_REFRESH} +}; + +static GtkItemFactoryEntry plsel_menu_entries[] = { + {N_("/Invert Selection"), NULL, + playlistwin_sub_menu_callback, + SEL_INV, "<ImageItem>", selectinvert_pixbuf}, + + ITEM_SEPARATOR, + + {N_("/Select None"),"<Ctrl><Shift>A", + playlistwin_sub_menu_callback, + SEL_ZERO, "<ImageItem>", selectnone_pixbuf}, + + {N_("/Select All"), "<Ctrl>A", + playlistwin_sub_menu_callback, + SEL_ALL, "<ImageItem>", selectall_pixbuf}, +}; + +static GtkItemFactoryEntry plsort_menu_entries[] = { + {N_("/Randomize List"), NULL, plsort_menu_callback, + PLAYLISTWIN_SORT_RANDOMIZE, "<ImageItem>", randomizepl_pixbuf}, + {N_("/Reverse List"), NULL, plsort_menu_callback, + PLAYLISTWIN_SORT_REVERSE, "<ImageItem>", invertpl_pixbuf}, + ITEM_SEPARATOR, + {N_("/Sort List"), NULL, NULL, 0, "<Branch>", NULL}, + {N_("/Sort List/By Title"), NULL, plsort_menu_callback, + PLAYLISTWIN_SORT_BYTITLE, "<ImageItem>", sortbytitle_pixbuf}, + {N_("/Sort List/By Artist"), NULL, plsort_menu_callback, + PLAYLISTWIN_SORT_BYARTIST, "<ImageItem>", sortbytitle_pixbuf}, + {N_("/Sort List/By Filename"), NULL, plsort_menu_callback, + PLAYLISTWIN_SORT_BYFILENAME, "<ImageItem>", sortbyfilename_pixbuf}, + {N_("/Sort List/By Path + Filename"), NULL, plsort_menu_callback, + PLAYLISTWIN_SORT_BYPATH, "<ImageItem>", sortbypathfile_pixbuf}, + {N_("/Sort List/By Date"), NULL, plsort_menu_callback, + PLAYLISTWIN_SORT_BYDATE, "<ImageItem>", sortbydate_pixbuf}, + {N_("/Sort List/By Track Number"), NULL, plsort_menu_callback, + PLAYLISTWIN_SORT_BYTRACK, "<ImageItem>", sortbytitle_pixbuf}, + {N_("/Sort List/By Playlist Entry"), NULL, plsort_menu_callback, + PLAYLISTWIN_SORT_BYPLAYLIST, "<ImageItem>", sortbytitle_pixbuf}, + {N_("/Sort Selection"), NULL, NULL, 0, "<Branch>", NULL}, + {N_("/Sort Selection/By Title"), NULL, plsort_menu_callback, + PLAYLISTWIN_SORT_SEL_BYTITLE, "<ImageItem>", sortbytitle_pixbuf}, + {N_("/Sort Selection/By Artist"), NULL, plsort_menu_callback, + PLAYLISTWIN_SORT_SEL_BYARTIST, "<ImageItem>", sortbytitle_pixbuf}, + {N_("/Sort Selection/By Filename"), NULL, plsort_menu_callback, + PLAYLISTWIN_SORT_SEL_BYFILENAME, "<ImageItem>", sortbyfilename_pixbuf}, + {N_("/Sort Selection/By Path + Filename"), NULL, plsort_menu_callback, + PLAYLISTWIN_SORT_SEL_BYPATH, "<ImageItem>", sortbypathfile_pixbuf}, + {N_("/Sort Selection/By Date"), NULL, plsort_menu_callback, + PLAYLISTWIN_SORT_SEL_BYDATE, "<ImageItem>", sortbydate_pixbuf}, + {N_("/Sort Selection/By Track Number"), NULL, plsort_menu_callback, + PLAYLISTWIN_SORT_SEL_BYTRACK, "<ImageItem>", sortbytitle_pixbuf}, + {N_("/Sort Selection/By Playlist Entry"), NULL, plsort_menu_callback, + PLAYLISTWIN_SORT_SEL_BYPLAYLIST, "<ImageItem>", sortbytitle_pixbuf} +}; + + +static void playlistwin_draw_frame(void); + + +gboolean +playlistwin_is_shaded(void) +{ + return cfg.playlist_shaded; +} + +gint +playlistwin_get_width(void) +{ + cfg.playlist_width /= PLAYLISTWIN_WIDTH_SNAP; + cfg.playlist_width *= PLAYLISTWIN_WIDTH_SNAP; + return cfg.playlist_width; +} + +gint +playlistwin_get_height_unshaded(void) +{ + cfg.playlist_height /= PLAYLISTWIN_HEIGHT_SNAP; + cfg.playlist_height *= PLAYLISTWIN_HEIGHT_SNAP; + gint height = cfg.playlist_height; + return height; +} + +gint +playlistwin_get_height_shaded(void) +{ + return PLAYLISTWIN_SHADED_HEIGHT; +} + +gint +playlistwin_get_height(void) +{ + if (playlistwin_is_shaded()) + return playlistwin_get_height_shaded(); + else + return playlistwin_get_height_unshaded(); +} + +void +playlistwin_get_size(gint * width, gint * height) +{ + if (width) + *width = playlistwin_get_width(); + + if (height) + *height = playlistwin_get_height(); +} + +static void +playlistwin_update_info(void) +{ + gchar *text, *sel_text, *tot_text; + gulong selection, total; + gboolean selection_more, total_more; + + playlist_get_total_time(&total, &selection, &total_more, &selection_more); + + if (selection > 0 || (selection == 0 && !selection_more)) { + if (selection > 3600) + sel_text = + g_strdup_printf("%lu:%-2.2lu:%-2.2lu%s", selection / 3600, + (selection / 60) % 60, selection % 60, + (selection_more ? "+" : "")); + else + sel_text = + g_strdup_printf("%lu:%-2.2lu%s", selection / 60, + selection % 60, (selection_more ? "+" : "")); + } + else + sel_text = g_strdup("?"); + if (total > 0 || (total == 0 && !total_more)) { + if (total > 3600) + tot_text = + g_strdup_printf("%lu:%-2.2lu:%-2.2lu%s", total / 3600, + (total / 60) % 60, total % 60, + total_more ? "+" : ""); + else + tot_text = + g_strdup_printf("%lu:%-2.2lu%s", total / 60, total % 60, + total_more ? "+" : ""); + } + else + tot_text = g_strdup("?"); + text = g_strconcat(sel_text, "/", tot_text, NULL); + textbox_set_text(playlistwin_info, text); + g_free(text); + g_free(tot_text); + g_free(sel_text); +} + +static void +playlistwin_update_sinfo(void) +{ + gchar *posstr, *timestr, *title, *info, *dots; + gint pos, time; + guint max_len; + + pos = playlist_get_position(); + title = playlist_get_songtitle(pos); + time = playlist_get_songtime(pos); + + if (!title) { + textbox_set_text(playlistwin_sinfo, ""); + return; + } + + if (cfg.show_numbers_in_pl) + posstr = g_strdup_printf("%d. ", pos + 1); + else + posstr = g_strdup(""); + + max_len = (playlistwin_get_width() - 35) / 5 - strlen(posstr); + + if (time != -1) { + timestr = g_strdup_printf(" %d:%-2.2d", time / 60000, + (time / 1000) % 60); + max_len -= strlen(timestr); + } + else + timestr = g_strdup(""); + + convert_title_text(title); + + if (strlen(title) > max_len) { + max_len -= 1; + dots = "\r"; + /* textbox.c interprets \r as the ellipsis character, as there + is none in ASCII. */ + } + else + dots = ""; + + info = g_strdup_printf("%s%-*.*s%s%s", posstr, max_len, max_len, + title, dots, timestr); + g_free(posstr); + g_free(title); + g_free(timestr); + + textbox_set_text(playlistwin_sinfo, info); + g_free(info); +} + +gboolean +playlistwin_item_visible(gint index) +{ + if (index >= playlistwin_list->pl_first + && index < + (playlistwin_list->pl_first + playlistwin_list->pl_num_visible)) + return TRUE; + return FALSE; +} + +gint +playlistwin_get_toprow(void) +{ + if (playlistwin_list) + return (playlistwin_list->pl_first); + return (-1); +} + +void +playlistwin_set_toprow(gint toprow) +{ + if (playlistwin_list) + playlistwin_list->pl_first = toprow; + playlistwin_update_list(); +} + +void +playlistwin_update_list(void) +{ + g_return_if_fail(playlistwin_list != NULL); + + widget_draw(WIDGET(playlistwin_list)); + widget_draw(WIDGET(playlistwin_slider)); + playlistwin_update_info(); + playlistwin_update_sinfo(); + /* mainwin_update_jtf(); */ +} + +#if 0 +static void +playlistwin_redraw_list(void) +{ + g_return_if_fail(playlistwin_list != NULL); + + draw_widget(playlistwin_list); + draw_widget(playlistwin_slider); +} +#endif + +static void +playlistwin_set_mask(void) +{ + GdkGC *gc; + GdkColor pattern; + + if (playlistwin_mask) + g_object_unref(playlistwin_mask); + + playlistwin_mask = + gdk_pixmap_new(playlistwin->window, playlistwin_get_width(), + playlistwin_get_height(), 1); + gc = gdk_gc_new(playlistwin_mask); + pattern.pixel = 1; + gdk_gc_set_foreground(gc, &pattern); + gdk_draw_rectangle(playlistwin_mask, gc, TRUE, 0, 0, + playlistwin_get_width(), playlistwin_get_height()); + gdk_gc_destroy(gc); + + gtk_widget_shape_combine_mask(playlistwin, playlistwin_mask, 0, 0); +} + +static void +playlistwin_set_geometry_hints(gboolean shaded) +{ + GdkGeometry geometry; + GdkWindowHints mask; + + geometry.min_width = PLAYLISTWIN_MIN_WIDTH; + geometry.max_width = G_MAXUINT16; + + geometry.width_inc = PLAYLISTWIN_WIDTH_SNAP; + geometry.height_inc = PLAYLISTWIN_HEIGHT_SNAP; + + if (shaded) { + geometry.min_height = PLAYLISTWIN_SHADED_HEIGHT; + geometry.max_height = PLAYLISTWIN_SHADED_HEIGHT; + geometry.base_height = PLAYLISTWIN_SHADED_HEIGHT; + } + else { + geometry.min_height = PLAYLISTWIN_MIN_HEIGHT; + geometry.max_height = G_MAXUINT16; + } + + mask = GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE | GDK_HINT_RESIZE_INC; + + gtk_window_set_geometry_hints(GTK_WINDOW(playlistwin), + playlistwin, &geometry, mask); +} + +void +playlistwin_set_shade(gboolean shaded) +{ + cfg.playlist_shaded = shaded; + + if (shaded) { + widget_show(WIDGET(playlistwin_sinfo)); + playlistwin_shade->pb_nx = 128; + playlistwin_shade->pb_ny = 45; + playlistwin_shade->pb_px = 150; + playlistwin_shade->pb_py = 42; + playlistwin_close->pb_nx = 138; + playlistwin_close->pb_ny = 45; + } + else { + widget_hide(WIDGET(playlistwin_sinfo)); + playlistwin_shade->pb_nx = 157; + playlistwin_shade->pb_ny = 3; + playlistwin_shade->pb_px = 62; + playlistwin_shade->pb_py = 42; + playlistwin_close->pb_nx = 167; + playlistwin_close->pb_ny = 3; + } + + dock_shade(dock_window_list, GTK_WINDOW(playlistwin), + playlistwin_get_height()); + + playlistwin_set_geometry_hints(cfg.playlist_shaded); + + gtk_window_resize(GTK_WINDOW(playlistwin), + playlistwin_get_width(), + playlistwin_get_height()); + + playlistwin_set_mask(); + + widget_draw(WIDGET(playlistwin_list)); + widget_draw(WIDGET(playlistwin_slider)); + + draw_playlist_window(TRUE); +} + +static void +playlistwin_set_shade_menu(gboolean shaded) +{ + GtkWidget *item; + + item = gtk_item_factory_get_widget(mainwin_view_menu, + "/Roll up Playlist Editor"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), shaded); + + playlistwin_set_shade(shaded); +} + +void +playlistwin_shade_toggle(void) +{ + playlistwin_set_shade_menu(!cfg.playlist_shaded); +} + +static void +playlistwin_release(GtkWidget * widget, + GdkEventButton * event, + gpointer callback_data) +{ + if (event->button == 3) + return; + + gdk_pointer_ungrab(GDK_CURRENT_TIME); + playlistwin_resizing = FALSE; + gdk_flush(); + + if (dock_is_moving(GTK_WINDOW(playlistwin))) + { + dock_move_release(GTK_WINDOW(playlistwin)); + + if (cfg.playlist_transparent) + playlistwin_update_list(); + } + else + { + handle_release_cb(playlistwin_wlist, widget, event); + draw_playlist_window(FALSE); + } +} + +void +playlistwin_scroll(gint num) +{ + playlistwin_list->pl_first += num; + playlistwin_update_list(); +} + +void +playlistwin_scroll_up_pushed(void) +{ + playlistwin_list->pl_first -= 3; + playlistwin_update_list(); +} + +void +playlistwin_scroll_down_pushed(void) +{ + playlistwin_list->pl_first += 3; + playlistwin_update_list(); +} + +static void +playlistwin_select_all(void) +{ + playlist_select_all(TRUE); + playlistwin_list->pl_prev_selected = 0; + playlistwin_list->pl_prev_min = 0; + playlistwin_list->pl_prev_max = playlist_get_length() - 1; + playlistwin_update_list(); +} + +static void +playlistwin_select_none(void) +{ + playlist_select_all(FALSE); + playlistwin_list->pl_prev_selected = -1; + playlistwin_list->pl_prev_min = -1; + playlistwin_update_list(); +} + +static void +playlistwin_inverse_selection(void) +{ + playlist_select_invert_all(); + playlistwin_list->pl_prev_selected = -1; + playlistwin_list->pl_prev_min = -1; + playlistwin_update_list(); +} + +static void +playlistwin_resize(gint width, gint height) +{ + gboolean redraw; + + g_return_if_fail(width > 0 && height > 0); + + cfg.playlist_width = width; + + if (!cfg.playlist_shaded) + cfg.playlist_height = height; + else + height = cfg.playlist_height; + + /* FIXME: why the fsck are we doing this manually? */ + /* adjust widget positions and sizes */ + + widget_resize(WIDGET(playlistwin_list), width - 31, height - 58); + + widget_move(WIDGET(playlistwin_slider), width - 15, 20); + widget_resize(WIDGET(playlistwin_slider), 8, height - 58); + + widget_resize(WIDGET(playlistwin_sinfo), width - 35, 14); + playlistwin_update_sinfo(); + + widget_move(WIDGET(playlistwin_shade), width - 21, 3); + widget_move(WIDGET(playlistwin_close), width - 11, 3); + widget_move(WIDGET(playlistwin_time_min), width - 82, height - 15); + widget_move(WIDGET(playlistwin_time_sec), width - 64, height - 15); + widget_move(WIDGET(playlistwin_info), width - 143, height - 28); + widget_move(WIDGET(playlistwin_srew), width - 144, height - 16); + widget_move(WIDGET(playlistwin_splay), width - 138, height - 16); + widget_move(WIDGET(playlistwin_spause), width - 128, height - 16); + widget_move(WIDGET(playlistwin_sstop), width - 118, height - 16); + widget_move(WIDGET(playlistwin_sfwd), width - 109, height - 16); + widget_move(WIDGET(playlistwin_seject), width - 100, height - 16); + widget_move(WIDGET(playlistwin_sscroll_up), width - 14, height - 35); + widget_move(WIDGET(playlistwin_sscroll_down), width - 14, height - 30); + + g_object_unref(playlistwin_bg); + playlistwin_bg = gdk_pixmap_new(playlistwin->window, width, height, -1); + playlistwin_set_mask(); + + widget_list_lock(playlistwin_wlist); + + widget_list_change_pixmap(playlistwin_wlist, playlistwin_bg); + playlistwin_draw_frame(); + widget_list_draw(playlistwin_wlist, &redraw, TRUE); + widget_list_clear_redraw(playlistwin_wlist); + + widget_list_unlock(playlistwin_wlist); + + gdk_window_set_back_pixmap(playlistwin->window, playlistwin_bg, 0); + gdk_window_clear(playlistwin->window); +} + + + +static void +playlistwin_motion(GtkWidget * widget, + GdkEventMotion * event, + gpointer callback_data) +{ + GdkEvent *gevent; + + if (dock_is_moving(GTK_WINDOW(playlistwin))) { + dock_move_motion(GTK_WINDOW(playlistwin), event); + } + else { + handle_motion_cb(playlistwin_wlist, widget, event); + draw_playlist_window(FALSE); + } + gdk_flush(); + + while ((gevent = gdk_event_get()) != NULL) gdk_event_free(gevent); +} + +static void +playlistwin_enter(GtkWidget * widget, + GdkEventMotion * event, + gpointer callback_data) +{ + playlistwin_list->pl_tooltips = TRUE; +} + +static void +playlistwin_leave(GtkWidget * widget, + GdkEventMotion * event, + gpointer callback_data) +{ + playlistwin_list->pl_tooltips = FALSE; +} + +static void +playlistwin_show_filebrowser(void) +{ + util_run_filebrowser(NO_PLAY_BUTTON); +} + +#if 0 +static void +playlistwin_add_dir_handler(const gchar * dir) +{ + g_free(cfg.filesel_path); + cfg.filesel_path = g_strdup(dir); + playlist_add_dir(dir); +} +#endif + +static void +playlistwin_fileinfo(void) +{ + /* Show the first selected file, or the current file if nothing is + * selected */ + GList *list = playlist_get_selected(); + if (list) { + playlist_fileinfo(GPOINTER_TO_INT(list->data)); + g_list_free(list); + } + else + playlist_fileinfo_current(); +} + +static void +menu_set_item_sensitive(GtkItemFactory * item_factory, + const gchar * path, + gboolean sensitive) +{ + GtkWidget *item = gtk_item_factory_get_widget(item_factory, path); + gtk_widget_set_sensitive(item, sensitive); +} + +/* FIXME: broken */ +static void +playlistwin_set_sensitive_sortmenu(void) +{ + gboolean set = playlist_get_num_selected() > 1; + menu_set_item_sensitive(plsort_menu, "/Sort Selection/By Title", set); + menu_set_item_sensitive(plsort_menu, "/Sort Selection/By Filename", set); + menu_set_item_sensitive(plsort_menu, "/Sort Selection/By Path + Filename", set); + menu_set_item_sensitive(plsort_menu, "/Sort Selection/By Date", set); +} + +static void +show_playlist_save_error(GtkWindow * parent, + const gchar * filename) +{ + GtkWidget *dialog; + + g_return_if_fail(GTK_IS_WINDOW(parent)); + g_return_if_fail(filename != NULL); + + dialog = gtk_message_dialog_new(GTK_WINDOW(parent), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Error writing playlist \"%s\": %s"), + filename, strerror(errno)); + + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); +} + +static gboolean +show_playlist_overwrite_prompt(GtkWindow * parent, + const gchar * filename) +{ + GtkWidget *dialog; + gint result; + + g_return_val_if_fail(GTK_IS_WINDOW(parent), FALSE); + g_return_val_if_fail(filename != NULL, FALSE); + + dialog = gtk_message_dialog_new(GTK_WINDOW(parent), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_YES_NO, + _("%s already exist. Continue?"), + filename); + + result = gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); + + return (result == GTK_RESPONSE_YES); +} + +static void +show_playlist_save_format_error(GtkWindow * parent, + const gchar * filename) +{ + const gchar *markup = + N_("<b><big>Unable to save playlist.</big></b>\n\n" + "Unknown file type for '%s'.\n"); + + GtkWidget *dialog; + + g_return_if_fail(GTK_IS_WINDOW(parent)); + g_return_if_fail(filename != NULL); + + dialog = + gtk_message_dialog_new_with_markup(GTK_WINDOW(parent), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _(markup), + filename); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); +} + +static void +playlistwin_save_playlist(const gchar * filename) +{ + PlaylistContainer *plc; + gchar *ext = strrchr(filename, '.') + 1; + + plc = playlist_container_find(ext); + if (plc == NULL) { + show_playlist_save_format_error(GTK_WINDOW(playlistwin), filename); + return; + } + + str_replace_in(&cfg.playlist_path, g_path_get_dirname(filename)); + + if (g_file_test(filename, G_FILE_TEST_IS_REGULAR)) + if (!show_playlist_overwrite_prompt(GTK_WINDOW(playlistwin), filename)) + return; + + if (!playlist_save(filename)) + show_playlist_save_error(GTK_WINDOW(playlistwin), filename); +} + +#if 0 +static void +playlistwin_save_current(void) +{ + const gchar *filename; + + if (!(filename = playlist_get_current_name())) + return; + + playlistwin_save_playlist(filename); +} +#endif + +static void +playlistwin_load_playlist(const gchar * filename) +{ + g_return_if_fail(filename != NULL); + + str_replace_in(&cfg.playlist_path, g_strdup(filename)); + + playlist_clear(); + mainwin_clear_song_info(); + mainwin_set_info_text(); + + playlist_load(filename); + playlist_set_current_name(filename); +} + +static gchar * +playlist_file_selection(const gchar * title, + gboolean save, + const gchar * default_filename) +{ + GtkWidget *dialog, *button; + gchar *filename; + + g_return_val_if_fail(title != NULL, NULL); + + dialog = gtk_file_chooser_dialog_new(title, GTK_WINDOW(mainwin), + save ? GTK_FILE_CHOOSER_ACTION_SAVE : + GTK_FILE_CHOOSER_ACTION_OPEN, NULL, NULL); + if (default_filename) + gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), + default_filename); + + button = gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, + GTK_RESPONSE_REJECT); + gtk_button_set_use_stock(GTK_BUTTON(button), TRUE); + GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT); + + button = gtk_dialog_add_button(GTK_DIALOG(dialog), + save ? GTK_STOCK_SAVE : GTK_STOCK_OPEN, + GTK_RESPONSE_ACCEPT); + gtk_button_set_use_stock(GTK_BUTTON(button), TRUE); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + else + filename = NULL; + + gtk_widget_destroy(dialog); + + return filename; +} + +void +playlistwin_select_playlist_to_load(const gchar * default_filename) +{ + gchar *filename = + playlist_file_selection(_("Load Playlist"), FALSE, default_filename); + + if (filename) { + playlistwin_load_playlist(filename); + g_free(filename); + } +} + +static void +playlistwin_select_playlist_to_save(const gchar * default_filename) +{ + gchar *filename = + playlist_file_selection(_("Save Playlist"), TRUE, default_filename); + + if (filename) { + /* Default to M3U if no filename has extension */ + + /* NOTE: This doesn't work correctly for hidden files + - descender */ + if (!strchr(filename, '.')) { + gchar *tmpstr = filename; + filename = g_strconcat(filename, ".m3u", NULL); + g_free(tmpstr); + } + + playlistwin_save_playlist(filename); + g_free(filename); + } +} + +static gboolean +inside_sensitive_widgets(gint x, gint y) +{ + return (widget_contains(WIDGET(playlistwin_list), x, y) || + widget_contains(WIDGET(playlistwin_slider), x, y) || + widget_contains(WIDGET(playlistwin_close), x, y) || + widget_contains(WIDGET(playlistwin_shade), x, y) || + widget_contains(WIDGET(playlistwin_time_min), x, y) || + widget_contains(WIDGET(playlistwin_time_sec), x, y) || + widget_contains(WIDGET(playlistwin_info), x, y) || + widget_contains(WIDGET(playlistwin_srew), x, y) || + widget_contains(WIDGET(playlistwin_splay), x, y) || + widget_contains(WIDGET(playlistwin_spause), x, y) || + widget_contains(WIDGET(playlistwin_sstop), x, y) || + widget_contains(WIDGET(playlistwin_sfwd), x, y) || + widget_contains(WIDGET(playlistwin_seject), x, y) || + widget_contains(WIDGET(playlistwin_sscroll_up), x, y) || + widget_contains(WIDGET(playlistwin_sscroll_down), x, y)); +} + +#define REGION_L(x1,x2,y1,y2) \ + (event->x >= (x1) && event->x < (x2) && \ + event->y >= cfg.playlist_height - (y1) && \ + event->y < cfg.playlist_height - (y2)) + +#define REGION_R(x1,x2,y1,y2) \ + (event->x >= playlistwin_get_width() - (x1) && \ + event->x < playlistwin_get_width() - (x2) && \ + event->y >= cfg.playlist_height - (y1) && \ + event->y < cfg.playlist_height - (y2)) + +static void +playlistwin_scrolled(GtkWidget * widget, + GdkEventScroll * event, + gpointer callback_data) +{ + + if (event->direction == GDK_SCROLL_DOWN) + playlistwin_scroll(cfg.scroll_pl_by); + + if (event->direction == GDK_SCROLL_UP) + playlistwin_scroll(-cfg.scroll_pl_by); + +} + + + + +static gboolean +playlistwin_press(GtkWidget * widget, + GdkEventButton * event, + gpointer callback_data) +{ + gboolean grab = TRUE; + gint xpos, ypos; + GtkWidget *_menu; + GtkRequisition req; + + gtk_window_get_position(GTK_WINDOW(playlistwin), &xpos, &ypos); + + if (event->button == 1 && !cfg.show_wm_decorations && + ((!cfg.playlist_shaded && + event->x > playlistwin_get_width() - 20 && + event->y > cfg.playlist_height - 20) || + (cfg.playlist_shaded && + event->x >= playlistwin_get_width() - 31 && + event->x < playlistwin_get_width() - 22))) { + + /* NOTE: Workaround for bug #214 */ + if (event->type != GDK_2BUTTON_PRESS && + event->type != GDK_3BUTTON_PRESS) { + /* resize area */ + playlistwin_resizing = TRUE; + gtk_window_begin_resize_drag(GTK_WINDOW(widget), + GDK_WINDOW_EDGE_SOUTH_EAST, + event->button, + event->x + xpos, event->y + ypos, + event->time); + } + grab = FALSE; + } + else if (event->button == 1 && REGION_L(12, 37, 29, 11)) { + /* ADD button menu */ + + _menu = GTK_WIDGET(pladd_menu->widget); + if (!GTK_WIDGET_REALIZED(_menu)) gtk_widget_realize(_menu); + gtk_widget_size_request(_menu, &req); + gtk_item_factory_popup_with_data(pladd_menu, + NULL, NULL, + xpos+12, + (ypos + playlistwin_get_height()) - 8 - req.height, 1, event->time); + grab = FALSE; + } + else if (event->button == 1 && REGION_L(41, 66, 29, 11)) { + /* SUB button menu */ + _menu = GTK_WIDGET(pldel_menu->widget); + if (!GTK_WIDGET_REALIZED(_menu)) gtk_widget_realize(_menu); + gtk_widget_size_request(_menu, &req); + gtk_item_factory_popup_with_data(pldel_menu, + NULL, NULL, + xpos+40, + (ypos + playlistwin_get_height()) - 8 - req.height, 1, event->time); + grab = FALSE; + } + else if (event->button == 1 && REGION_L(70, 95, 29, 11)) { + /* SEL button menu */ + _menu = GTK_WIDGET(plsel_menu->widget); + if (!GTK_WIDGET_REALIZED(_menu)) gtk_widget_realize(_menu); + gtk_widget_size_request(_menu, &req); + gtk_item_factory_popup_with_data(plsel_menu, + NULL, NULL, + xpos+68, + (ypos + playlistwin_get_height()) - 8 - req.height, 1, event->time); + + grab = FALSE; + } + else if (event->button == 1 && REGION_L(99, 124, 29, 11)) { + /* MISC button menu */ + _menu = GTK_WIDGET(plsort_menu->widget); + if (!GTK_WIDGET_REALIZED(_menu)) gtk_widget_realize(_menu); + gtk_widget_size_request(_menu, &req); + gtk_item_factory_popup_with_data(plsort_menu, + NULL, NULL, + xpos+100, + (ypos + playlistwin_get_height()) - 8 - req.height, 1, event->time); + grab = FALSE; + } + else if (event->button == 1 && REGION_R(46, 23, 29, 11)) { + /* LIST button menu */ + _menu = GTK_WIDGET(pllist_menu->widget); + if (!GTK_WIDGET_REALIZED(_menu)) gtk_widget_realize(_menu); + gtk_widget_size_request(_menu, &req); + gtk_item_factory_popup_with_data(pllist_menu, + NULL, NULL, + xpos + playlistwin_get_width() - req.width - 12, + (ypos + playlistwin_get_height()) - 8 - req.height, 1, event->time); + grab = FALSE; + } + else if (event->button == 1 && REGION_R(82, 54, 15, 9)) { + if (cfg.timer_mode == TIMER_ELAPSED) + cfg.timer_mode = TIMER_REMAINING; + else + cfg.timer_mode = TIMER_ELAPSED; + } + else if (event->button == 2 && (event->type == GDK_BUTTON_PRESS) && + widget_contains(WIDGET(playlistwin_list), event->x, event->y)) { + gtk_selection_convert(widget, GDK_SELECTION_PRIMARY, + GDK_TARGET_STRING, event->time); + } + else if (event->button == 1 && event->type == GDK_BUTTON_PRESS && + !inside_sensitive_widgets(event->x, event->y) && event->y < 14) + { + dock_move_press(dock_window_list, GTK_WINDOW(playlistwin), event, + FALSE); + gtk_window_present(GTK_WINDOW(playlistwin)); + } + else if (event->button == 1 && event->type == GDK_2BUTTON_PRESS && + !inside_sensitive_widgets(event->x, event->y) + && event->y < 14) { + /* double click on title bar */ + playlistwin_shade_toggle(); + if (dock_is_moving(GTK_WINDOW(playlistwin))) + dock_move_release(GTK_WINDOW(playlistwin)); + return TRUE; + } + else if (event->button == 3 && + !(widget_contains(WIDGET(playlistwin_list), event->x, event->y) || + (event->y >= cfg.playlist_height - 29 && + event->y < cfg.playlist_height - 11 && + ((event->x >= 12 && event->x < 37) || + (event->x >= 41 && event->x < 66) || + (event->x >= 70 && event->x < 95) || + (event->x >= 99 && event->x < 124) || + (event->x >= playlistwin_get_width() - 46 && + event->x < playlistwin_get_width() - 23))))) { + /* + * Pop up the main menu a few pixels down to avoid + * anything to be selected initially. + */ + util_item_factory_popup(mainwin_general_menu, event->x_root, + event->y_root + 2, 3, event->time); + grab = FALSE; + } + else if (event->button == 3 && + widget_contains(WIDGET(playlistwin_list), event->x, event->y)) { + /* popup menu */ + playlistwin_set_sensitive_sortmenu(); + { + GtkWidget *item = gtk_item_factory_get_widget(playlistwin_popup_menu, "/Show Popup Info"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), cfg.show_filepopup_for_tuple); + } + gtk_item_factory_popup(playlistwin_popup_menu, + event->x_root, event->y_root + 5, + 3, event->time); + grab = FALSE; + } + else { + handle_press_cb(playlistwin_wlist, widget, event); + draw_playlist_window(FALSE); + } + + if (grab) + gdk_pointer_grab(playlistwin->window, FALSE, + GDK_BUTTON_MOTION_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_BUTTON1_MOTION_MASK, NULL, NULL, + GDK_CURRENT_TIME); + + return FALSE; +} + +static gboolean +playlistwin_focus_in(GtkWidget * widget, GdkEvent * event, gpointer data) +{ + playlistwin_close->pb_allow_draw = TRUE; + playlistwin_shade->pb_allow_draw = TRUE; + draw_playlist_window(TRUE); + return FALSE; +} + +static gboolean +playlistwin_focus_out(GtkWidget * widget, + GdkEventButton * event, gpointer data) +{ + playlistwin_close->pb_allow_draw = FALSE; + playlistwin_shade->pb_allow_draw = FALSE; + draw_playlist_window(TRUE); + return FALSE; +} + +static gboolean +playlistwin_configure(GtkWidget * window, + GdkEventConfigure * event, gpointer data) +{ + if (!GTK_WIDGET_VISIBLE(window)) + return FALSE; + + cfg.playlist_x = event->x; + cfg.playlist_y = event->y; + + if (playlistwin_resizing) { + if (event->width != playlistwin_get_width() || + event->height != playlistwin_get_height()) + playlistwin_resize(event->width, event->height); + } + return TRUE; +} + +void +playlistwin_set_back_pixmap(void) +{ + gdk_window_set_back_pixmap(playlistwin->window, playlistwin_bg, 0); + gdk_window_clear(playlistwin->window); +} + +static gboolean +playlistwin_delete(GtkWidget * w, gpointer data) +{ + playlistwin_hide(); + return TRUE; +} + +static void +playlistwin_keypress_up_down_handler(PlayList_List * pl, + gboolean up, guint state) +{ + if ((state & GDK_MOD1_MASK) && (state & GDK_SHIFT_MASK)) + return; + if (!(state & GDK_MOD1_MASK)) + playlist_select_all(FALSE); + + if (pl->pl_prev_selected == -1 || + (!playlistwin_item_visible(pl->pl_prev_selected) && + !(state & GDK_SHIFT_MASK && pl->pl_prev_min != -1))) { + pl->pl_prev_selected = pl->pl_first; + } + else if (state & GDK_SHIFT_MASK) { + if (pl->pl_prev_min == -1) { + pl->pl_prev_max = pl->pl_prev_selected; + pl->pl_prev_min = pl->pl_prev_selected; + } + pl->pl_prev_max += (up ? -1 : 1); + pl->pl_prev_max = + CLAMP(pl->pl_prev_max, 0, playlist_get_length() - 1); + + pl->pl_first = MIN(pl->pl_first, pl->pl_prev_max); + pl->pl_first = MAX(pl->pl_first, pl->pl_prev_max - + pl->pl_num_visible + 1); + playlist_select_range(pl->pl_prev_min, pl->pl_prev_max, TRUE); + return; + } + else if (state & GDK_MOD1_MASK) { + if (up) + playlist_list_move_up(pl); + else + playlist_list_move_down(pl); + if (pl->pl_prev_min < pl->pl_first) + pl->pl_first = pl->pl_prev_min; + else if (pl->pl_prev_max >= (pl->pl_first + pl->pl_num_visible)) + pl->pl_first = pl->pl_prev_max - pl->pl_num_visible + 1; + return; + } + else if (up) + pl->pl_prev_selected--; + else + pl->pl_prev_selected++; + + pl->pl_prev_selected = + CLAMP(pl->pl_prev_selected, 0, playlist_get_length() - 1); + + if (pl->pl_prev_selected < pl->pl_first) + pl->pl_first--; + else if (pl->pl_prev_selected >= (pl->pl_first + pl->pl_num_visible)) + pl->pl_first++; + + playlist_select_range(pl->pl_prev_selected, pl->pl_prev_selected, TRUE); + pl->pl_prev_min = -1; + +} + +/* FIXME: Handle the keys through menu */ + +static gboolean +playlistwin_keypress(GtkWidget * w, GdkEventKey * event, gpointer data) +{ + guint keyval; + gboolean refresh = FALSE; + + if (cfg.playlist_shaded) + return FALSE; + + switch (keyval = event->keyval) { + case GDK_KP_Up: + case GDK_KP_Down: + case GDK_Up: + case GDK_Down: + playlistwin_keypress_up_down_handler(playlistwin_list, + keyval == GDK_Up + || keyval == GDK_KP_Up, + event->state); + refresh = TRUE; + break; + case GDK_Page_Up: + playlistwin_scroll(-playlistwin_list->pl_num_visible); + refresh = TRUE; + break; + case GDK_Page_Down: + playlistwin_scroll(playlistwin_list->pl_num_visible); + refresh = TRUE; + break; + case GDK_Home: + playlistwin_list->pl_first = 0; + refresh = TRUE; + break; + case GDK_End: + playlistwin_list->pl_first = + playlist_get_length() - playlistwin_list->pl_num_visible; + refresh = TRUE; + break; + case GDK_Return: + if (playlistwin_list->pl_prev_selected > -1 + && playlistwin_item_visible(playlistwin_list->pl_prev_selected)) { + playlist_set_position(playlistwin_list->pl_prev_selected); + if (!bmp_playback_get_playing()) + bmp_playback_initiate(); + } + break; + case GDK_3: + if (event->state & GDK_CONTROL_MASK) + playlistwin_fileinfo(); + break; + case GDK_Delete: + if (event->state & GDK_CONTROL_MASK) + playlist_delete(TRUE); + else + playlist_delete(FALSE); + break; + case GDK_Insert: + if (event->state & GDK_MOD1_MASK) + mainwin_show_add_url_window(); + else + playlistwin_show_filebrowser(); + break; + case GDK_Left: + case GDK_KP_Left: + case GDK_KP_7: + if (playlist_get_current_length() != -1) + bmp_playback_seek(CLAMP + (bmp_playback_get_time() - 1000, 0, + playlist_get_current_length()) / 1000); + break; + case GDK_Right: + case GDK_KP_Right: + case GDK_KP_9: + if (playlist_get_current_length() != -1) + bmp_playback_seek(CLAMP + (bmp_playback_get_time() + 1000, 0, + playlist_get_current_length()) / 1000); + break; + + case GDK_Escape: + mainwin_minimize_cb(); + break; + default: + return FALSE; + } + + if (refresh) + playlistwin_update_list(); + + return TRUE; +} + +static void +playlistwin_draw_frame(void) +{ + gboolean focus = + gtk_window_has_toplevel_focus(GTK_WINDOW(playlistwin)) || + !cfg.dim_titlebar; + + if (cfg.playlist_shaded) { + skin_draw_playlistwin_shaded(bmp_active_skin, + playlistwin_bg, playlistwin_gc, + playlistwin_get_width(), focus); + } + else { + skin_draw_playlistwin_frame(bmp_active_skin, + playlistwin_bg, playlistwin_gc, + playlistwin_get_width(), + cfg.playlist_height, focus); + } +} + +void +draw_playlist_window(gboolean force) +{ + gboolean redraw; + GList *wl; + Widget *w; + + if (force) + playlistwin_draw_frame(); + + widget_list_lock(playlistwin_wlist); + widget_list_draw(playlistwin_wlist, &redraw, force); + + if (redraw || force) { + if (force) { + gdk_window_clear(playlistwin->window); + } + else { + for (wl = playlistwin_wlist; wl; wl = g_list_next(wl)) { + w = WIDGET(wl->data); + if (w->redraw && w->visible) { + gdk_window_clear_area(playlistwin->window, w->x, w->y, + w->width, w->height); + w->redraw = FALSE; + } + } + } + + gdk_flush(); + } + + widget_list_unlock(playlistwin_wlist); +} + + +void +playlistwin_hide_timer(void) +{ + textbox_set_text(playlistwin_time_min, " "); + textbox_set_text(playlistwin_time_sec, " "); +} + +void +playlistwin_set_time(gint time, gint length, TimerMode mode) +{ + gchar *text, sign; + + if (mode == TIMER_REMAINING && length != -1) { + time = length - time; + sign = '-'; + } + else + sign = ' '; + + time /= 1000; + + if (time < 0) + time = 0; + if (time > 99 * 60) + time /= 60; + + text = g_strdup_printf("%c%-2.2d", sign, time / 60); + textbox_set_text(playlistwin_time_min, text); + g_free(text); + + text = g_strdup_printf("%-2.2d", time % 60); + textbox_set_text(playlistwin_time_sec, text); + g_free(text); +} + +static void +playlistwin_drag_motion(GtkWidget * widget, + GdkDragContext * context, + gint x, gint y, + GtkSelectionData * selection_data, + guint info, guint time, gpointer user_data) +{ + playlistwin_list->pl_drag_motion = TRUE; + playlistwin_list->drag_motion_x = x; + playlistwin_list->drag_motion_y = y; + playlistwin_update_list(); + playlistwin_hint_flag = TRUE; +} + +static void +playlistwin_drag_end(GtkWidget * widget, + GdkDragContext * context, gpointer user_data) +{ + playlistwin_list->pl_drag_motion = FALSE; + playlistwin_hint_flag = FALSE; + playlistwin_update_list(); +} + +static void +playlistwin_drag_data_received(GtkWidget * widget, + GdkDragContext * context, + gint x, gint y, + GtkSelectionData * + selection_data, guint info, + guint time, gpointer user_data) +{ + gint pos; + + g_return_if_fail(selection_data != NULL); + + if (!selection_data->data) { + g_message("Received no DND data!"); + return; + } + + if (widget_contains(WIDGET(playlistwin_list), x, y)) { + pos = (y - WIDGET(playlistwin_list)->y) / + playlistwin_list->pl_fheight + playlistwin_list->pl_first; + + pos = MIN(pos, playlist_get_length()); + playlist_ins_url((gchar *) selection_data->data, pos); + } + else + playlist_add_url((gchar *) selection_data->data); +} + +static void +playlistwin_create_widgets(void) +{ + /* This function creates the custom widgets used by the playlist editor */ + + /* text box for displaying song title in shaded mode */ + playlistwin_sinfo = + create_textbox(&playlistwin_wlist, playlistwin_bg, playlistwin_gc, + 4, 4, playlistwin_get_width() - 35, FALSE, SKIN_TEXT); + + if (!cfg.playlist_shaded) + widget_hide(WIDGET(playlistwin_sinfo)); + + /* shade/unshade window push button */ + if (cfg.playlist_shaded) + playlistwin_shade = + create_pbutton(&playlistwin_wlist, playlistwin_bg, + playlistwin_gc, playlistwin_get_width() - 21, 3, + 9, 9, 128, 45, 150, 42, + playlistwin_shade_toggle, SKIN_PLEDIT); + else + playlistwin_shade = + create_pbutton(&playlistwin_wlist, playlistwin_bg, + playlistwin_gc, playlistwin_get_width() - 21, 3, + 9, 9, 157, 3, 62, 42, playlistwin_shade_toggle, + SKIN_PLEDIT); + + playlistwin_shade->pb_allow_draw = FALSE; + + /* close window push button */ + playlistwin_close = + create_pbutton(&playlistwin_wlist, playlistwin_bg, playlistwin_gc, + playlistwin_get_width() - 11, 3, 9, 9, + cfg.playlist_shaded ? 138 : 167, + cfg.playlist_shaded ? 45 : 3, 52, 42, + playlistwin_hide, SKIN_PLEDIT); + playlistwin_close->pb_allow_draw = FALSE; + + /* playlist list box */ + playlistwin_list = + create_playlist_list(&playlistwin_wlist, playlistwin_bg, + playlistwin_gc, 12, 20, + playlistwin_get_width() - 31, + cfg.playlist_height - 58); + playlist_list_set_font(cfg.playlist_font); + + /* playlist list box slider */ + playlistwin_slider = + create_playlistslider(&playlistwin_wlist, playlistwin_bg, + playlistwin_gc, playlistwin_get_width() - 15, + 20, cfg.playlist_height - 58, playlistwin_list); + /* track time (minute) */ + playlistwin_time_min = + create_textbox(&playlistwin_wlist, playlistwin_bg, playlistwin_gc, + playlistwin_get_width() - 82, + cfg.playlist_height - 15, 15, FALSE, SKIN_TEXT); + + /* track time (second) */ + playlistwin_time_sec = + create_textbox(&playlistwin_wlist, playlistwin_bg, playlistwin_gc, + playlistwin_get_width() - 64, + cfg.playlist_height - 15, 10, FALSE, SKIN_TEXT); + + /* playlist information (current track length / total track length) */ + playlistwin_info = + create_textbox(&playlistwin_wlist, playlistwin_bg, playlistwin_gc, + playlistwin_get_width() - 143, + cfg.playlist_height - 28, 90, FALSE, SKIN_TEXT); + + /* mini play control buttons at right bottom corner */ + + /* rewind button */ + playlistwin_srew = + create_sbutton(&playlistwin_wlist, playlistwin_bg, playlistwin_gc, + playlistwin_get_width() - 144, + cfg.playlist_height - 16, 8, 7, playlist_prev); + + /* play button */ + playlistwin_splay = + create_sbutton(&playlistwin_wlist, playlistwin_bg, playlistwin_gc, + playlistwin_get_width() - 138, + cfg.playlist_height - 16, 10, 7, mainwin_play_pushed); + + /* pause button */ + playlistwin_spause = + create_sbutton(&playlistwin_wlist, playlistwin_bg, playlistwin_gc, + playlistwin_get_width() - 128, + cfg.playlist_height - 16, 10, 7, bmp_playback_pause); + + /* stop button */ + playlistwin_sstop = + create_sbutton(&playlistwin_wlist, playlistwin_bg, playlistwin_gc, + playlistwin_get_width() - 118, + cfg.playlist_height - 16, 9, 7, mainwin_stop_pushed); + + /* forward button */ + playlistwin_sfwd = + create_sbutton(&playlistwin_wlist, playlistwin_bg, playlistwin_gc, + playlistwin_get_width() - 109, + cfg.playlist_height - 16, 8, 7, playlist_next); + + /* eject button */ + playlistwin_seject = + create_sbutton(&playlistwin_wlist, playlistwin_bg, playlistwin_gc, + playlistwin_get_width() - 100, + cfg.playlist_height - 16, 9, 7, mainwin_eject_pushed); + + + playlistwin_sscroll_up = + create_sbutton(&playlistwin_wlist, playlistwin_bg, playlistwin_gc, + playlistwin_get_width() - 14, + cfg.playlist_height - 35, 8, 5, + playlistwin_scroll_up_pushed); + playlistwin_sscroll_down = + create_sbutton(&playlistwin_wlist, playlistwin_bg, playlistwin_gc, + playlistwin_get_width() - 14, + cfg.playlist_height - 30, 8, 5, + playlistwin_scroll_down_pushed); + +} + +static void +selection_received(GtkWidget * widget, + GtkSelectionData * selection_data, gpointer data) +{ + if (selection_data->type == GDK_SELECTION_TYPE_STRING && + selection_data->length > 0) + playlist_add_url((gchar *) selection_data->data); +} + +static void +playlistwin_create_window(void) +{ + GdkPixbuf *icon; + + playlistwin = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(playlistwin), _("Audacious Playlist Editor")); + gtk_window_set_wmclass(GTK_WINDOW(playlistwin), "playlist", "Audacious"); + gtk_window_set_role(GTK_WINDOW(playlistwin), "playlist"); + gtk_window_set_default_size(GTK_WINDOW(playlistwin), + playlistwin_get_width(), + playlistwin_get_height()); + gtk_window_set_resizable(GTK_WINDOW(playlistwin), TRUE); + playlistwin_set_geometry_hints(cfg.playlist_shaded); + dock_window_list = dock_window_set_decorated(dock_window_list, + GTK_WINDOW(playlistwin), + cfg.show_wm_decorations); + + gtk_window_set_transient_for(GTK_WINDOW(playlistwin), + GTK_WINDOW(mainwin)); + gtk_window_set_skip_taskbar_hint(GTK_WINDOW(playlistwin), TRUE); + + icon = gdk_pixbuf_new_from_xpm_data((const gchar **) bmp_playlist_icon); + gtk_window_set_icon(GTK_WINDOW(playlistwin), icon); + g_object_unref(icon); + + gtk_widget_set_app_paintable(playlistwin, TRUE); + + if (cfg.playlist_x != -1 && cfg.save_window_position) + gtk_window_move(GTK_WINDOW(playlistwin), + cfg.playlist_x, cfg.playlist_y); + + gtk_widget_add_events(playlistwin, GDK_POINTER_MOTION_MASK | + GDK_FOCUS_CHANGE_MASK | GDK_BUTTON_MOTION_MASK | + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | + GDK_SCROLL_MASK | GDK_VISIBILITY_NOTIFY_MASK); + gtk_widget_realize(playlistwin); + + util_set_cursor(playlistwin); + + g_signal_connect(playlistwin, "delete_event", + G_CALLBACK(playlistwin_delete), NULL); + g_signal_connect(playlistwin, "button_press_event", + G_CALLBACK(playlistwin_press), NULL); + g_signal_connect(playlistwin, "button_release_event", + G_CALLBACK(playlistwin_release), NULL); + g_signal_connect(playlistwin, "scroll_event", + G_CALLBACK(playlistwin_scrolled), NULL); + g_signal_connect(playlistwin, "motion_notify_event", + G_CALLBACK(playlistwin_motion), NULL); + g_signal_connect(playlistwin, "enter_notify_event", + G_CALLBACK(playlistwin_enter), NULL); + g_signal_connect(playlistwin, "leave_notify_event", + G_CALLBACK(playlistwin_leave), NULL); + g_signal_connect_after(playlistwin, "focus_in_event", + G_CALLBACK(playlistwin_focus_in), NULL); + g_signal_connect_after(playlistwin, "focus_out_event", + G_CALLBACK(playlistwin_focus_out), NULL); + g_signal_connect(playlistwin, "configure_event", + G_CALLBACK(playlistwin_configure), NULL); + g_signal_connect(playlistwin, "style_set", + G_CALLBACK(playlistwin_set_back_pixmap), NULL); + + bmp_drag_dest_set(playlistwin); + + /* DnD stuff */ + g_signal_connect(playlistwin, "drag-leave", + G_CALLBACK(playlistwin_drag_end), NULL); + g_signal_connect(playlistwin, "drag-data-delete", + G_CALLBACK(playlistwin_drag_end), NULL); + g_signal_connect(playlistwin, "drag-end", + G_CALLBACK(playlistwin_drag_end), NULL); + g_signal_connect(playlistwin, "drag-drop", + G_CALLBACK(playlistwin_drag_end), NULL); + g_signal_connect(playlistwin, "drag-data-received", + G_CALLBACK(playlistwin_drag_data_received), NULL); + g_signal_connect(playlistwin, "drag-motion", + G_CALLBACK(playlistwin_drag_motion), NULL); + + g_signal_connect(playlistwin, "key_press_event", + G_CALLBACK(playlistwin_keypress), NULL); + g_signal_connect(playlistwin, "selection_received", + G_CALLBACK(selection_received), NULL); + + playlistwin_set_mask(); +} + +void +playlistwin_create_popup_menus(void) +{ + playlistwin_accel = gtk_accel_group_new(); + + /* playlist window popup menu */ + playlistwin_popup_menu = create_menu(playlistwin_popup_menu_entries, + G_N_ELEMENTS(playlistwin_popup_menu_entries), + playlistwin_accel); + + pladd_menu = create_menu(pladd_menu_entries, G_N_ELEMENTS(pladd_menu_entries), + playlistwin_accel); + pldel_menu = create_menu(pldel_menu_entries, G_N_ELEMENTS(pldel_menu_entries), + playlistwin_accel); + plsel_menu = create_menu(plsel_menu_entries, G_N_ELEMENTS(plsel_menu_entries), + playlistwin_accel); + plsort_menu = create_menu(plsort_menu_entries, + G_N_ELEMENTS(plsort_menu_entries), + playlistwin_accel); + pllist_menu = create_menu(pllist_menu_entries, G_N_ELEMENTS(pllist_menu_entries), + playlistwin_accel); + +#if 0 + make_submenu(playlistwin_popup_menu, "/Playlist", + playlistwin_playlist_menu); + make_submenu(playlistwin_popup_menu, "/Playback", + playlistwin_playback_menu); + make_submenu(playlistwin_popup_menu, "/Add", + pladd_menu); +#endif +} + +void +playlistwin_create(void) +{ + playlistwin_create_window(); + playlistwin_create_popup_menus(); + + /* create GC and back pixmap for custom widget to draw on */ + playlistwin_gc = gdk_gc_new(playlistwin->window); + playlistwin_bg = gdk_pixmap_new(playlistwin->window, + playlistwin_get_width(), + playlistwin_get_height_unshaded(), -1); + gdk_window_set_back_pixmap(playlistwin->window, playlistwin_bg, 0); + + playlistwin_create_widgets(); + playlistwin_update_info(); + + gtk_window_add_accel_group(GTK_WINDOW(playlistwin), playlistwin_accel); + gtk_window_add_accel_group(GTK_WINDOW(playlistwin), mainwin_accel); +} + + +void +playlistwin_show(void) +{ + GtkWidget *item; + + item = gtk_item_factory_get_widget(mainwin_view_menu, + "/Show Playlist Editor"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), TRUE); + + tbutton_set_toggled(mainwin_pl, TRUE); + cfg.playlist_visible = TRUE; + + playlistwin_set_toprow(0); + playlist_check_pos_current(); + + gtk_widget_show(playlistwin); +} + +void +playlistwin_hide(void) +{ + GtkWidget *item; + + item = gtk_item_factory_get_widget(mainwin_view_menu, + "/Show Playlist Editor"); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(item), FALSE); + + gtk_widget_hide(playlistwin); + tbutton_set_toggled(mainwin_pl, FALSE); + cfg.playlist_visible = FALSE; + + gtk_window_present(GTK_WINDOW(mainwin)); + gtk_widget_grab_focus(mainwin); +} + + +static void +plsort_menu_callback(gpointer data, + guint action, + GtkWidget * widget) +{ + switch (action) { + case PLAYLISTWIN_SORT_BYPLAYLIST: + playlist_sort(PLAYLIST_SORT_PLAYLIST); + playlistwin_update_list(); + break; + case PLAYLISTWIN_SORT_BYTRACK: + playlist_sort(PLAYLIST_SORT_TRACK); + playlistwin_update_list(); + break; + case PLAYLISTWIN_SORT_BYTITLE: + playlist_sort(PLAYLIST_SORT_TITLE); + playlistwin_update_list(); + break; + case PLAYLISTWIN_SORT_BYARTIST: + playlist_sort(PLAYLIST_SORT_ARTIST); + playlistwin_update_list(); + break; + case PLAYLISTWIN_SORT_BYPATH: + playlist_sort(PLAYLIST_SORT_PATH); + playlistwin_update_list(); + break; + case PLAYLISTWIN_SORT_BYDATE: + playlist_sort(PLAYLIST_SORT_DATE); + playlistwin_update_list(); + break; + case PLAYLISTWIN_SORT_BYFILENAME: + playlist_sort(PLAYLIST_SORT_FILENAME); + playlistwin_update_list(); + break; + case PLAYLISTWIN_SORT_SEL_BYPLAYLIST: + playlist_sort_selected(PLAYLIST_SORT_PLAYLIST); + playlistwin_update_list(); + break; + case PLAYLISTWIN_SORT_SEL_BYTRACK: + playlist_sort_selected(PLAYLIST_SORT_TRACK); + playlistwin_update_list(); + break; + case PLAYLISTWIN_SORT_SEL_BYTITLE: + playlist_sort_selected(PLAYLIST_SORT_TITLE); + playlistwin_update_list(); + break; + case PLAYLISTWIN_SORT_SEL_BYARTIST: + playlist_sort_selected(PLAYLIST_SORT_ARTIST); + playlistwin_update_list(); + break; + case PLAYLISTWIN_SORT_SEL_BYFILENAME: + playlist_sort_selected(PLAYLIST_SORT_FILENAME); + playlistwin_update_list(); + break; + case PLAYLISTWIN_SORT_SEL_BYPATH: + playlist_sort_selected(PLAYLIST_SORT_PATH); + playlistwin_update_list(); + break; + case PLAYLISTWIN_SORT_SEL_BYDATE: + playlist_sort_selected(PLAYLIST_SORT_DATE); + playlistwin_update_list(); + break; + case PLAYLISTWIN_SORT_REVERSE: + playlist_reverse(); + playlistwin_update_list(); + break; + case PLAYLISTWIN_SORT_RANDOMIZE: + playlist_random(); + playlistwin_update_list(); + break; + } +} + +static void +playlistwin_sub_menu_callback(gpointer data, + guint action, + GtkWidget * widget) +{ + switch (action) { + case PLIST_NEW: + playlist_set_current_name(NULL); + playlist_clear(); + mainwin_clear_song_info(); + mainwin_set_info_text(); + break; + case PLIST_SAVE: + playlistwin_select_playlist_to_save(playlist_get_current_name()); + break; + case PLIST_DEFAULTSAVE: + playlist_save(bmp_paths[BMP_PATH_PLAYLIST_FILE]); + break; + case PLIST_SAVE_AS: + playlistwin_select_playlist_to_save(playlist_get_current_name()); + break; + case PLIST_LOAD: + playlistwin_select_playlist_to_load(playlist_get_current_name()); + break; + case SEL_INV: + playlistwin_inverse_selection(); + break; + case SEL_ZERO: + playlistwin_select_none(); + break; + case SEL_ALL: + playlistwin_select_all(); + break; + case SUB_ALL: + playlist_clear(); + mainwin_clear_song_info(); + mainwin_set_info_text(); + break; + case SUB_CROP: + playlist_delete(TRUE); + break; + case SUB_SELECTED: + playlist_delete(FALSE); + break; + case SUB_DUPLICATE_BYTITLE: + playlist_remove_duplicates(PLAYLIST_DUPS_TITLE); + break; + case SUB_DUPLICATE_BYFILENAME: + playlist_remove_duplicates(PLAYLIST_DUPS_FILENAME); + break; + case SUB_DUPLICATE_BYPATH: + playlist_remove_duplicates(PLAYLIST_DUPS_PATH); + break; + case PLAYLISTWIN_REMOVE_DEAD_FILES: + playlist_remove_dead_files(); + break; + case PLAYLISTWIN_REFRESH: + playlist_read_info_selection(); + playlistwin_update_list(); + break; + } +} + +static void +playlistwin_popup_menu_callback(gpointer data, + guint action, + GtkWidget * widget) +{ + extern GtkWidget *filepopupbutton; + + switch (action) { + case ADD_FILES: + playlistwin_show_filebrowser(); + break; + case CLOSE_PL_WINDOW: + playlistwin_hide(); + break; + case MISC_FILEINFO: + playlistwin_fileinfo(); + break; + case SEL_LOOKUP: + playlist_read_info_selection(); + break; + case MISC_QUEUE: + playlist_queue(); + break; + case PLIST_CQUEUE: + playlist_clear_queue(); + break; + case PLIST_JTT: + mainwin_jump_to_time(); + break; + case PLIST_JTF: + mainwin_jump_to_file(); + break; + case MISC_FILEPOPUP: + cfg.show_filepopup_for_tuple = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)); + if(filepopupbutton != NULL){ + gtk_signal_emit_by_name(GTK_OBJECT(filepopupbutton), "realize"); + } + break; + + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/ui_playlist.h Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,78 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef PLAYLISTWIN_H +#define PLAYLISTWIN_H + +#include <glib.h> + +#include "mainwin.h" +#include "widgets/widgetcore.h" + +#define PLAYLISTWIN_FRAME_TOP_HEIGHT 20 +#define PLAYLISTWIN_FRAME_BOTTOM_HEIGHT 38 +#define PLAYLISTWIN_FRAME_LEFT_WIDTH 12 +#define PLAYLISTWIN_FRAME_RIGHT_WIDTH 19 + +#define PLAYLISTWIN_MIN_WIDTH MAINWIN_WIDTH +#define PLAYLISTWIN_MIN_HEIGHT MAINWIN_HEIGHT +#define PLAYLISTWIN_WIDTH_SNAP 25 +#define PLAYLISTWIN_HEIGHT_SNAP 29 +#define PLAYLISTWIN_SHADED_HEIGHT MAINWIN_SHADED_HEIGHT +#define PLAYLISTWIN_WIDTH cfg.playlist_width +#define PLAYLISTWIN_HEIGHT \ + (cfg.playlist_shaded ? PLAYLISTWIN_SHADED_HEIGHT : cfg.playlist_height) + +#define PLAYLISTWIN_DEFAULT_WIDTH 275 +#define PLAYLISTWIN_DEFAULT_HEIGHT 232 +#define PLAYLISTWIN_DEFAULT_POS_X 295 +#define PLAYLISTWIN_DEFAULT_POS_Y 20 + +#define PLAYLISTWIN_DEFAULT_FONT "Sans Bold 8" + +gboolean playlistwin_is_shaded(void); +void playlistwin_update_list(void); +gboolean playlistwin_item_visible(gint index); +gint playlistwin_get_toprow(void); +void playlistwin_set_toprow(gint top); +void playlistwin_set_shade_menu_cb(gboolean shaded); +void playlistwin_set_shade(gboolean shaded); +void playlistwin_shade_toggle(void); +void playlistwin_create(void); +void draw_playlist_window(gboolean force); +void playlistwin_hide_timer(void); +void playlistwin_set_time(gint time, gint length, TimerMode mode); +void playlistwin_show(void); +void playlistwin_hide(void); +void playlistwin_set_back_pixmap(void); +void playlistwin_scroll(gint num); +void playlistwin_scroll_up_pushed(void); +void playlistwin_scroll_down_pushed(void); +void playlistwin_select_playlist_to_load(const gchar * default_filename); + +extern GtkWidget *playlistwin; +extern PlayList_List *playlistwin_list; + +extern PButton *playlistwin_shade, *playlistwin_close; + +extern gboolean playlistwin_focus; + +#endif
--- a/audacious/util.c Thu Sep 07 22:00:35 2006 -0700 +++ b/audacious/util.c Thu Sep 07 22:26:54 2006 -0700 @@ -53,6 +53,7 @@ #include "main.h" #include "playback.h" #include "playlist.h" +#include "ui_playlist.h" #ifdef USE_CHARDET #include "../libguess/libguess.h" @@ -763,57 +764,6 @@ return win; } -/* *** TO WA2GUI *** */ - -#if 0 -/* text_get_extents() taken from The GIMP (C) Spencer Kimball, Peter - * Mattis et al */ -gboolean -text_get_extents(const gchar * fontname, - const gchar * text, - gint * width, gint * height, gint * ascent, gint * descent) -{ - PangoFontDescription *font_desc; - PangoLayout *layout; - PangoRectangle rect; - - g_return_val_if_fail(fontname != NULL, FALSE); - g_return_val_if_fail(text != NULL, FALSE); - - /* FIXME: resolution */ - layout = gtk_widget_create_pango_layout(GTK_WIDGET(mainwin), text); - - font_desc = pango_font_description_from_string(fontname); - pango_layout_set_font_description(layout, font_desc); - pango_font_description_free(font_desc); - pango_layout_get_pixel_extents(layout, NULL, &rect); - - if (width) - *width = rect.width; - if (height) - *height = rect.height; - - if (ascent || descent) { - PangoLayoutIter *iter; - PangoLayoutLine *line; - - iter = pango_layout_get_iter(layout); - line = pango_layout_iter_get_line(iter); - pango_layout_iter_free(iter); - - pango_layout_line_get_pixel_extents(line, NULL, &rect); - - if (ascent) - *ascent = PANGO_ASCENT(rect); - if (descent) - *descent = -PANGO_DESCENT(rect); - } - - g_object_unref(layout); - - return TRUE; -} - static void filebrowser_add_files(GtkFileChooser * browser, GSList * files) @@ -1095,56 +1045,6 @@ gtk_window_present(GTK_WINDOW(dialog)); } -#endif - -/******************************************************************** keep in util.c */ - -GtkWidget * -make_filebrowser(const gchar * title, - GtkWidget *parent, - gboolean save) -{ - GtkWidget *dialog; - GtkWidget *button; - GtkWidget *button_close; - - g_return_val_if_fail(title != NULL, NULL); - - dialog = gtk_file_chooser_dialog_new(title, GTK_WINDOW(parent), - GTK_FILE_CHOOSER_ACTION_OPEN, NULL, NULL); - if (save) - gtk_file_chooser_set_action(GTK_FILE_CHOOSER(dialog), - GTK_FILE_CHOOSER_ACTION_SAVE); - - if (!save) - gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); - - g_signal_connect(dialog, "destroy", - G_CALLBACK(gtk_widget_destroyed), &dialog); - -#ifdef HAVE_GNOME_VFS - gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dialog), FALSE); -#endif - - button_close = gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, - GTK_RESPONSE_REJECT); - gtk_button_set_use_stock(GTK_BUTTON(button_close), TRUE); - GTK_WIDGET_SET_FLAGS(button_close, GTK_CAN_DEFAULT); - g_signal_connect_swapped(button_close, "clicked", - G_CALLBACK(gtk_widget_destroy), dialog); - - button = gtk_dialog_add_button(GTK_DIALOG(dialog), save ? - GTK_STOCK_SAVE : GTK_STOCK_OPEN, - GTK_RESPONSE_ACCEPT); - gtk_button_set_use_stock(GTK_BUTTON(button), TRUE); - GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT); - gtk_window_set_default(GTK_WINDOW(dialog), button); - - gtk_widget_show(dialog); - - return dialog; -} - GdkFont * util_font_load(const gchar * name) { @@ -1192,6 +1092,54 @@ gdk_window_set_cursor(window->window, cursor); } +/* text_get_extents() taken from The GIMP (C) Spencer Kimball, Peter + * Mattis et al */ +gboolean +text_get_extents(const gchar * fontname, + const gchar * text, + gint * width, gint * height, gint * ascent, gint * descent) +{ + PangoFontDescription *font_desc; + PangoLayout *layout; + PangoRectangle rect; + + g_return_val_if_fail(fontname != NULL, FALSE); + g_return_val_if_fail(text != NULL, FALSE); + + /* FIXME: resolution */ + layout = gtk_widget_create_pango_layout(GTK_WIDGET(mainwin), text); + + font_desc = pango_font_description_from_string(fontname); + pango_layout_set_font_description(layout, font_desc); + pango_font_description_free(font_desc); + pango_layout_get_pixel_extents(layout, NULL, &rect); + + if (width) + *width = rect.width; + if (height) + *height = rect.height; + + if (ascent || descent) { + PangoLayoutIter *iter; + PangoLayoutLine *line; + + iter = pango_layout_get_iter(layout); + line = pango_layout_iter_get_line(iter); + pango_layout_iter_free(iter); + + pango_layout_line_get_pixel_extents(line, NULL, &rect); + + if (ascent) + *ascent = PANGO_ASCENT(rect); + if (descent) + *descent = -PANGO_DESCENT(rect); + } + + g_object_unref(layout); + + return TRUE; +} + /* counts number of digits in a gint */ guint gint_count_digits(gint n) @@ -1410,6 +1358,52 @@ return TRUE; } +GtkWidget * +make_filebrowser(const gchar * title, + gboolean save) +{ + GtkWidget *dialog; + GtkWidget *button; + GtkWidget *button_close; + + g_return_val_if_fail(title != NULL, NULL); + + dialog = gtk_file_chooser_dialog_new(title, GTK_WINDOW(mainwin), + GTK_FILE_CHOOSER_ACTION_OPEN, NULL, NULL); + if (save) + gtk_file_chooser_set_action(GTK_FILE_CHOOSER(dialog), + GTK_FILE_CHOOSER_ACTION_SAVE); + + if (!save) + gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); + + g_signal_connect(dialog, "destroy", + G_CALLBACK(gtk_widget_destroyed), &dialog); + +#ifdef HAVE_GNOME_VFS + gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dialog), FALSE); +#endif + + button_close = gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, + GTK_RESPONSE_REJECT); + gtk_button_set_use_stock(GTK_BUTTON(button_close), TRUE); + GTK_WIDGET_SET_FLAGS(button_close, GTK_CAN_DEFAULT); + g_signal_connect_swapped(button_close, "clicked", + G_CALLBACK(gtk_widget_destroy), dialog); + + button = gtk_dialog_add_button(GTK_DIALOG(dialog), save ? + GTK_STOCK_SAVE : GTK_STOCK_OPEN, + GTK_RESPONSE_ACCEPT); + gtk_button_set_use_stock(GTK_BUTTON(button), TRUE); + GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT); + gtk_window_set_default(GTK_WINDOW(dialog), button); + + gtk_widget_show(dialog); + + return dialog; +} + + GtkItemFactory * create_menu(GtkItemFactoryEntry *entries, guint n_entries,
--- a/audacious/util.h Thu Sep 07 22:00:35 2006 -0700 +++ b/audacious/util.h Thu Sep 07 22:26:54 2006 -0700 @@ -114,7 +114,7 @@ const gchar *item_path, GtkItemFactory *submenu); -GtkWidget *make_filebrowser(const gchar * title, GtkWidget *parent, +GtkWidget *make_filebrowser(const gchar * title, gboolean save); /* Not sure this should be here? */
--- a/audacious/visualization.c Thu Sep 07 22:00:35 2006 -0700 +++ b/audacious/visualization.c Thu Sep 07 22:26:54 2006 -0700 @@ -32,7 +32,7 @@ #include "playback.h" #include "plugin.h" #include "prefswin.h" -#include "interface.h" +#include "widgets/widgetcore.h" VisPluginData vp_data = { NULL, @@ -280,15 +280,12 @@ gint i; if (!pcm_data || nch < 1) { - current_interface->send_pcm_data(pcm_data, nch, length); -#if 0 if (cfg.vis_type != VIS_OFF) { if (cfg.player_shaded && cfg.player_visible) svis_timeout_func(mainwin_svis, NULL); else vis_timeout_func(active_vis, NULL); } -#endif return; } @@ -329,8 +326,6 @@ node = g_list_next(node); } - current_interface->send_pcm_data(pcm_data, nch, length); -#if 0 if (cfg.vis_type == VIS_OFF) return; @@ -439,5 +434,4 @@ svis_timeout_func(mainwin_svis, intern_vis_data); else vis_timeout_func(active_vis, intern_vis_data); -#endif }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/Makefile Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,38 @@ +include ../../mk/rules.mk +include ../../mk/init.mk + +OBJECTIVE_LIBS_NOINST = libwidgets.a + +LDFLAGS += -Wl,-export-dynamic + +CFLAGS += \ + $(GTK_CFLAGS) \ + $(LIBGLADE_CFLAGS) \ + $(BEEP_DEFINES) \ + $(ARCH_DEFINES) \ + -I../.. \ + -I.. \ + -I../../intl \ + +SOURCES = \ + widget.c \ + sbutton.c \ + pbutton.c \ + tbutton.c \ + textbox.c \ + hslider.c \ + menurow.c \ + monostereo.c \ + vis.c \ + svis.c \ + number.c \ + playstatus.c \ + playlist_list.c \ + playlist_slider.c \ + eq_graph.c \ + eq_slider.c \ + skin.c + +OBJECTS = ${SOURCES:.c=.o} + +include ../../mk/objective.mk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/eq_graph.c Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,149 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "widgetcore.h" + +#include <glib.h> + +#include "main.h" +#include "skin.h" + +void +init_spline(gfloat * x, gfloat * y, gint n, gfloat * y2) +{ + gint i, k; + gfloat p, qn, sig, un, *u; + + u = (gfloat *) g_malloc(n * sizeof(gfloat)); + + y2[0] = u[0] = 0.0; + + for (i = 1; i < n - 1; i++) { + sig = ((gfloat) x[i] - x[i - 1]) / ((gfloat) x[i + 1] - x[i - 1]); + p = sig * y2[i - 1] + 2.0; + y2[i] = (sig - 1.0) / p; + u[i] = + (((gfloat) y[i + 1] - y[i]) / (x[i + 1] - x[i])) - + (((gfloat) y[i] - y[i - 1]) / (x[i] - x[i - 1])); + u[i] = (6.0 * u[i] / (x[i + 1] - x[i - 1]) - sig * u[i - 1]) / p; + } + qn = un = 0.0; + + y2[n - 1] = (un - qn * u[n - 2]) / (qn * y2[n - 2] + 1.0); + for (k = n - 2; k >= 0; k--) + y2[k] = y2[k] * y2[k + 1] + u[k]; + g_free(u); +} + +gfloat +eval_spline(gfloat xa[], gfloat ya[], gfloat y2a[], gint n, gfloat x) +{ + gint klo, khi, k; + gfloat h, b, a; + + klo = 0; + khi = n - 1; + while (khi - klo > 1) { + k = (khi + klo) >> 1; + if (xa[k] > x) + khi = k; + else + klo = k; + } + h = xa[khi] - xa[klo]; + a = (xa[khi] - x) / h; + b = (x - xa[klo]) / h; + return (a * ya[klo] + b * ya[khi] + + ((a * a * a - a) * y2a[klo] + + (b * b * b - b) * y2a[khi]) * (h * h) / 6.0); +} + +void +eqgraph_draw(Widget * w) +{ + EqGraph *eg = (EqGraph *) w; + GdkPixmap *obj; + GdkColor col; + guint32 cols[19]; + gint i, y, ymin, ymax, py = 0; + gfloat x[] = { 0, 11, 23, 35, 47, 59, 71, 83, 97, 109 }, yf[10]; + + /* + * This avoids the init_spline() function to be inlined. + * Inlining the function caused troubles when compiling with + * `-O' (at least on FreeBSD). + */ + void (*__init_spline) (gfloat *, gfloat *, gint, gfloat *) = init_spline; + + obj = eg->eg_widget.parent; + skin_draw_pixmap(bmp_active_skin, obj, eg->eg_widget.gc, SKIN_EQMAIN, + 0, 294, eg->eg_widget.x, eg->eg_widget.y, + eg->eg_widget.width, eg->eg_widget.height); + skin_draw_pixmap(bmp_active_skin, obj, eg->eg_widget.gc, SKIN_EQMAIN, + 0, 314, eg->eg_widget.x, + eg->eg_widget.y + 9 + + ((cfg.equalizer_preamp * 9) / 20), + eg->eg_widget.width, 1); + + skin_get_eq_spline_colors(bmp_active_skin, cols); + + __init_spline(x, cfg.equalizer_bands, 10, yf); + for (i = 0; i < 109; i++) { + y = 9 - + (gint) ((eval_spline(x, cfg.equalizer_bands, yf, 10, i) * + 9.0) / 20.0); + if (y < 0) + y = 0; + if (y > 18) + y = 18; + if (!i) + py = y; + if (y < py) { + ymin = y; + ymax = py; + } + else { + ymin = py; + ymax = y; + } + py = y; + for (y = ymin; y <= ymax; y++) { + col.pixel = cols[y]; + gdk_gc_set_foreground(eg->eg_widget.gc, &col); + gdk_draw_point(obj, eg->eg_widget.gc, eg->eg_widget.x + i + 2, + eg->eg_widget.y + y); + } + } +} + +EqGraph * +create_eqgraph(GList ** wlist, GdkPixmap * parent, GdkGC * gc, gint x, gint y) +{ + EqGraph *eg; + + eg = g_new0(EqGraph, 1); + widget_init(&eg->eg_widget, parent, gc, x, y, 113, 19, TRUE); + eg->eg_widget.draw = eqgraph_draw; + + widget_list_add(wlist, WIDGET(eg)); + + return eg; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/eq_graph.h Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,44 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _WIDGETCORE_H_ +#error Please do not include me directly! Use widgetcore.h instead! +#endif + +#ifndef EQ_GRAPH_H +#define EQ_GRAPH_H + +#include <glib.h> +#include <gdk/gdk.h> + +#include "widget.h" + +#define EQ_GRAPH(x) ((EqGraph *)(x)) +struct _EqGraph { + Widget eg_widget; +}; + +typedef struct _EqGraph EqGraph; + +EqGraph *create_eqgraph(GList ** wlist, GdkPixmap * parent, GdkGC * gc, + gint x, gint y); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/eq_slider.c Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,235 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "widgetcore.h" + +#include <glib.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> +#include <gdk/gdk.h> + +#include "equalizer.h" +#include "mainwin.h" +#include "skin.h" + +void +eqslider_set_position(EqSlider * es, + gfloat pos) +{ + es->es_position = 25 - (gint) ((pos * 25.0) / 20.0); + + if (es->es_position < 0) + es->es_position = 0; + + if (es->es_position > 50) + es->es_position = 50; + + if (es->es_position >= 24 && es->es_position <= 26) + es->es_position = 25; + + widget_draw(WIDGET(es)); +} + +gfloat +eqslider_get_position(EqSlider * es) +{ + return 20.0 - (((gfloat) es->es_position * 20.0) / 25.0); +} + +void +eqslider_draw(Widget * w) +{ + EqSlider *es = (EqSlider *) w; + GdkPixmap *obj; + SkinPixmapId src; + gint frame; + + src = SKIN_EQMAIN; + obj = es->es_widget.parent; + + frame = 27 - ((es->es_position * 27) / 50); + if (frame < 14) + skin_draw_pixmap(bmp_active_skin, obj, es->es_widget.gc, src, + (frame * 15) + 13, 164, es->es_widget.x, + es->es_widget.y, es->es_widget.width, + es->es_widget.height); + else + skin_draw_pixmap(bmp_active_skin, obj, es->es_widget.gc, src, + ((frame - 14) * 15) + 13, 229, es->es_widget.x, + es->es_widget.y, es->es_widget.width, + es->es_widget.height); + if (es->es_isdragging) + skin_draw_pixmap(bmp_active_skin, obj, es->es_widget.gc, src, 0, + 176, es->es_widget.x + 1, + es->es_widget.y + es->es_position, 11, 11); + else + skin_draw_pixmap(bmp_active_skin, obj, es->es_widget.gc, src, 0, + 164, es->es_widget.x + 1, + es->es_widget.y + es->es_position, 11, 11); +} + +void +eqslider_set_mainwin_text(EqSlider * es) +{ + gint band = 0; + const gchar *bandname[11] = { N_("PREAMP"), N_("60HZ"), N_("170HZ"), + N_("310HZ"), N_("600HZ"), N_("1KHZ"), + N_("3KHZ"), N_("6KHZ"), N_("12KHZ"), + N_("14KHZ"), N_("16KHZ") + }; + gchar *tmp; + + if (es->es_widget.x > 21) + band = ((es->es_widget.x - 78) / 18) + 1; + + tmp = + g_strdup_printf("EQ: %s: %+.1f DB", _(bandname[band]), + eqslider_get_position(es)); + mainwin_lock_info_text(tmp); + g_free(tmp); +} + +void +eqslider_button_press_cb(GtkWidget * w, + GdkEventButton * event, + gpointer data) +{ + EqSlider *es = EQ_SLIDER(data); + gint y; + + if (widget_contains(&es->es_widget, event->x, event->y)) { + if (event->button == 1) { + y = event->y - es->es_widget.y; + es->es_isdragging = TRUE; + if (y >= es->es_position && y < es->es_position + 11) + es->es_drag_y = y - es->es_position; + else { + es->es_position = y - 5; + es->es_drag_y = 5; + if (es->es_position < 0) + es->es_position = 0; + if (es->es_position > 50) + es->es_position = 50; + if (es->es_position >= 24 && es->es_position <= 26) + es->es_position = 25; + equalizerwin_eq_changed(); + } + + eqslider_set_mainwin_text(es); + widget_draw(WIDGET(es)); + } + if (event->button == 4) { + es->es_position -= 2; + if (es->es_position < 0) + es->es_position = 0; + equalizerwin_eq_changed(); + widget_draw(WIDGET(es)); + } + } +} + +void +eqslider_mouse_scroll_cb(GtkWidget * w, + GdkEventScroll * event, + gpointer data) +{ + EqSlider *es = EQ_SLIDER(data); + + if (!widget_contains(&es->es_widget, event->x, event->y)) + return; + + if (event->direction == GDK_SCROLL_UP) { + es->es_position -= 2; + + if (es->es_position < 0) + es->es_position = 0; + + equalizerwin_eq_changed(); + widget_draw(WIDGET(es)); + } + else { + es->es_position += 2; + + if (es->es_position > 50) + es->es_position = 50; + + equalizerwin_eq_changed(); + widget_draw(WIDGET(es)); + } +} + +void +eqslider_motion_cb(GtkWidget * w, + GdkEventMotion * event, + gpointer data) +{ + EqSlider *es = EQ_SLIDER(data); + gint y; + + y = event->y - es->es_widget.y; + if (es->es_isdragging) { + es->es_position = y - es->es_drag_y; + if (es->es_position < 0) + es->es_position = 0; + if (es->es_position > 50) + es->es_position = 50; + if (es->es_position >= 24 && es->es_position <= 26) + es->es_position = 25; + equalizerwin_eq_changed(); + eqslider_set_mainwin_text(es); + widget_draw(WIDGET(es)); + } +} + +void +eqslider_button_release_cb(GtkWidget * w, + GdkEventButton * event, + gpointer data) +{ + EqSlider *es = EQ_SLIDER(data); + + if (es->es_isdragging) { + es->es_isdragging = FALSE; + mainwin_release_info_text(); + widget_draw(WIDGET(es)); + } +} + +EqSlider * +create_eqslider(GList ** wlist, + GdkPixmap * parent, + GdkGC * gc, + gint x, gint y) +{ + EqSlider *es; + + es = g_new0(EqSlider, 1); + widget_init(&es->es_widget, parent, gc, x, y, 14, 63, TRUE); + es->es_widget.button_press_cb = eqslider_button_press_cb; + es->es_widget.button_release_cb = eqslider_button_release_cb; + es->es_widget.motion_cb = eqslider_motion_cb; + es->es_widget.draw = eqslider_draw; + es->es_widget.mouse_scroll_cb = eqslider_mouse_scroll_cb; + + widget_list_add(wlist, WIDGET(es)); + + return es; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/eq_slider.h Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,49 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _WIDGETCORE_H_ +#error Please do not include me directly! Use widgetcore.h instead! +#endif + +#ifndef EQ_SLIDER_H +#define EQ_SLIDER_H + +#include <glib.h> +#include <gdk/gdk.h> + +#include "widget.h" + +#define EQ_SLIDER(x) ((EqSlider *)(x)) +struct _EqSlider { + Widget es_widget; + gint es_position; + gboolean es_isdragging; + gint es_drag_y; +}; + +typedef struct _EqSlider EqSlider; + +EqSlider *create_eqslider(GList ** wlist, GdkPixmap * parent, GdkGC * gc, + gint x, gint y); +void eqslider_set_position(EqSlider * es, gfloat pos); +gfloat eqslider_get_position(EqSlider * es); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/hslider.c Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,203 @@ +/* XMMS - Cross-platform multimedia player + * Copyright (C) 1998-2000 Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "widgetcore.h" + +#include <glib.h> +#include <gdk/gdk.h> +#include <gtk/gtk.h> + +#include "skin.h" + +void +hslider_set_position(HSlider * hs, + gint pos) +{ + if (pos == hs->hs_position || hs->hs_pressed) + return; + + hs->hs_position = pos; + + if (hs->hs_frame_cb) + hs->hs_frame = hs->hs_frame_cb(hs->hs_position); + + widget_draw(WIDGET(hs)); +} + +gint +hslider_get_position(HSlider * hs) +{ + return hs->hs_position; +} + +void +hslider_draw(Widget * w) +{ + HSlider *hs = (HSlider *) w; + GdkPixmap *obj; + + obj = hs->hs_widget.parent; + + skin_draw_pixmap(bmp_active_skin, obj, hs->hs_widget.gc, + hs->hs_skin_index, hs->hs_frame_offset, + hs->hs_frame * hs->hs_frame_height, hs->hs_widget.x, + hs->hs_widget.y, hs->hs_widget.width, + hs->hs_widget.height); + if (hs->hs_pressed) + skin_draw_pixmap(bmp_active_skin, obj, hs->hs_widget.gc, + hs->hs_skin_index, hs->hs_knob_px, + hs->hs_knob_py, hs->hs_widget.x + hs->hs_position, + hs->hs_widget.y + + ((hs->hs_widget.height - hs->hs_knob_height) / 2), + hs->hs_knob_width, hs->hs_knob_height); + else + skin_draw_pixmap(bmp_active_skin, obj, hs->hs_widget.gc, + hs->hs_skin_index, hs->hs_knob_nx, hs->hs_knob_ny, + hs->hs_widget.x + hs->hs_position, + hs->hs_widget.y + + ((hs->hs_widget.height - hs->hs_knob_height) / 2), + hs->hs_knob_width, hs->hs_knob_height); +} + +void +hslider_button_press_cb(GtkWidget * w, + GdkEventButton * event, + gpointer data) +{ + HSlider *hs = HSLIDER(data); + gint x; + + if (event->button != 1) + return; + + if (widget_contains(&hs->hs_widget, event->x, event->y)) { + x = event->x - hs->hs_widget.x; + hs->hs_pressed = TRUE; + + if (x >= hs->hs_position && x < hs->hs_position + hs->hs_knob_width) + hs->hs_pressed_x = x - hs->hs_position; + else { + hs->hs_position = x - (hs->hs_knob_width / 2); + hs->hs_pressed_x = hs->hs_knob_width / 2; + if (hs->hs_position < hs->hs_min) + hs->hs_position = hs->hs_min; + if (hs->hs_position > hs->hs_max) + hs->hs_position = hs->hs_max; + if (hs->hs_frame_cb) + hs->hs_frame = hs->hs_frame_cb(hs->hs_position); + + } + + if (hs->hs_motion_cb) + hs->hs_motion_cb(hs->hs_position); + + widget_draw(WIDGET(hs)); + } +} + +void +hslider_motion_cb(GtkWidget * w, GdkEventMotion * event, gpointer data) +{ + HSlider *hs = (HSlider *) data; + gint x; + + if (hs->hs_pressed) { + if (!hs->hs_widget.visible) { + hs->hs_pressed = FALSE; + return; + } + + x = event->x - hs->hs_widget.x; + hs->hs_position = x - hs->hs_pressed_x; + + if (hs->hs_position < hs->hs_min) + hs->hs_position = hs->hs_min; + + if (hs->hs_position > hs->hs_max) + hs->hs_position = hs->hs_max; + + if (hs->hs_frame_cb) + hs->hs_frame = hs->hs_frame_cb(hs->hs_position); + + if (hs->hs_motion_cb) + hs->hs_motion_cb(hs->hs_position); + + widget_draw(WIDGET(hs)); + } +} + +void +hslider_button_release_cb(GtkWidget * w, + GdkEventButton * event, + gpointer data) +{ + HSlider *hs = HSLIDER(data); + + if (hs->hs_pressed) { + hs->hs_pressed = FALSE; + + if (hs->hs_release_cb) + hs->hs_release_cb(hs->hs_position); + + widget_draw(WIDGET(hs)); + } +} + +HSlider * +create_hslider(GList ** wlist, GdkPixmap * parent, GdkGC * gc, + gint x, gint y, gint w, gint h, gint knx, gint kny, + gint kpx, gint kpy, gint kw, gint kh, gint fh, + gint fo, gint min, gint max, gint(*fcb) (gint), + void (*mcb) (gint), void (*rcb) (gint), SkinPixmapId si) +{ + HSlider *hs; + + hs = g_new0(HSlider, 1); + widget_init(&hs->hs_widget, parent, gc, x, y, w, h, 1); + hs->hs_widget.button_press_cb = + (void (*)(GtkWidget *, GdkEventButton *, gpointer)) + hslider_button_press_cb; + hs->hs_widget.button_release_cb = + (void (*)(GtkWidget *, GdkEventButton *, gpointer)) + hslider_button_release_cb; + hs->hs_widget.motion_cb = + (void (*)(GtkWidget *, GdkEventMotion *, gpointer)) + hslider_motion_cb; + hs->hs_widget.draw = hslider_draw; + hs->hs_knob_nx = knx; + hs->hs_knob_ny = kny; + hs->hs_knob_px = kpx; + hs->hs_knob_py = kpy; + hs->hs_knob_width = kw; + hs->hs_knob_height = kh; + hs->hs_frame_height = fh; + hs->hs_frame_offset = fo; + hs->hs_min = min; + hs->hs_position = min; + hs->hs_max = max; + hs->hs_frame_cb = fcb; + hs->hs_motion_cb = mcb; + hs->hs_release_cb = rcb; + if (hs->hs_frame_cb) + hs->hs_frame = hs->hs_frame_cb(0); + hs->hs_skin_index = si; + + widget_list_add(wlist, WIDGET(hs)); + + return hs; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/hslider.h Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,59 @@ +/* XMMS - Cross-platform multimedia player + * Copyright (C) 1998-2000 Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _WIDGETCORE_H_ +#error Please do not include me directly! Use widgetcore.h instead! +#endif + +#ifndef HSLIDER_H +#define HSLIDER_H + +#include <glib.h> +#include <gdk/gdk.h> + +#include "skin.h" +#include "widget.h" + +#define HSLIDER(x) ((HSlider *)(x)) +struct _HSlider { + Widget hs_widget; + gint hs_frame, hs_frame_offset, hs_frame_height, hs_min, hs_max; + gint hs_knob_nx, hs_knob_ny, hs_knob_px, hs_knob_py; + gint hs_knob_width, hs_knob_height; + gint hs_position; + gboolean hs_pressed; + gint hs_pressed_x, hs_pressed_y; + gint(*hs_frame_cb) (gint); + void (*hs_motion_cb) (gint); + void (*hs_release_cb) (gint); + SkinPixmapId hs_skin_index; +}; + +typedef struct _HSlider HSlider; + +HSlider *create_hslider(GList ** wlist, GdkPixmap * parent, GdkGC * gc, + gint x, gint y, gint w, gint h, gint knx, gint kny, + gint kpx, gint kpy, gint kw, gint kh, gint fh, + gint fo, gint min, gint max, gint(*fcb) (gint), + void (*mcb) (gint), void (*rcb) (gint), + SkinPixmapId si); + +void hslider_set_position(HSlider * hs, gint pos); +gint hslider_get_position(HSlider * hs); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/menurow.c Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,186 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "widgetcore.h" + +#include <glib.h> +#include <gdk/gdk.h> + +#include "main.h" +#include "menurow.h" +#include "widget.h" + +void +menurow_draw(Widget * widget) +{ + MenuRow *mr = MENU_ROW(widget); + + GdkPixmap *obj = mr->mr_widget.parent; + + if (mr->mr_selected == MENUROW_NONE) { + if (cfg.always_show_cb || mr->mr_bpushed) + skin_draw_pixmap(bmp_active_skin, obj, + mr->mr_widget.gc, + mr->mr_skin_index, + mr->mr_nx, mr->mr_ny, + mr->mr_widget.x, mr->mr_widget.y, 8, 43); + else + skin_draw_pixmap(bmp_active_skin, obj, + mr->mr_widget.gc, + mr->mr_skin_index, + mr->mr_nx + 8, mr->mr_ny, + mr->mr_widget.x, mr->mr_widget.y, 8, 43); + } + else { + skin_draw_pixmap(bmp_active_skin, obj, + mr->mr_widget.gc, + mr->mr_skin_index, + mr->mr_sx + ((mr->mr_selected - 1) * 8), + mr->mr_sy, mr->mr_widget.x, mr->mr_widget.y, 8, 43); + } + if (cfg.always_show_cb || mr->mr_bpushed) { + if (mr->mr_always_selected) + skin_draw_pixmap(bmp_active_skin, obj, + mr->mr_widget.gc, + mr->mr_skin_index, + mr->mr_sx + 8, mr->mr_sy + 10, + mr->mr_widget.x, mr->mr_widget.y + 10, 8, 8); + if (mr->mr_doublesize_selected) + skin_draw_pixmap(bmp_active_skin, obj, + mr->mr_widget.gc, + mr->mr_skin_index, + mr->mr_sx + 24, mr->mr_sy + 26, + mr->mr_widget.x, mr->mr_widget.y + 26, 8, 8); + } + +} + +MenuRowItem +menurow_find_selected(MenuRow * mr, gint x, gint y) +{ + MenuRowItem ret = MENUROW_NONE; + + x -= mr->mr_widget.x; + y -= mr->mr_widget.y; + if (x > 0 && x < 8) { + if (y >= 0 && y <= 10) + ret = MENUROW_OPTIONS; + if (y >= 10 && y <= 17) + ret = MENUROW_ALWAYS; + if (y >= 18 && y <= 25) + ret = MENUROW_FILEINFOBOX; + if (y >= 26 && y <= 33) + ret = MENUROW_DOUBLESIZE; + if (y >= 34 && y <= 42) + ret = MENUROW_VISUALIZATION; + } + return ret; +} + +void +menurow_button_press(GtkWidget * widget, + GdkEventButton * event, + gpointer data) +{ + MenuRow *mr = MENU_ROW(data); + + if (event->button != 1) + return; + + if (widget_contains(&mr->mr_widget, event->x, event->y)) { + mr->mr_bpushed = TRUE; + mr->mr_selected = menurow_find_selected(mr, event->x, event->y); + + widget_draw(WIDGET(mr)); + + if (mr->mr_change_callback) + mr->mr_change_callback(mr->mr_selected); + } +} + +void +menurow_motion(GtkWidget * widget, + GdkEventMotion * event, + gpointer data) +{ + MenuRow *mr = MENU_ROW(data); + + if (mr->mr_bpushed) { + mr->mr_selected = menurow_find_selected(mr, event->x, event->y); + + widget_draw(WIDGET(mr)); + + if (mr->mr_change_callback) + mr->mr_change_callback(mr->mr_selected); + } +} + +void +menurow_button_release(GtkWidget * widget, + GdkEventButton * event, + gpointer data) +{ + MenuRow *mr = MENU_ROW(data); + + if (mr->mr_bpushed) { + mr->mr_bpushed = FALSE; + + if (mr->mr_selected == MENUROW_ALWAYS) + mr->mr_always_selected = !mr->mr_always_selected; + + if (mr->mr_selected == MENUROW_DOUBLESIZE) + mr->mr_doublesize_selected = !mr->mr_doublesize_selected; + + if ((int)(mr->mr_selected) != -1 && mr->mr_release_callback) + mr->mr_release_callback(mr->mr_selected); + + mr->mr_selected = MENUROW_NONE; + + widget_draw(WIDGET(mr)); + } +} + +MenuRow * +create_menurow(GList ** wlist, GdkPixmap * parent, GdkGC * gc, + gint x, gint y, gint nx, gint ny, gint sx, gint sy, + void (*ccb) (MenuRowItem), + void (*rcb) (MenuRowItem), SkinPixmapId si) +{ + MenuRow *mr; + + mr = g_new0(MenuRow, 1); + widget_init(&mr->mr_widget, parent, gc, x, y, 8, 43, 1); + mr->mr_widget.draw = menurow_draw; + mr->mr_widget.button_press_cb = menurow_button_press; + mr->mr_widget.motion_cb = menurow_motion; + mr->mr_widget.button_release_cb = menurow_button_release; + mr->mr_nx = nx; + mr->mr_ny = ny; + mr->mr_sx = sx; + mr->mr_sy = sy; + mr->mr_selected = MENUROW_NONE; + mr->mr_change_callback = ccb; + mr->mr_release_callback = rcb; + mr->mr_skin_index = si; + + widget_list_add(wlist, WIDGET(mr)); + return mr; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/menurow.h Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,61 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _WIDGETCORE_H_ +#error Please do not include me directly! Use widgetcore.h instead! +#endif + +#ifndef MENUROW_H +#define MENUROW_H + +#include <glib.h> +#include <gdk/gdk.h> + +#include "skin.h" +#include "widget.h" + +typedef enum { + MENUROW_NONE, MENUROW_OPTIONS, MENUROW_ALWAYS, MENUROW_FILEINFOBOX, + MENUROW_DOUBLESIZE, MENUROW_VISUALIZATION +} MenuRowItem; + +#define MENU_ROW(x) ((MenuRow *)(x)) +struct _MenuRow { + Widget mr_widget; + gint mr_nx, mr_ny; + gint mr_sx, mr_sy; + MenuRowItem mr_selected; + gboolean mr_bpushed; + gboolean mr_always_selected; + gboolean mr_doublesize_selected; + void (*mr_change_callback) (MenuRowItem); + void (*mr_release_callback) (MenuRowItem); + SkinPixmapId mr_skin_index; +}; + +typedef struct _MenuRow MenuRow; + +MenuRow *create_menurow(GList ** wlist, GdkPixmap * parent, GdkGC * gc, + gint x, gint y, gint nx, gint ny, gint sx, gint sy, + void (*ccb) (MenuRowItem), + void (*rcb) (MenuRowItem), SkinPixmapId si); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/monostereo.c Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,93 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "widgetcore.h" + +#include <glib.h> +#include <gdk/gdk.h> + +#include "skin.h" +#include "widget.h" + +void +monostereo_draw(Widget * widget) +{ + MonoStereo *ms = (MonoStereo *) widget; + GdkPixmap *obj; + + obj = ms->ms_widget.parent; + + switch (ms->ms_num_channels) { + case 0: + skin_draw_pixmap(bmp_active_skin, obj, ms->ms_widget.gc, + ms->ms_skin_index, 29, 12, + ms->ms_widget.x, ms->ms_widget.y, 27, 12); + skin_draw_pixmap(bmp_active_skin, obj, ms->ms_widget.gc, + ms->ms_skin_index, 0, 12, + ms->ms_widget.x + 27, ms->ms_widget.y, 29, 12); + break; + case 1: + skin_draw_pixmap(bmp_active_skin, obj, ms->ms_widget.gc, + ms->ms_skin_index, 29, 0, + ms->ms_widget.x, ms->ms_widget.y, 27, 12); + skin_draw_pixmap(bmp_active_skin, obj, ms->ms_widget.gc, + ms->ms_skin_index, 0, 12, + ms->ms_widget.x + 27, ms->ms_widget.y, 29, 12); + break; + case 2: + skin_draw_pixmap(bmp_active_skin, obj, ms->ms_widget.gc, + ms->ms_skin_index, 29, 12, + ms->ms_widget.x, ms->ms_widget.y, 27, 12); + skin_draw_pixmap(bmp_active_skin, obj, ms->ms_widget.gc, + ms->ms_skin_index, 0, 0, + ms->ms_widget.x + 27, ms->ms_widget.y, 29, 12); + break; + } +} + +void +monostereo_set_num_channels(MonoStereo * ms, + gint nch) +{ + if (!ms) + return; + + ms->ms_num_channels = nch; + widget_draw(WIDGET(ms)); +} + +MonoStereo * +create_monostereo(GList ** wlist, + GdkPixmap * parent, + GdkGC * gc, + gint x, gint y, + SkinPixmapId si) +{ + MonoStereo *ms; + + ms = g_new0(MonoStereo, 1); + widget_init(&ms->ms_widget, parent, gc, x, y, 56, 12, 1); + ms->ms_widget.draw = monostereo_draw; + ms->ms_skin_index = si; + + widget_list_add(wlist, WIDGET(ms)); + return ms; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/monostereo.h Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,48 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _WIDGETCORE_H_ +#error Please do not include me directly! Use widgetcore.h instead! +#endif + +#ifndef MONOSTEREO_H +#define MONOSTEREO_H + +#include <glib.h> +#include <gdk/gdk.h> + +#include "skin.h" +#include "widget.h" + +#define MONO_STEREO(x) ((MonoStereo *)(x)) +struct _MonoStereo { + Widget ms_widget; + gint ms_num_channels; + SkinPixmapId ms_skin_index; +}; + +typedef struct _MonoStereo MonoStereo; + +MonoStereo *create_monostereo(GList ** wlist, GdkPixmap * parent, + GdkGC * gc, gint x, gint y, SkinPixmapId si); +void monostereo_set_num_channels(MonoStereo * ms, gint nch); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/number.c Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,75 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "widgetcore.h" + +#include <glib.h> +#include <gdk/gdk.h> + +#include "skin.h" + +void +number_set_number(Number * nu, + gint number) +{ + if (number == nu->nu_number) + return; + + nu->nu_number = number; + widget_draw(WIDGET(nu)); +} + +void +number_draw(Widget * w) +{ + Number *nu = NUMBER(w); + GdkPixmap *obj; + + obj = nu->nu_widget.parent; + + if (nu->nu_number <= 11) + skin_draw_pixmap(bmp_active_skin, obj, nu->nu_widget.gc, + nu->nu_skin_index, nu->nu_number * 9, 0, + nu->nu_widget.x, nu->nu_widget.y, 9, 13); + else + skin_draw_pixmap(bmp_active_skin, obj, nu->nu_widget.gc, + nu->nu_skin_index, 90, 0, nu->nu_widget.x, + nu->nu_widget.y, 9, 13); +} + +Number * +create_number(GList ** wlist, + GdkPixmap * parent, + GdkGC * gc, + gint x, gint y, + SkinPixmapId si) +{ + Number *nu; + + nu = g_new0(Number, 1); + widget_init(&nu->nu_widget, parent, gc, x, y, 9, 13, 1); + nu->nu_widget.draw = number_draw; + nu->nu_number = 10; + nu->nu_skin_index = si; + + widget_list_add(wlist, WIDGET(nu)); + return nu; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/number.h Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,48 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _WIDGETCORE_H_ +#error Please do not include me directly! Use widgetcore.h instead! +#endif + +#ifndef NUMBER_H +#define NUMBER_H + +#include <glib.h> +#include <gdk/gdk.h> + +#include "widget.h" +#include "skin.h" + +#define NUMBER(x) ((Number *)(x)) +struct _Number { + Widget nu_widget; + gint nu_number; + SkinPixmapId nu_skin_index; +}; + +typedef struct _Number Number; + +void number_set_number(Number * nu, gint number); +Number *create_number(GList ** wlist, GdkPixmap * parent, GdkGC * gc, + gint x, gint y, SkinPixmapId si); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/pbutton.c Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,194 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "widgetcore.h" + +#include <glib.h> +#include <gtk/gtk.h> +#include <gdk/gdk.h> + +#include "skin.h" +#include "widget.h" + +void +pbutton_draw(PButton * button) +{ + GdkPixmap *obj; + + if (button->pb_allow_draw) { + obj = button->pb_widget.parent; + + if (button->pb_pressed && button->pb_inside) { + skin_draw_pixmap(bmp_active_skin, obj, + button->pb_widget.gc, + button->pb_skin_index2, button->pb_px, + button->pb_py, button->pb_widget.x, + button->pb_widget.y, + button->pb_widget.width, + button->pb_widget.height); + } + else { + skin_draw_pixmap(bmp_active_skin, obj, + button->pb_widget.gc, + button->pb_skin_index1, + button->pb_nx, button->pb_ny, + button->pb_widget.x, button->pb_widget.y, + button->pb_widget.width, + button->pb_widget.height); + } + } +} + +void +pbutton_button_press_cb(GtkWidget * widget, + GdkEventButton * event, + PButton * button) +{ + if (event->button != 1) + return; + + if (widget_contains(&button->pb_widget, event->x, event->y)) { + button->pb_pressed = 1; + button->pb_inside = 1; + widget_draw(WIDGET(button)); + if (button->pb_push_cb) + button->pb_push_cb(); + } +} + +void +pbutton_button_release_cb(GtkWidget * widget, + GdkEventButton * event, + PButton * button) +{ + if (event->button != 1) + return; + if (button->pb_inside && button->pb_pressed) { + button->pb_inside = 0; + widget_draw(WIDGET(button)); + if (button->pb_release_cb) + button->pb_release_cb(); + } + if (button->pb_pressed) + button->pb_pressed = 0; +} + +void +pbutton_motion_cb(GtkWidget * widget, GdkEventMotion * event, + PButton * button) +{ + gint inside; + + if (!button->pb_pressed) + return; + + inside = widget_contains(&button->pb_widget, event->x, event->y); + + if (inside != button->pb_inside) { + button->pb_inside = inside; + widget_draw(WIDGET(button)); + } +} + +void +pbutton_set_skin_index(PButton * b, SkinPixmapId si) +{ + b->pb_skin_index1 = b->pb_skin_index2 = si; +} + +void +pbutton_set_skin_index1(PButton * b, SkinPixmapId si) +{ + b->pb_skin_index1 = si; +} + +void +pbutton_set_skin_index2(PButton * b, SkinPixmapId si) +{ + b->pb_skin_index2 = si; +} + +void +pbutton_set_button_data(PButton * b, gint nx, gint ny, gint px, gint py) +{ + if (nx > -1) + b->pb_nx = nx; + if (ny > -1) + b->pb_ny = ny; + if (px > -1) + b->pb_px = px; + if (py > -1) + b->pb_py = py; +} + + +PButton * +create_pbutton_ex(GList ** wlist, GdkPixmap * parent, GdkGC * gc, + gint x, gint y, gint w, gint h, gint nx, + gint ny, gint px, gint py, void (*push_cb) (void), + void (*release_cb) (void), + SkinPixmapId si1, SkinPixmapId si2) +{ + PButton *b; + + b = g_new0(PButton, 1); + widget_init(&b->pb_widget, parent, gc, x, y, w, h, 1); + b->pb_widget.button_press_cb = + (void (*)(GtkWidget *, GdkEventButton *, gpointer)) + pbutton_button_press_cb; + b->pb_widget.button_release_cb = + (void (*)(GtkWidget *, GdkEventButton *, gpointer)) + pbutton_button_release_cb; + b->pb_widget.motion_cb = + (void (*)(GtkWidget *, GdkEventMotion *, gpointer)) + pbutton_motion_cb; + + b->pb_widget.draw = (void (*)(Widget *)) pbutton_draw; + b->pb_nx = nx; + b->pb_ny = ny; + b->pb_px = px; + b->pb_py = py; + b->pb_push_cb = push_cb; + b->pb_release_cb = release_cb; + b->pb_skin_index1 = si1; + b->pb_skin_index2 = si2; + b->pb_allow_draw = TRUE; + b->pb_inside = 0; + b->pb_pressed = 0; + widget_list_add(wlist, WIDGET(b)); + + return b; +} + +PButton * +create_pbutton(GList ** wlist, GdkPixmap * parent, GdkGC * gc, + gint x, gint y, gint w, gint h, gint nx, gint ny, + gint px, gint py, void (*cb) (void), SkinPixmapId si) +{ + return create_pbutton_ex(wlist, parent, gc, x, y, w, h, nx, ny, px, py, + NULL, cb, si, si); +} + +void +free_pbutton(PButton * b) +{ + g_free(b); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/pbutton.h Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,64 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _WIDGETCORE_H_ +#error Please do not include me directly! Use widgetcore.h instead! +#endif + +#ifndef PBUTTON_H +#define PBUTTON_H + +#include <glib.h> +#include <gdk/gdk.h> + +#include "widget.h" +#include "skin.h" + +#define PBUTTON(x) ((PButton *)(x)) +struct _PButton { + Widget pb_widget; + gint pb_nx, pb_ny; + gint pb_px, pb_py; + gboolean pb_pressed; + gboolean pb_inside; + gboolean pb_allow_draw; + void (*pb_push_cb) (void); + void (*pb_release_cb) (void); + SkinPixmapId pb_skin_index1, pb_skin_index2; +}; + +typedef struct _PButton PButton; + +PButton *create_pbutton(GList ** wlist, GdkPixmap * parent, GdkGC * gc, + gint x, gint y, gint w, gint h, gint nx, gint ny, + gint px, gint py, void (*push_cb) (void), SkinPixmapId si); +PButton *create_pbutton_ex(GList ** wlist, GdkPixmap * parent, GdkGC * gc, + gint x, gint y, gint w, gint h, gint nx, + gint ny, gint px, gint py, void (*push_cb) (void), + void (*release_cb) (void), SkinPixmapId si1, + SkinPixmapId si2); +void free_pbutton(PButton * b); +void pbutton_set_skin_index(PButton * b, SkinPixmapId si); +void pbutton_set_skin_index1(PButton * b, SkinPixmapId si); +void pbutton_set_skin_index2(PButton * b, SkinPixmapId si); +void pbutton_set_button_data(PButton * b, gint nx, gint ny, gint px, gint py); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/playlist_list.c Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,966 @@ +/* Audacious - Cross-platform multimedia player + * Copyright (C) 2005-2006 Audacious development team. + * + * BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * A note about Pango and some funky spacey fonts: Weirdly baselined + * fonts, or fonts with weird ascents or descents _will_ display a + * little bit weird in the playlist widget, but the display engine + * won't make it look too bad, just a little deranged. I honestly + * don't think it's worth fixing (around...), it doesn't have to be + * perfectly fitting, just the general look has to be ok, which it + * IMHO is. + * + * A second note: The numbers aren't perfectly aligned, but in the + * end it looks better when using a single Pango layout for each + * number. + */ + +#include "widgetcore.h" + +#include <stdlib.h> +#include <string.h> + +#include "main.h" +#include "input.h" +#include "playback.h" +#include "playlist.h" +#include "ui_playlist.h" +#include "util.h" + +#include "debug.h" + +static PangoFontDescription *playlist_list_font = NULL; +static gint ascent, descent, width_delta_digit_one; +static gboolean has_slant; +static guint padding; + +/* FIXME: the following globals should not be needed. */ +static gint width_approx_letters; +static gint width_colon, width_colon_third; +static gint width_approx_digits, width_approx_digits_half; + +GdkPixmap *rootpix; + +void playlist_list_draw(Widget * w); + +/* Sort of stolen from XChat, but not really, as theres uses Xlib */ +static void +shade_gdkimage_generic (GdkVisual *visual, GdkImage *ximg, int bpl, int w, int h, int rm, int gm, int bm, int bg) +{ + int x, y; + int bgr = (256 - rm) * (bg & visual->red_mask); + int bgg = (256 - gm) * (bg & visual->green_mask); + int bgb = (256 - bm) * (bg & visual->blue_mask); + + for (x = 0; x < w; x++) + { + for (y = 0; y < h; y++) + { + unsigned long pixel = gdk_image_get_pixel (ximg, x, y); + int r, g, b; + + r = rm * (pixel & visual->red_mask) + bgr; + g = gm * (pixel & visual->green_mask) + bgg; + b = bm * (pixel & visual->blue_mask) + bgb; + + gdk_image_put_pixel (ximg, x, y, + ((r >> 8) & visual->red_mask) | + ((g >> 8) & visual->green_mask) | + ((b >> 8) & visual->blue_mask)); + } + } +} + +/* and this is definately mine... -nenolod */ +GdkPixmap * +shade_pixmap(GdkPixmap *in, gint x, gint y, gint x_offset, gint y_offset, gint w, gint h, GdkColor *shade_color) +{ + GdkImage *ximg; + GdkPixmap *p = gdk_pixmap_new(in, w, h, -1); + GdkGC *gc = gdk_gc_new(p); + + gdk_draw_pixmap(p, gc, in, x, y, 0, 0, w, h); + + gdk_error_trap_push(); + + ximg = gdk_drawable_copy_to_image(in, NULL, x, y, 0, 0, w, h); /* copy */ + + gdk_error_trap_pop(); + + if (GDK_IS_IMAGE(ximg)) + { + shade_gdkimage_generic(gdk_drawable_get_visual(GDK_WINDOW(playlistwin->window)), + ximg, ximg->bpl, w, h, 60, 60, 60, shade_color->pixel); + + gdk_draw_image(p, gc, ximg, 0, 0, x, y, w, h); + } + else { + cfg.playlist_transparent = FALSE; + } + + g_object_unref(gc); + + return p; +} + +#ifdef GDK_WINDOWING_X11 + +#include <gdk/gdkx.h> +#include <X11/Xlib.h> +#include <X11/Xatom.h> + +GdkDrawable *get_transparency_pixmap(void) +{ + GdkDrawable *root; + XID *pixmaps; + GdkAtom prop_type; + gint prop_size; + GdkPixmap *pixmap; + gboolean ret; + + root = gdk_get_default_root_window(); + + pixmap = NULL; + pixmaps = NULL; + + gdk_error_trap_push(); + + ret = gdk_property_get(root, gdk_atom_intern("_XROOTPMAP_ID", TRUE), + 0, 0, INT_MAX - 3, + FALSE, + &prop_type, NULL, &prop_size, + (guchar **) &pixmaps); + + gdk_error_trap_pop(); + + if ((ret == TRUE) && (prop_type == GDK_TARGET_PIXMAP) && (prop_size >= sizeof(XID)) && (pixmaps != NULL)) + { + pixmap = gdk_pixmap_foreign_new_for_display(gdk_drawable_get_display(root), + pixmaps[0]); + + if (pixmaps != NULL) + g_free(pixmaps); + } + + return GDK_DRAWABLE(pixmap); +} + +static GdkFilterReturn +root_event_cb (GdkXEvent *xev, GdkEventProperty *event, gpointer data) +{ + static Atom at = None; + XEvent *xevent = (XEvent *)xev; + + if (xevent->type == PropertyNotify) + { + if (at == None) + at = XInternAtom (xevent->xproperty.display, "_XROOTPMAP_ID", True); + + if (at == xevent->xproperty.atom) + { + rootpix = shade_pixmap(get_transparency_pixmap(), 0, 0, 0, 0, gdk_screen_width(), gdk_screen_height(), + skin_get_color(bmp_active_skin, SKIN_PLEDIT_NORMALBG)); + + if (cfg.playlist_transparent) + { + playlistwin_update_list(); + draw_playlist_window(TRUE); + } + } + } + + return GDK_FILTER_CONTINUE; +} + +#else + +GdkPixmap *get_transparency_pixmap(void) +{ + return NULL; +} + +#endif + +static gboolean +playlist_list_auto_drag_down_func(gpointer data) +{ + PlayList_List *pl = data; + + if (pl->pl_auto_drag_down) { + playlist_list_move_down(pl); + pl->pl_first++; + playlistwin_update_list(); + return TRUE; + } + return FALSE; +} + +static gboolean +playlist_list_auto_drag_up_func(gpointer data) +{ + PlayList_List *pl = data; + + if (pl->pl_auto_drag_up) { + playlist_list_move_up(pl); + pl->pl_first--; + playlistwin_update_list(); + return TRUE; + + } + return FALSE; +} + +void +playlist_list_move_up(PlayList_List * pl) +{ + GList *list; + + PLAYLIST_LOCK(); + if ((list = playlist_get()) == NULL) { + PLAYLIST_UNLOCK(); + return; + } + if (PLAYLIST_ENTRY(list->data)->selected) { + /* We are at the top */ + PLAYLIST_UNLOCK(); + return; + } + while (list) { + if (PLAYLIST_ENTRY(list->data)->selected) + glist_moveup(list); + list = g_list_next(list); + } + PLAYLIST_UNLOCK(); + if (pl->pl_prev_selected != -1) + pl->pl_prev_selected--; + if (pl->pl_prev_min != -1) + pl->pl_prev_min--; + if (pl->pl_prev_max != -1) + pl->pl_prev_max--; +} + +void +playlist_list_move_down(PlayList_List * pl) +{ + GList *list; + + PLAYLIST_LOCK(); + + if (!(list = g_list_last(playlist_get()))) { + PLAYLIST_UNLOCK(); + return; + } + + if (PLAYLIST_ENTRY(list->data)->selected) { + /* We are at the bottom */ + PLAYLIST_UNLOCK(); + return; + } + + while (list) { + if (PLAYLIST_ENTRY(list->data)->selected) + glist_movedown(list); + list = g_list_previous(list); + } + + PLAYLIST_UNLOCK(); + + if (pl->pl_prev_selected != -1) + pl->pl_prev_selected++; + if (pl->pl_prev_min != -1) + pl->pl_prev_min++; + if (pl->pl_prev_max != -1) + pl->pl_prev_max++; +} + +static void +playlist_list_button_press_cb(GtkWidget * widget, + GdkEventButton * event, + PlayList_List * pl) +{ + gint nr, y; + + if (event->button == 1 && pl->pl_fheight && + widget_contains(&pl->pl_widget, event->x, event->y)) { + + y = event->y - pl->pl_widget.y; + nr = (y / pl->pl_fheight) + pl->pl_first; + + if (nr >= playlist_get_length()) + nr = playlist_get_length() - 1; + + if (!(event->state & GDK_CONTROL_MASK)) + playlist_select_all(FALSE); + + if (event->state & GDK_SHIFT_MASK && pl->pl_prev_selected != -1) { + playlist_select_range(pl->pl_prev_selected, nr, TRUE); + pl->pl_prev_min = pl->pl_prev_selected; + pl->pl_prev_max = nr; + pl->pl_drag_pos = nr - pl->pl_first; + } + else { + if (playlist_select_invert(nr)) { + if (event->state & GDK_CONTROL_MASK) { + if (pl->pl_prev_min == -1) { + pl->pl_prev_min = pl->pl_prev_selected; + pl->pl_prev_max = pl->pl_prev_selected; + } + if (nr < pl->pl_prev_min) + pl->pl_prev_min = nr; + else if (nr > pl->pl_prev_max) + pl->pl_prev_max = nr; + } + else + pl->pl_prev_min = -1; + pl->pl_prev_selected = nr; + pl->pl_drag_pos = nr - pl->pl_first; + } + } + if (event->type == GDK_2BUTTON_PRESS) { + /* + * Ungrab the pointer to prevent us from + * hanging on to it during the sometimes slow + * bmp_playback_initiate(). + */ + gdk_pointer_ungrab(GDK_CURRENT_TIME); + gdk_flush(); + playlist_set_position(nr); + if (!bmp_playback_get_playing()) + bmp_playback_initiate(); + } + + pl->pl_dragging = TRUE; + playlistwin_update_list(); + } +} + +gint +playlist_list_get_playlist_position(PlayList_List * pl, + gint x, + gint y) +{ + gint iy, length; + gint ret; + + if (!widget_contains(WIDGET(pl), x, y) || !pl->pl_fheight) + return -1; + + if ((length = playlist_get_length()) == 0) + return -1; + iy = y - pl->pl_widget.y; + + ret = (iy / pl->pl_fheight) + pl->pl_first; + + if(ret > length-1) + ret = -1; + + return ret; +} + +static void +playlist_list_motion_cb(GtkWidget * widget, + GdkEventMotion * event, + PlayList_List * pl) +{ + gint nr, y, off, i; + + if (pl->pl_dragging) { + y = event->y - pl->pl_widget.y; + nr = (y / pl->pl_fheight); + if (nr < 0) { + nr = 0; + if (!pl->pl_auto_drag_up) { + pl->pl_auto_drag_up = TRUE; + pl->pl_auto_drag_up_tag = + gtk_timeout_add(100, playlist_list_auto_drag_up_func, pl); + } + } + else if (pl->pl_auto_drag_up) + pl->pl_auto_drag_up = FALSE; + + if (nr >= pl->pl_num_visible) { + nr = pl->pl_num_visible - 1; + if (!pl->pl_auto_drag_down) { + pl->pl_auto_drag_down = TRUE; + pl->pl_auto_drag_down_tag = + gtk_timeout_add(100, playlist_list_auto_drag_down_func, + pl); + } + } + else if (pl->pl_auto_drag_down) + pl->pl_auto_drag_down = FALSE; + + off = nr - pl->pl_drag_pos; + if (off) { + for (i = 0; i < abs(off); i++) { + if (off < 0) + playlist_list_move_up(pl); + else + playlist_list_move_down(pl); + + } + playlistwin_update_list(); + } + pl->pl_drag_pos = nr; + } +} + +static void +playlist_list_button_release_cb(GtkWidget * widget, + GdkEventButton * event, + PlayList_List * pl) +{ + pl->pl_dragging = FALSE; + pl->pl_auto_drag_down = FALSE; + pl->pl_auto_drag_up = FALSE; +} + +static void +playlist_list_draw_string(PlayList_List * pl, + PangoFontDescription * font, + gint line, + gint width, + const gchar * text, + guint ppos) +{ + guint plist_length_int; + + PangoLayout *layout; + + REQUIRE_STATIC_LOCK(playlist); + + if (cfg.show_numbers_in_pl) { + gchar *pos_string = g_strdup_printf(cfg.show_separator_in_pl == TRUE ? "%d" : "%d.", ppos); + plist_length_int = + gint_count_digits(playlist_get_length_nolock()) + !cfg.show_separator_in_pl + 1; /* cf.show_separator_in_pl will be 0 if false */ + + padding = plist_length_int; + padding = ((padding + 1) * width_approx_digits); + + layout = gtk_widget_create_pango_layout(playlistwin, pos_string); + pango_layout_set_font_description(layout, playlist_list_font); + pango_layout_set_width(layout, plist_length_int * 100); + + pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); + gdk_draw_layout(pl->pl_widget.parent, pl->pl_widget.gc, + pl->pl_widget.x + + (width_approx_digits * + (-1 + plist_length_int - strlen(pos_string))) + + (width_approx_digits / 4), + pl->pl_widget.y + (line - 1) * pl->pl_fheight + + ascent + abs(descent), layout); + g_free(pos_string); + g_object_unref(layout); + + if (!cfg.show_separator_in_pl) + padding -= (width_approx_digits * 1.5); + } + else { + padding = 3; + } + + width -= padding; + + layout = gtk_widget_create_pango_layout(playlistwin, text); + + pango_layout_set_font_description(layout, playlist_list_font); + pango_layout_set_width(layout, width * PANGO_SCALE); + pango_layout_set_single_paragraph_mode(layout, TRUE); + pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); + gdk_draw_layout(pl->pl_widget.parent, pl->pl_widget.gc, + pl->pl_widget.x + padding + (width_approx_letters / 4), + pl->pl_widget.y + (line - 1) * pl->pl_fheight + + ascent + abs(descent), layout); + + g_object_unref(layout); +} + +void +playlist_list_draw(Widget * w) +{ + PlayList_List *pl = PLAYLIST_LIST(w); + GList *list; + GdkGC *gc; + GdkPixmap *obj; + PangoLayout *layout; + gchar *title; + gint width, height; + gint i, max_first; + guint padding, padding_dwidth, padding_plength; + guint max_time_len = 0; + gfloat queue_tailpadding = 0; + gint tpadding; + gsize tpadding_dwidth = 0; + gint x, y; + guint tail_width; + guint tail_len; + + gchar tail[100]; + gchar queuepos[255]; + gchar length[40]; + + gchar **frags; + gchar *frag0; + + gint plw_w, plw_h; + + GdkRectangle *playlist_rect; + + gc = pl->pl_widget.gc; + + width = pl->pl_widget.width; + height = pl->pl_widget.height; + + obj = pl->pl_widget.parent; + + gtk_window_get_size(GTK_WINDOW(playlistwin), &plw_w, &plw_h); + + playlist_rect = g_new0(GdkRectangle, 1); + + playlist_rect->x = 0; + playlist_rect->y = 0; + playlist_rect->width = plw_w - 17; + playlist_rect->height = plw_h - 36; + + gdk_gc_set_clip_origin(gc, 31, 58); + gdk_gc_set_clip_rectangle(gc, playlist_rect); + + if (cfg.playlist_transparent == FALSE) + { + gdk_gc_set_foreground(gc, + skin_get_color(bmp_active_skin, + SKIN_PLEDIT_NORMALBG)); + gdk_draw_rectangle(obj, gc, TRUE, pl->pl_widget.x, pl->pl_widget.y, + width, height); + } + else + { + if (!rootpix) + rootpix = shade_pixmap(get_transparency_pixmap(), 0, 0, 0, 0, gdk_screen_width(), gdk_screen_height(), + skin_get_color(bmp_active_skin, SKIN_PLEDIT_NORMALBG)); + gdk_draw_pixmap(obj, gc, rootpix, cfg.playlist_x + pl->pl_widget.x, + cfg.playlist_y + pl->pl_widget.y, pl->pl_widget.x, pl->pl_widget.y, + width, height); + } + + if (!playlist_list_font) { + g_critical("Couldn't open playlist font"); + return; + } + + pl->pl_fheight = (ascent + abs(descent)); + pl->pl_num_visible = height / pl->pl_fheight; + + max_first = playlist_get_length() - pl->pl_num_visible; + max_first = MAX(max_first, 0); + + pl->pl_first = CLAMP(pl->pl_first, 0, max_first); + + PLAYLIST_LOCK(); + list = playlist_get(); + list = g_list_nth(list, pl->pl_first); + + /* It sucks having to run the iteration twice but this is the only + way you can reliably get the maximum width so we can get our + playlist nice and aligned... -- plasmaroo */ + + for (i = pl->pl_first; + list && i < pl->pl_first + pl->pl_num_visible; + list = g_list_next(list), i++) { + PlaylistEntry *entry = list->data; + + if (entry->length != -1) + { + g_snprintf(length, sizeof(length), "%d:%-2.2d", + entry->length / 60000, (entry->length / 1000) % 60); + tpadding_dwidth = MAX(tpadding_dwidth, strlen(length)); + } + } + + /* Reset */ + list = playlist_get(); + list = g_list_nth(list, pl->pl_first); + + for (i = pl->pl_first; + list && i < pl->pl_first + pl->pl_num_visible; + list = g_list_next(list), i++) { + gint pos; + PlaylistEntry *entry = list->data; + + if (entry->selected) { + gdk_gc_set_foreground(gc, + skin_get_color(bmp_active_skin, + SKIN_PLEDIT_SELECTEDBG)); + gdk_draw_rectangle(obj, gc, TRUE, pl->pl_widget.x, + pl->pl_widget.y + + ((i - pl->pl_first) * pl->pl_fheight), + width, pl->pl_fheight); + } + + /* FIXME: entry->title should NEVER be NULL, and there should + NEVER be a need to do a UTF-8 conversion. Playlist title + strings should be kept properly. */ + + if (!entry->title) { + gchar *basename = g_path_get_basename(entry->filename); + title = filename_to_utf8(basename); + g_free(basename); + } + else + title = str_to_utf8(entry->title); + + pos = playlist_get_queue_position(entry); + + tail[0] = 0; + queuepos[0] = 0; + length[0] = 0; + + if (pos != -1) + g_snprintf(queuepos, sizeof(queuepos), "%d", pos + 1); + + if (entry->length != -1) + { + g_snprintf(length, sizeof(length), "%d:%-2.2d", + entry->length / 60000, (entry->length / 1000) % 60); + } + + strncat(tail, length, sizeof(tail)); + tail_len = strlen(tail); + + max_time_len = MAX(max_time_len, tail_len); + + if (pos != -1 && tpadding_dwidth <= 0) + tail_width = width - (width_approx_digits * (strlen(queuepos) + 2.25)); + else if (pos != -1) + tail_width = width - (width_approx_digits * (tpadding_dwidth + strlen(queuepos) + 4)); + else if (tpadding_dwidth > 0) + tail_width = width - (width_approx_digits * (tpadding_dwidth + 2.5)); + else + tail_width = width; + + if (i == playlist_get_position_nolock()) + gdk_gc_set_foreground(gc, + skin_get_color(bmp_active_skin, + SKIN_PLEDIT_CURRENT)); + else + gdk_gc_set_foreground(gc, + skin_get_color(bmp_active_skin, + SKIN_PLEDIT_NORMAL)); + playlist_list_draw_string(pl, playlist_list_font, + i - pl->pl_first, tail_width, title, + i + 1); + + x = pl->pl_widget.x + width - width_approx_digits * 2; + y = pl->pl_widget.y + ((i - pl->pl_first) - + 1) * pl->pl_fheight + ascent; + + frags = NULL; + frag0 = NULL; + + if ((strlen(tail) > 0) && (tail != NULL)) { + frags = g_strsplit(tail, ":", 0); + frag0 = g_strconcat(frags[0], ":", NULL); + + layout = gtk_widget_create_pango_layout(playlistwin, frags[1]); + pango_layout_set_font_description(layout, playlist_list_font); + pango_layout_set_width(layout, tail_len * 100); + pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); + gdk_draw_layout(obj, gc, x - (0.5 * width_approx_digits), + y + abs(descent), layout); + g_object_unref(layout); + + layout = gtk_widget_create_pango_layout(playlistwin, frag0); + pango_layout_set_font_description(layout, playlist_list_font); + pango_layout_set_width(layout, tail_len * 100); + pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT); + gdk_draw_layout(obj, gc, x - (0.75 * width_approx_digits), + y + abs(descent), layout); + g_object_unref(layout); + + g_free(frag0); + g_strfreev(frags); + } + + if (pos != -1) { + + /* DON'T remove the commented code yet please -- Milosz */ + + if (tpadding_dwidth > 0) + queue_tailpadding = tpadding_dwidth + 1; + else + queue_tailpadding = -0.75; + + gdk_draw_rectangle(obj, gc, FALSE, + x - + (((queue_tailpadding + + strlen(queuepos)) * + width_approx_digits) + + (width_approx_digits / 4)), + y + abs(descent), + (strlen(queuepos)) * + width_approx_digits + + (width_approx_digits / 2), + pl->pl_fheight - 2); + + layout = + gtk_widget_create_pango_layout(playlistwin, queuepos); + pango_layout_set_font_description(layout, playlist_list_font); + pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); + + gdk_draw_layout(obj, gc, + x - + ((queue_tailpadding + + strlen(queuepos)) * width_approx_digits) + + (width_approx_digits / 4), + y + abs(descent), layout); + g_object_unref(layout); + } + + g_free(title); + } + + + /* + * Drop target hovering over the playlist, so draw some hint where the + * drop will occur. + * + * This is (currently? unfixably?) broken when dragging files from Qt/KDE apps, + * probably due to DnD signaling problems (actually i have no clue). + * + */ + + if (pl->pl_drag_motion) { + guint pos, plength, lpadding; + gint x, y, plx, ply; + + if (cfg.show_numbers_in_pl) { + lpadding = gint_count_digits(playlist_get_length_nolock()) + 1; + lpadding = ((lpadding + 1) * width_approx_digits); + } + else { + lpadding = 3; + }; + + /* We already hold the mutex and have the playlist locked, so call + the non-locking function. */ + plength = playlist_get_length_nolock(); + + x = pl->drag_motion_x; + y = pl->drag_motion_y; + + plx = pl->pl_widget.x; + ply = pl->pl_widget.y; + + if ((x > pl->pl_widget.x) && !(x > pl->pl_widget.width)) { + + if ((y > pl->pl_widget.y) + && !(y > (pl->pl_widget.height + ply))) { + + pos = ((y - ((Widget *) pl)->y) / pl->pl_fheight) + + pl->pl_first; + + if (pos > (plength)) { + pos = plength; + } + + gdk_gc_set_foreground(gc, + skin_get_color(bmp_active_skin, + SKIN_PLEDIT_CURRENT)); + + gdk_draw_line(obj, gc, pl->pl_widget.x, + pl->pl_widget.y + ((pos - pl->pl_first) * pl->pl_fheight), + pl->pl_widget.width + pl->pl_widget.x - 1, + pl->pl_widget.y + + ((pos - pl->pl_first) * pl->pl_fheight)); + } + + } + + /* When dropping on the borders of the playlist, outside the text area, + * files get appended at the end of the list. Show that too. + */ + + if ((y < ply) || (y > pl->pl_widget.height + ply)) { + if ((y >= 0) || (y <= (pl->pl_widget.height + ply))) { + pos = plength; + gdk_gc_set_foreground(gc, + skin_get_color(bmp_active_skin, + SKIN_PLEDIT_CURRENT)); + + gdk_draw_line(obj, gc, pl->pl_widget.x, + pl->pl_widget.y + + ((pos - pl->pl_first) * pl->pl_fheight), + pl->pl_widget.width + pl->pl_widget.x - 1, + pl->pl_widget.y + + ((pos - pl->pl_first) * pl->pl_fheight)); + + } + } + } + + gdk_gc_set_foreground(gc, + skin_get_color(bmp_active_skin, + SKIN_PLEDIT_NORMAL)); + + if (cfg.show_numbers_in_pl) { + + padding_plength = playlist_get_length_nolock(); + + if (padding_plength == 0) { + padding_dwidth = 0; + } + else { + padding_dwidth = gint_count_digits(playlist_get_length_nolock()); + } + + padding = + (padding_dwidth * + width_approx_digits) + width_approx_digits; + + + /* For italic or oblique fonts we add another half of the + * approximate width */ + if (has_slant) + padding += width_approx_digits_half; + + if (cfg.show_separator_in_pl) { + gdk_draw_line(obj, gc, + pl->pl_widget.x + padding, + pl->pl_widget.y, + pl->pl_widget.x + padding, + pl->pl_widget.y + pl->pl_widget.height - 1); + } + } + + if (tpadding_dwidth != 0) + { + tpadding = (tpadding_dwidth * width_approx_digits) + (width_approx_digits * 1.5); + + if (has_slant) + tpadding += width_approx_digits_half; + + if (cfg.show_separator_in_pl) { + gdk_draw_line(obj, gc, + pl->pl_widget.x + pl->pl_widget.width - tpadding, + pl->pl_widget.y, + pl->pl_widget.x + pl->pl_widget.width - tpadding, + pl->pl_widget.y + pl->pl_widget.height - 1); + } + } + + gdk_gc_set_clip_origin(gc, 0, 0); + gdk_gc_set_clip_rectangle(gc, NULL); + + PLAYLIST_UNLOCK(); + + gdk_flush(); + + g_free(playlist_rect); +} + + +PlayList_List * +create_playlist_list(GList ** wlist, + GdkPixmap * parent, + GdkGC * gc, + gint x, gint y, + gint w, gint h) +{ + PlayList_List *pl; + + pl = g_new0(PlayList_List, 1); + widget_init(&pl->pl_widget, parent, gc, x, y, w, h, TRUE); + + pl->pl_widget.button_press_cb = + (WidgetButtonPressFunc) playlist_list_button_press_cb; + pl->pl_widget.button_release_cb = + (WidgetButtonReleaseFunc) playlist_list_button_release_cb; + pl->pl_widget.motion_cb = (WidgetMotionFunc) playlist_list_motion_cb; + pl->pl_widget.draw = playlist_list_draw; + + pl->pl_prev_selected = -1; + pl->pl_prev_min = -1; + pl->pl_prev_max = -1; + + widget_list_add(wlist, WIDGET(pl)); + +#ifdef GDK_WINDOWING_X11 + gdk_window_set_events (gdk_get_default_root_window(), GDK_PROPERTY_CHANGE_MASK); + gdk_window_add_filter (gdk_get_default_root_window(), (GdkFilterFunc)root_event_cb, pl); +#endif + + return pl; +} + +void +playlist_list_set_font(const gchar * font) +{ + + /* Welcome to bad hack central 2k3 */ + + gchar *font_lower; + gint width_temp; + gint width_temp_0; + + playlist_list_font = pango_font_description_from_string(font); + + text_get_extents(font, + "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz ", + &width_approx_letters, NULL, &ascent, &descent); + + width_approx_letters = (width_approx_letters / 53); + + /* Experimental: We don't weigh the 1 into total because it's width is almost always + * very different from the rest + */ + text_get_extents(font, "023456789", &width_approx_digits, NULL, NULL, + NULL); + width_approx_digits = (width_approx_digits / 9); + + /* Precache some often used calculations */ + width_approx_digits_half = width_approx_digits / 2; + + /* FIXME: We assume that any other number is broader than the "1" */ + text_get_extents(font, "1", &width_temp, NULL, NULL, NULL); + text_get_extents(font, "2", &width_temp_0, NULL, NULL, NULL); + + if (abs(width_temp_0 - width_temp) < 2) { + width_delta_digit_one = 0; + } + else { + width_delta_digit_one = ((width_temp_0 - width_temp) / 2) + 2; + } + + text_get_extents(font, ":", &width_colon, NULL, NULL, NULL); + width_colon_third = width_colon / 4; + + font_lower = g_utf8_strdown(font, strlen(font)); + /* This doesn't take any i18n into account, but i think there is none with TTF fonts + * FIXME: This can probably be retrieved trough Pango too + */ + has_slant = g_strstr_len(font_lower, strlen(font_lower), "oblique") + || g_strstr_len(font_lower, strlen(font_lower), "italic"); + + g_free(font_lower); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/playlist_list.h Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,60 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _WIDGETCORE_H_ +#error Please do not include me directly! Use widgetcore.h instead! +#endif + +#ifndef PLAYLIST_LIST_H +#define PLAYLIST_LIST_H + +#include <glib.h> +#include <gtk/gtk.h> +#include <gdk/gdk.h> + +#include "widget.h" + +#define PLAYLIST_LIST(x) ((PlayList_List *)(x)) +struct _PlayList_List { + Widget pl_widget; + gint pl_first, pl_fheight, pl_prev_selected, pl_prev_min, pl_prev_max; + gint pl_num_visible, pl_drag_pos; + gboolean pl_dragging, pl_auto_drag_down, pl_auto_drag_up; + gint pl_auto_drag_up_tag, pl_auto_drag_down_tag; + gboolean pl_drag_motion; + gint drag_motion_x, drag_motion_y; + gboolean pl_tooltips; +}; + +typedef struct _PlayList_List PlayList_List; + +PlayList_List *create_playlist_list(GList ** wlist, GdkPixmap * parent, + GdkGC * gc, gint x, gint y, gint w, + gint h); +void playlist_list_move_up(PlayList_List * pl); +void playlist_list_move_down(PlayList_List * pl); +int playlist_list_get_playlist_position(PlayList_List * pl, gint x, gint y); +void playlist_list_set_font(const gchar * font); +GdkPixmap *rootpix; +GdkPixmap *shade_pixmap(GdkDrawable *in, gint x, gint y, gint x_offset, gint y_offset, gint w, gint h, GdkColor *shade_color); +GdkDrawable *get_transparency_pixmap(void); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/playlist_slider.c Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,168 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "widgetcore.h" + +#include <glib.h> + +#include "playlist.h" +#include "ui_playlist.h" +#include "skin.h" +#include "widget.h" + +void +playlistslider_draw(Widget * w) +{ + PlaylistSlider *ps = (PlaylistSlider *) w; + GdkPixmap *obj; + gint y, skinx; + + g_return_if_fail(ps != NULL); + g_return_if_fail(ps->ps_list != NULL); + + if (playlist_get_length() > ps->ps_list->pl_num_visible) + y = (ps->ps_list->pl_first * (ps->ps_widget.height - 19)) / + (playlist_get_length() - ps->ps_list->pl_num_visible); + else + y = 0; + + obj = ps->ps_widget.parent; + + if (ps->ps_back_image) { + if (skin_get_id() != ps->ps_skin_id) + ps->ps_skin_id = skin_get_id(); + else if (ps->ps_widget.height == ps->ps_prev_height) + gdk_draw_image(obj, ps->ps_widget.gc, + ps->ps_back_image, 0, 0, + ps->ps_widget.x, + ps->ps_widget.y + ps->ps_prev_y, 8, 18); + gdk_image_destroy(ps->ps_back_image); + } + + ps->ps_prev_y = y; + ps->ps_prev_height = ps->ps_widget.height; + ps->ps_back_image = gdk_drawable_get_image(obj, ps->ps_widget.x, + ps->ps_widget.y + y, 8, 18); + if (ps->ps_is_draging) + skinx = 61; + else + skinx = 52; + + skin_draw_pixmap(bmp_active_skin, obj, ps->ps_widget.gc, SKIN_PLEDIT, + skinx, 53, ps->ps_widget.x, ps->ps_widget.y + y, 8, 18); +} + +static void +playlistslider_set_pos(PlaylistSlider * ps, gint y) +{ + gint pos; + + y = CLAMP(y, 0, ps->ps_widget.height - 19); + + pos = (y * (playlist_get_length() - ps->ps_list->pl_num_visible)) / + (ps->ps_widget.height - 19); + playlistwin_set_toprow(pos); +} + + +void +playlistslider_button_press_cb(GtkWidget * widget, + GdkEventButton * event, PlaylistSlider * ps) +{ + gint y = event->y - ps->ps_widget.y; + + if (!widget_contains(&ps->ps_widget, event->x, event->y)) + return; + + if (event->button != 1 && event->button != 2) + return; + + if ((y >= ps->ps_prev_y && y < ps->ps_prev_y + 18)) { + ps->ps_is_draging |= event->button; + ps->ps_drag_y = y - ps->ps_prev_y; + widget_draw(WIDGET(ps)); + } + else if (event->button == 2) { + playlistslider_set_pos(ps, y); + ps->ps_is_draging |= event->button; + ps->ps_drag_y = 0; + widget_draw(WIDGET(ps)); + } + else { + gint n = ps->ps_list->pl_num_visible / 2; + if (y < ps->ps_prev_y) + n *= -1; + playlistwin_scroll(n); + } +} + +void +playlistslider_button_release_cb(GtkWidget * widget, + GdkEventButton * event, + PlaylistSlider * ps) +{ + if (ps->ps_is_draging) { + ps->ps_is_draging &= ~event->button; + widget_draw(WIDGET(ps)); + } +} + +void +playlistslider_motion_cb(GtkWidget * widget, GdkEventMotion * event, + PlaylistSlider * ps) +{ + gint y; + + if (!ps->ps_is_draging) + return; + + y = event->y - ps->ps_widget.y - ps->ps_drag_y; + playlistslider_set_pos(ps, y); +} + +PlaylistSlider * +create_playlistslider(GList ** wlist, GdkPixmap * parent, + GdkGC * gc, gint x, gint y, gint h, + PlayList_List * list) +{ + PlaylistSlider *ps; + + ps = g_new0(PlaylistSlider, 1); + widget_init(&ps->ps_widget, parent, gc, x, y, 8, h, 1); + + ps->ps_widget.button_press_cb = + (void (*)(GtkWidget *, GdkEventButton *, gpointer)) + playlistslider_button_press_cb; + + ps->ps_widget.button_release_cb = + (void (*)(GtkWidget *, GdkEventButton *, gpointer)) + playlistslider_button_release_cb; + + ps->ps_widget.motion_cb = + (void (*)(GtkWidget *, GdkEventMotion *, gpointer)) + playlistslider_motion_cb; + + ps->ps_widget.draw = playlistslider_draw; + ps->ps_list = list; + + widget_list_add(wlist, WIDGET(ps)); + return ps; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/playlist_slider.h Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,51 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _WIDGETCORE_H_ +#error Please do not include me directly! Use widgetcore.h instead! +#endif + +#ifndef PLAYLIST_SLIDER_H +#define PLAYLIST_SLIDER_H + +#include <glib.h> +#include <gdk/gdk.h> + +#include "playlist_list.h" +#include "widget.h" + +#define PLAYLIST_SLIDER(x) ((PlayerlistSlider *)(x)) +struct _PlaylistSlider { + Widget ps_widget; + PlayList_List *ps_list; + gboolean ps_is_draging; + gint ps_drag_y, ps_prev_y, ps_prev_height; + GdkImage *ps_back_image; + gint ps_skin_id; +}; + +typedef struct _PlaylistSlider PlaylistSlider; + +PlaylistSlider *create_playlistslider(GList ** wlist, GdkPixmap * parent, + GdkGC * gc, gint x, gint y, gint h, + PlayList_List * list); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/playstatus.c Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,102 @@ +/* XMMS - Cross-platform multimedia player + * Copyright (C) 1998-2000 Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "widgetcore.h" + +#include <glib.h> +#include <gdk/gdk.h> + +#include "skin.h" +#include "widget.h" + +void +playstatus_draw(Widget * w) +{ + PlayStatus *ps = PLAY_STATUS(w); + GdkPixmap *obj; + + if (!w) + return; + + obj = ps->ps_widget.parent; + if (ps->ps_status == STATUS_STOP && ps->ps_status_buffering == TRUE) + ps->ps_status_buffering = FALSE; + if (ps->ps_status == STATUS_PLAY && ps->ps_status_buffering == TRUE) + skin_draw_pixmap(bmp_active_skin, obj, ps->ps_widget.gc, + SKIN_PLAYPAUSE, 39, 0, ps->ps_widget.x, + ps->ps_widget.y, 3, 9); + else if (ps->ps_status == STATUS_PLAY) + skin_draw_pixmap(bmp_active_skin, obj, ps->ps_widget.gc, + SKIN_PLAYPAUSE, 36, 0, ps->ps_widget.x, + ps->ps_widget.y, 3, 9); + else + skin_draw_pixmap(bmp_active_skin, obj, ps->ps_widget.gc, + SKIN_PLAYPAUSE, 27, 0, ps->ps_widget.x, + ps->ps_widget.y, 2, 9); + switch (ps->ps_status) { + case STATUS_STOP: + skin_draw_pixmap(bmp_active_skin, obj, ps->ps_widget.gc, + SKIN_PLAYPAUSE, 18, 0, + ps->ps_widget.x + 2, ps->ps_widget.y, 9, 9); + break; + case STATUS_PAUSE: + skin_draw_pixmap(bmp_active_skin, obj, ps->ps_widget.gc, + SKIN_PLAYPAUSE, 9, 0, + ps->ps_widget.x + 2, ps->ps_widget.y, 9, 9); + break; + case STATUS_PLAY: + skin_draw_pixmap(bmp_active_skin, obj, ps->ps_widget.gc, + SKIN_PLAYPAUSE, 1, 0, + ps->ps_widget.x + 3, ps->ps_widget.y, 8, 9); + break; + } +} + +void +playstatus_set_status(PlayStatus * ps, PStatus status) +{ + if (!ps) + return; + + ps->ps_status = status; + widget_draw(WIDGET(ps)); +} + +void +playstatus_set_status_buffering(PlayStatus * ps, gboolean status) +{ + if (!ps) + return; + + ps->ps_status_buffering = status; + widget_draw(WIDGET(ps)); +} + +PlayStatus * +create_playstatus(GList ** wlist, GdkPixmap * parent, + GdkGC * gc, gint x, gint y) +{ + PlayStatus *ps; + + ps = g_new0(PlayStatus, 1); + widget_init(&ps->ps_widget, parent, gc, x, y, 11, 9, TRUE); + ps->ps_widget.draw = playstatus_draw; + + widget_list_add(wlist, WIDGET(ps)); + return ps; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/playstatus.h Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,46 @@ +/* XMMS - Cross-platform multimedia player + * Copyright (C) 1998-2000 Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _WIDGETCORE_H_ +#error Please do not include me directly! Use widgetcore.h instead! +#endif + +#ifndef PLAYSTATUS_H +#define PLAYSTATUS_H + +#include "widget.h" + +typedef enum { + STATUS_STOP, STATUS_PAUSE, STATUS_PLAY +} PStatus; + +#define PLAY_STATUS(x) ((PlayStatus *)(x)) +struct _PlayStatus { + Widget ps_widget; + PStatus ps_status; + gboolean ps_status_buffering; +}; + +typedef struct _PlayStatus PlayStatus; + +void playstatus_set_status(PlayStatus * ps, PStatus status); +void playstatus_set_status_buffering(PlayStatus * ps, gboolean status); +PlayStatus *create_playstatus(GList ** wlist, GdkPixmap * parent, + GdkGC * gc, gint x, gint y); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/sbutton.c Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,99 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "widgetcore.h" + +#include <glib.h> +#include <gtk/gtk.h> +#include <gdk/gdk.h> + +void +sbutton_button_press_cb(GtkWidget * widget, + GdkEventButton * event, + SButton * button) +{ + if (event->button != 1) + return; + + if (widget_contains(&button->sb_widget, event->x, event->y)) { + button->sb_pressed = 1; + button->sb_inside = 1; + } +} + +void +sbutton_button_release_cb(GtkWidget * widget, GdkEventButton * event, + SButton * button) +{ + if (event->button != 1) + return; + if (button->sb_inside && button->sb_pressed) { + button->sb_inside = 0; + if (button->sb_push_cb) + button->sb_push_cb(); + } + if (button->sb_pressed) + button->sb_pressed = 0; +} + +void +sbutton_motion_cb(GtkWidget * widget, GdkEventMotion * event, + SButton * button) +{ + int inside; + + if (!button->sb_pressed) + return; + + inside = widget_contains(&button->sb_widget, event->x, event->y); + + if (inside != button->sb_inside) + button->sb_inside = inside; +} + +SButton * +create_sbutton(GList ** wlist, GdkPixmap * parent, GdkGC * gc, + gint x, gint y, gint w, gint h, void (*cb) (void)) +{ + SButton *b; + + b = g_new0(SButton, 1); + widget_init(&b->sb_widget, parent, gc, x, y, w, h, 1); + b->sb_widget.button_press_cb = + (void (*)(GtkWidget *, GdkEventButton *, gpointer)) + sbutton_button_press_cb; + b->sb_widget.button_release_cb = + (void (*)(GtkWidget *, GdkEventButton *, gpointer)) + sbutton_button_release_cb; + b->sb_widget.motion_cb = + (void (*)(GtkWidget *, GdkEventMotion *, gpointer)) + sbutton_motion_cb; + b->sb_push_cb = cb; + + widget_list_add(wlist, WIDGET(b)); + return b; +} + +void +free_sbutton(SButton * b) +{ + g_free(b); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/sbutton.h Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,47 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _WIDGETCORE_H_ +#error Please do not include me directly! Use widgetcore.h instead! +#endif + +#ifndef SBUTTON_H +#define SBUTTON_H + +#include <glib.h> +#include <gdk/gdk.h> + +#include "widget.h" + +#define SBUTTON(x) ((SButton *)(x)) +struct _SButton { + Widget sb_widget; + gint sb_pressed, sb_inside; + void (*sb_push_cb) (void); +}; + +typedef struct _SButton SButton; + +SButton *create_sbutton(GList ** wlist, GdkPixmap * parent, GdkGC * gc, + gint x, gint y, gint w, gint h, void (*cb) (void)); +void free_sbutton(SButton * b); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/skin.c Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,1254 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +/* TODO: enforce default sizes! */ + +#include <glib.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include "widgetcore.h" + +#include "equalizer.h" +#include "main.h" +#include "ui_playlist.h" +#include "skinwin.h" +#include "util.h" + +#include "debug.h" + +#include "platform/smartinclude.h" + +#define EXTENSION_TARGETS 7 + +static gchar *ext_targets[EXTENSION_TARGETS] = { "bmp", "xpm", "png", "svg", + "gif", "jpg", "jpeg" }; + +struct _SkinPixmapIdMapping { + SkinPixmapId id; + const gchar *name; + const gchar *alt_name; + gint width, height; +}; + +struct _SkinMaskInfo { + gint width, height; + gchar *inistr; +}; + +typedef struct _SkinPixmapIdMapping SkinPixmapIdMapping; +typedef struct _SkinMaskInfo SkinMaskInfo; + + +Skin *bmp_active_skin = NULL; + +static gint skin_current_num; + +static SkinMaskInfo skin_mask_info[] = { + {275, 116, "Normal"}, + {275, 16, "WindowShade"}, + {275, 116, "Equalizer"}, + {275, 16, "EqualizerWS"} +}; + +static SkinPixmapIdMapping skin_pixmap_id_map[] = { + {SKIN_MAIN, "main", NULL, 0, 0}, + {SKIN_CBUTTONS, "cbuttons", NULL, 0, 0}, + {SKIN_SHUFREP, "shufrep", NULL, 0, 0}, + {SKIN_TEXT, "text", NULL, 0, 0}, + {SKIN_TITLEBAR, "titlebar", NULL, 0, 0}, + {SKIN_VOLUME, "volume", NULL, 0, 0}, + {SKIN_BALANCE, "balance", "volume", 0, 0}, + {SKIN_MONOSTEREO, "monoster", NULL, 0, 0}, + {SKIN_PLAYPAUSE, "playpaus", NULL, 0, 0}, + {SKIN_NUMBERS, "nums_ex", "numbers", 0, 0}, + {SKIN_POSBAR, "posbar", NULL, 0, 0}, + {SKIN_EQMAIN, "eqmain", NULL, 0, 0}, + {SKIN_PLEDIT, "pledit", NULL, 0, 0}, + {SKIN_EQ_EX, "eq_ex", NULL, 0, 0} +}; + +static guint skin_pixmap_id_map_size = G_N_ELEMENTS(skin_pixmap_id_map); + +static const guchar skin_default_viscolor[24][3] = { + {9, 34, 53}, + {10, 18, 26}, + {0, 54, 108}, + {0, 58, 116}, + {0, 62, 124}, + {0, 66, 132}, + {0, 70, 140}, + {0, 74, 148}, + {0, 78, 156}, + {0, 82, 164}, + {0, 86, 172}, + {0, 92, 184}, + {0, 98, 196}, + {0, 104, 208}, + {0, 110, 220}, + {0, 116, 232}, + {0, 122, 244}, + {0, 128, 255}, + {0, 128, 255}, + {0, 104, 208}, + {0, 80, 160}, + {0, 56, 112}, + {0, 32, 64}, + {200, 200, 200} +}; + +static GdkBitmap * +skin_create_transparent_mask(const gchar *, + const gchar *, + const gchar *, + GdkWindow *, + gint, gint); + +static void +skin_setup_masks(Skin * skin); + +static void +skin_set_default_vis_color(Skin * skin); + + +void +skin_lock(Skin * skin) +{ + g_mutex_lock(skin->lock); +} + +void +skin_unlock(Skin * skin) +{ + g_mutex_unlock(skin->lock); +} + +gboolean +bmp_active_skin_reload(void) +{ + return bmp_active_skin_load(bmp_active_skin->path); +} + +gboolean +bmp_active_skin_load(const gchar * path) +{ + g_return_val_if_fail(bmp_active_skin != NULL, FALSE); + + memset(&bmp_active_skin->properties, 0, sizeof(SkinProperties)); + + if (!skin_load(bmp_active_skin, path)) + return FALSE; + + skin_setup_masks(bmp_active_skin); + + if (cfg.playlist_transparent) + { + if (rootpix != NULL) + g_object_unref(rootpix); + + rootpix = shade_pixmap(get_transparency_pixmap(), 0, 0, 0, 0, gdk_screen_width(), gdk_screen_height(), + skin_get_color(bmp_active_skin, SKIN_PLEDIT_NORMALBG)); + } + + draw_main_window(TRUE); + draw_playlist_window(TRUE); + draw_equalizer_window(TRUE); + + vis_set_window(mainwin_vis, mainwin->window); + playlistwin_update_list(); + + return TRUE; +} + +void +skin_pixmap_free(SkinPixmap * p) +{ + g_return_if_fail(p != NULL); + g_return_if_fail(p->pixmap != NULL); + + g_object_unref(p->pixmap); + p->pixmap = NULL; +} + +Skin * +skin_new(void) +{ + Skin *skin; + skin = g_new0(Skin, 1); + skin->lock = g_mutex_new(); + return skin; +} + +void +skin_free(Skin * skin) +{ + gint i; + + g_return_if_fail(skin != NULL); + + skin_lock(skin); + + for (i = 0; i < SKIN_PIXMAP_COUNT; i++) + skin_pixmap_free(&skin->pixmaps[i]); + + for (i = 0; i < SKIN_PIXMAP_COUNT; i++) { + if (skin->masks[i]) + g_object_unref(skin->masks[i]); + + skin->masks[i] = NULL; + } + + skin_set_default_vis_color(skin); + skin_unlock(skin); +} + +void +skin_destroy(Skin * skin) +{ + g_return_if_fail(skin != NULL); + skin_free(skin); + g_mutex_free(skin->lock); + g_free(skin); +} + +const SkinPixmapIdMapping * +skin_pixmap_id_lookup(guint id) +{ + guint i; + + for (i = 0; i < skin_pixmap_id_map_size; i++) { + if (id == skin_pixmap_id_map[i].id) { + return &skin_pixmap_id_map[i]; + } + } + + return NULL; +} + +const gchar * +skin_pixmap_id_to_name(SkinPixmapId id) +{ + guint i; + + for (i = 0; i < skin_pixmap_id_map_size; i++) { + if (id == skin_pixmap_id_map[i].id) + return skin_pixmap_id_map[i].name; + } + return NULL; +} + +static void +skin_set_default_vis_color(Skin * skin) +{ + memcpy(skin->vis_color, skin_default_viscolor, + sizeof(skin_default_viscolor)); +} + +/* + * I have rewritten this to take an array of possible targets, + * once we find a matching target we now return, instead of loop + * recursively. This allows for us to support many possible format + * targets for our skinning engine than just the original winamp + * formats. + * + * -- nenolod, 16 January 2006 + */ +gchar * +skin_pixmap_locate(const gchar * dirname, gchar ** basenames) +{ + gchar *filename; + gint i; + + for (i = 0; basenames[i]; i++) + if (!(filename = find_file_recursively(dirname, basenames[i]))) + g_free(filename); + else + return filename; + + /* can't find any targets -- sorry */ + return NULL; +} + +/* FIXME: this function is temporary. It will be removed when the skinning system + uses GdkPixbuf in place of GdkPixmap */ + +static GdkPixmap * +pixmap_new_from_file(const gchar * filename) +{ + GdkPixbuf *pixbuf; + GdkPixmap *pixmap; + gint width, height; + + if (!(pixbuf = gdk_pixbuf_new_from_file(filename, NULL))) + return NULL; + + width = gdk_pixbuf_get_width(pixbuf); + height = gdk_pixbuf_get_height(pixbuf); + + if (!(pixmap = gdk_pixmap_new(mainwin->window, width, height, + gdk_rgb_get_visual()->depth))) { + g_object_unref(pixbuf); + return NULL; + } + + gdk_pixbuf_render_to_drawable(pixbuf, pixmap, mainwin_gc, 0, 0, 0, 0, + width, height, GDK_RGB_DITHER_MAX, 0, 0); + g_object_unref(pixbuf); + + return pixmap; +} + +static gboolean +skin_load_pixmap_id(Skin * skin, SkinPixmapId id, const gchar * path_p) +{ + const gchar *path; + gchar *filename; + gint width, height; + const SkinPixmapIdMapping *pixmap_id_mapping; + GdkPixmap *gpm; + SkinPixmap *pm = NULL; + gchar *basenames[EXTENSION_TARGETS * 2 + 1]; /* alternate basenames */ + gint i, y; + + g_return_val_if_fail(skin != NULL, FALSE); + g_return_val_if_fail(id < SKIN_PIXMAP_COUNT, FALSE); + + pixmap_id_mapping = skin_pixmap_id_lookup(id); + g_return_val_if_fail(pixmap_id_mapping != NULL, FALSE); + + memset(&basenames, 0, sizeof(basenames)); + + for (i = 0, y = 0; i < EXTENSION_TARGETS; i++, y++) + { + basenames[y] = g_strdup_printf("%s.%s", pixmap_id_mapping->name, + ext_targets[i]); + + if (pixmap_id_mapping->alt_name) + basenames[++y] = g_strdup_printf("%s.%s", + pixmap_id_mapping->alt_name, ext_targets[i]); + } + + path = path_p ? path_p : skin->path; + filename = skin_pixmap_locate(path, basenames); + + for (i = 0; basenames[i] != NULL; i++) + { + g_free(basenames[i]); + basenames[i] = NULL; + } + + if (!(gpm = pixmap_new_from_file(filename))) { + g_warning("loading of %s failed", filename); + g_free(filename); + return FALSE; + } + + g_free(filename); + + gdk_drawable_get_size(GDK_DRAWABLE(gpm), &width, &height); + pm = &skin->pixmaps[id]; + pm->pixmap = gpm; + pm->width = width; + pm->height = height; + pm->current_width = width; + pm->current_height = height; + + return TRUE; +} + +void +skin_mask_create(Skin * skin, + const gchar * path, + gint id, + GdkWindow * window) +{ + skin->masks[id] = + skin_create_transparent_mask(path, "region.txt", + skin_mask_info[id].inistr, window, + skin_mask_info[id].width, + skin_mask_info[id].height); +} + +static void +skin_setup_masks(Skin * skin) +{ + GdkBitmap *mask; + + if (cfg.show_wm_decorations) + return; + + if (cfg.player_visible) { + mask = skin_get_mask(skin, SKIN_MASK_MAIN + cfg.player_shaded); + gtk_widget_shape_combine_mask(mainwin, mask, 0, 0); + } + + mask = skin_get_mask(skin, SKIN_MASK_EQ + cfg.equalizer_shaded); + gtk_widget_shape_combine_mask(equalizerwin, mask, 0, 0); +} + +static GdkBitmap * +create_default_mask(GdkWindow * parent, gint w, gint h) +{ + GdkBitmap *ret; + GdkGC *gc; + GdkColor pattern; + + ret = gdk_pixmap_new(parent, w, h, 1); + gc = gdk_gc_new(ret); + pattern.pixel = 1; + gdk_gc_set_foreground(gc, &pattern); + gdk_draw_rectangle(ret, gc, TRUE, 0, 0, w, h); + gdk_gc_destroy(gc); + + return ret; +} + +static void +skin_query_color(GdkColormap * cm, GdkColor * c) +{ +#ifdef GDK_WINDOWING_X11 + XColor xc = { 0,0,0,0,0,0 }; + + xc.pixel = c->pixel; + XQueryColor(GDK_COLORMAP_XDISPLAY(cm), GDK_COLORMAP_XCOLORMAP(cm), &xc); + c->red = xc.red; + c->green = xc.green; + c->blue = xc.blue; +#else + /* do nothing. see what breaks? */ +#endif +} + +static glong +skin_calc_luminance(GdkColor * c) +{ + return (0.212671 * c->red + 0.715160 * c->green + 0.072169 * c->blue); +} + +static void +skin_get_textcolors(GdkPixmap * text, GdkColor * bgc, GdkColor * fgc) +{ + /* + * Try to extract reasonable background and foreground colors + * from the font pixmap + */ + + GdkImage *gi; + GdkColormap *cm; + gint i; + + g_return_if_fail(text != NULL); + + /* Get the first line of text */ + gi = gdk_drawable_get_image(text, 0, 0, 152, 6); + cm = gdk_window_get_colormap(playlistwin->window); + g_return_if_fail(GDK_IS_WINDOW(playlistwin->window)); + + for (i = 0; i < 6; i++) { + GdkColor c; + gint x; + glong d, max_d; + + /* Get a pixel from the middle of the space character */ + bgc[i].pixel = gdk_image_get_pixel(gi, 151, i); + skin_query_color(cm, &bgc[i]); + + max_d = 0; + for (x = 1; x < 150; x++) { + c.pixel = gdk_image_get_pixel(gi, x, i); + skin_query_color(cm, &c); + + d = labs(skin_calc_luminance(&c) - skin_calc_luminance(&bgc[i])); + if (d > max_d) { + memcpy(&fgc[i], &c, sizeof(GdkColor)); + max_d = d; + } + } + } + gdk_image_destroy(gi); +} + +gboolean +init_skins(const gchar * path) +{ + bmp_active_skin = skin_new(); + + if (!bmp_active_skin_load(path)) { + /* FIXME: Oddly, g_message() causes a crash if path is NULL on + * Solaris (see bug #165) */ + if (path) + g_message("Unable to load skin (%s), trying default...", path); + + /* can't load configured skin, retry with default */ + if (!bmp_active_skin_load(BMP_DEFAULT_SKIN_PATH)) { + g_message("Unable to load default skin (%s)! Giving up.", + BMP_DEFAULT_SKIN_PATH); + return FALSE; + } + } + + if (cfg.random_skin_on_play) + skinlist_update(); + + return TRUE; +} + +/* + * Opens and parses a skin's hints file. + * Hints files are somewhat like "scripts" in Winamp3/5. + * We'll probably add scripts to it next. + */ +void +skin_parse_hints(Skin * skin, gchar *path_p) +{ + gchar *filename, *tmp; + + path_p = path_p ? path_p : skin->path; + + filename = find_file_recursively(path_p, "skin.hints"); + + if (filename == NULL) + return; + +#if 0 + skin->description = read_ini_string(filename, "skin", "skinDescription"); +#endif + + tmp = read_ini_string(filename, "skin", "mainwinOthertext"); + + if (tmp != NULL) + skin->properties.mainwin_othertext = atoi(tmp); +} + +static guint +hex_chars_to_int(gchar hi, gchar lo) +{ + /* + * Converts a value in the range 0x00-0xFF + * to a integer in the range 0-65535 + */ + gchar str[3]; + + str[0] = hi; + str[1] = lo; + str[2] = 0; + + return (CLAMP(strtol(str, NULL, 16), 0, 0xFF) << 8); +} + +GdkColor * +skin_load_color(const gchar * path, const gchar * file, + const gchar * section, const gchar * key, + gchar * default_hex) +{ + gchar *filename, *value; + GdkColor *color = NULL; + + filename = find_file_recursively(path, file); + if (filename || default_hex) { + if (filename) { + value = read_ini_string(filename, section, key); + if (value == NULL) { + value = g_strdup(default_hex); + } + } else { + value = g_strdup(default_hex); + } + if (value) { + gchar *ptr = value; + gint len; + + color = g_new0(GdkColor, 1); + g_strstrip(value); + + if (value[0] == '#') + ptr++; + len = strlen(ptr); + /* + * The handling of incomplete values is done this way + * to maximize winamp compatibility + */ + if (len >= 6) { + color->red = hex_chars_to_int(*ptr, *(ptr + 1)); + ptr += 2; + } + if (len >= 4) { + color->green = hex_chars_to_int(*ptr, *(ptr + 1)); + ptr += 2; + } + if (len >= 2) + color->blue = hex_chars_to_int(*ptr, *(ptr + 1)); + + gdk_color_alloc(gdk_window_get_colormap(playlistwin->window), + color); + g_free(value); + } + if (filename) + g_free(filename); + } + return color; +} + + + +GdkBitmap * +skin_create_transparent_mask(const gchar * path, + const gchar * file, + const gchar * section, + GdkWindow * window, + gint width, + gint height) +{ + GdkBitmap *mask = NULL; + GdkGC *gc = NULL; + GdkColor pattern; + GdkPoint *gpoints; + + gchar *filename = NULL; + gboolean created_mask = FALSE; + GArray *num, *point; + guint i, j; + gint k; + + if (path) + filename = find_file_recursively(path, file); + + /* filename will be null if path wasn't set */ + if (!filename) { + return create_default_mask(window, width, height); + } + + if ((num = read_ini_array(filename, section, "NumPoints")) == NULL) { + g_free(filename); + return NULL; + } + + if ((point = read_ini_array(filename, section, "PointList")) == NULL) { + g_array_free(num, TRUE); + g_free(filename); + return NULL; + } + + mask = gdk_pixmap_new(window, width, height, 1); + gc = gdk_gc_new(mask); + + pattern.pixel = 0; + gdk_gc_set_foreground(gc, &pattern); + gdk_draw_rectangle(mask, gc, TRUE, 0, 0, width, height); + pattern.pixel = 1; + gdk_gc_set_foreground(gc, &pattern); + + j = 0; + for (i = 0; i < num->len; i++) { + if ((int)(point->len - j) >= (g_array_index(num, gint, i) * 2)) { + created_mask = TRUE; + gpoints = g_new(GdkPoint, g_array_index(num, gint, i)); + for (k = 0; k < g_array_index(num, gint, i); k++) { + gpoints[k].x = g_array_index(point, gint, j + k * 2); + gpoints[k].y = g_array_index(point, gint, j + k * 2 + 1); + } + j += k * 2; + gdk_draw_polygon(mask, gc, TRUE, gpoints, + g_array_index(num, gint, i)); + g_free(gpoints); + } + } + g_array_free(num, TRUE); + g_array_free(point, TRUE); + g_free(filename); + + if (!created_mask) + gdk_draw_rectangle(mask, gc, TRUE, 0, 0, width, height); + + gdk_gc_destroy(gc); + + return mask; +} + +void +skin_load_viscolor(Skin * skin, const gchar * path, const gchar * basename) +{ + FILE *file; + gint i, c; + gchar line[256], *filename; + GArray *a; + + g_return_if_fail(skin != NULL); + g_return_if_fail(path != NULL); + g_return_if_fail(basename != NULL); + + skin_set_default_vis_color(skin); + + filename = find_file_recursively(path, basename); + if (!filename) + return; + + if (!(file = fopen(filename, "r"))) { + g_free(filename); + return; + } + + g_free(filename); + + for (i = 0; i < 24; i++) { + if (fgets(line, 255, file)) { + a = string_to_garray(line); + if (a->len > 2) { + for (c = 0; c < 3; c++) + skin->vis_color[i][c] = g_array_index(a, gint, c); + } + g_array_free(a, TRUE); + } + else + break; + } + + fclose(file); +} + +#if 0 +static void +skin_numbers_generate_dash(Skin * skin) +{ + GdkGC *gc; + GdkPixmap *pixmap; + SkinPixmap *numbers; + + g_return_if_fail(skin != NULL); + + numbers = &skin->pixmaps[SKIN_NUMBERS]; + if (!numbers->pixmap || numbers->current_width < 99) + return; + + gc = gdk_gc_new(numbers->pixmap); + pixmap = gdk_pixmap_new(mainwin->window, 108, + numbers->current_height, + -1); + + skin_draw_pixmap(skin, pixmap, gc, SKIN_NUMBERS, 0, 0, 0, 0, 99, 13); + skin_draw_pixmap(skin, pixmap, gc, SKIN_NUMBERS, 90, 0, 99, 0, 9, 13); + skin_draw_pixmap(skin, pixmap, gc, SKIN_NUMBERS, 20, 6, 101, 6, 5, 1); + + g_object_unref(numbers->pixmap); + g_object_unref(gc); + + numbers->pixmap = pixmap; + numbers->current_width = 108; +} +#endif + +static void +skin_load_cursor(Skin * skin, const gchar * dirname) +{ + const gchar * basename = "normal.cur"; + gchar * filename = NULL; + GdkPixbuf * cursor_pixbuf = NULL; + GdkPixbufAnimation * cursor_animated = NULL; + GdkCursor * cursor_gdk = NULL; + GError * error = NULL; + + filename = find_file_recursively(dirname, basename); + + if (filename && cfg.custom_cursors) { + cursor_animated = gdk_pixbuf_animation_new_from_file(filename, &error); + cursor_pixbuf = gdk_pixbuf_animation_get_static_image(cursor_animated); + cursor_gdk = gdk_cursor_new_from_pixbuf(gdk_display_get_default(), + cursor_pixbuf, 0, 0); + } else { + cursor_gdk = gdk_cursor_new(GDK_LEFT_PTR); + } + + gdk_window_set_cursor(mainwin->window, cursor_gdk); + gdk_window_set_cursor(playlistwin->window, cursor_gdk); + gdk_window_set_cursor(equalizerwin->window, cursor_gdk); + gdk_cursor_unref(cursor_gdk); +} + +static void +skin_load_pixmaps(Skin * skin, const gchar * path) +{ + GdkPixmap *text_pm; + guint i; + + for (i = 0; i < SKIN_PIXMAP_COUNT; i++) + skin_load_pixmap_id(skin, i, path); + + text_pm = skin->pixmaps[SKIN_TEXT].pixmap; + + if (text_pm) + skin_get_textcolors(text_pm, skin->textbg, skin->textfg); + +#if 0 + if (skin->pixmaps[SKIN_NUMBERS].pixmap) + skin_numbers_generate_dash(skin); +#endif + + skin->colors[SKIN_PLEDIT_NORMAL] = + skin_load_color(path, "pledit.txt", "text", "normal", "#2499ff"); + skin->colors[SKIN_PLEDIT_CURRENT] = + skin_load_color(path, "pledit.txt", "text", "current", "#ffeeff"); + skin->colors[SKIN_PLEDIT_NORMALBG] = + skin_load_color(path, "pledit.txt", "text", "normalbg", "#0a120a"); + skin->colors[SKIN_PLEDIT_SELECTEDBG] = + skin_load_color(path, "pledit.txt", "text", "selectedbg", "#0a124a"); + + skin_mask_create(skin, path, SKIN_MASK_MAIN, mainwin->window); + skin_mask_create(skin, path, SKIN_MASK_MAIN_SHADE, mainwin->window); + + skin_mask_create(skin, path, SKIN_MASK_EQ, equalizerwin->window); + skin_mask_create(skin, path, SKIN_MASK_EQ_SHADE, equalizerwin->window); + + skin_load_viscolor(skin, path, "viscolor.txt"); +} + +static gboolean +skin_load_nolock(Skin * skin, const gchar * path, gboolean force) +{ + gchar *cpath; + + g_return_val_if_fail(skin != NULL, FALSE); + g_return_val_if_fail(path != NULL, FALSE); + REQUIRE_LOCK(skin->lock); + + if (!g_file_test(path, G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_DIR)) + return FALSE; + + if (!force) { + if (skin->path) + if (!strcmp(skin->path, path)) + return FALSE; + } + + skin_current_num++; + + skin->path = g_strdup(path); + + if (!file_is_archive(path)) { + skin_load_pixmaps(skin, path); + skin_load_cursor(skin, path); + + /* Parse the hints for this skin. */ + skin_parse_hints(skin, NULL); + + return TRUE; + } + + if (!(cpath = archive_decompress(path))) { + g_message("Unable to extract skin archive (%s)", path); + return FALSE; + } + + skin_load_pixmaps(skin, cpath); + skin_load_cursor(skin, cpath); + + /* Parse the hints for this skin. */ + skin_parse_hints(skin, cpath); + + del_directory(cpath); + g_free(cpath); + + return TRUE; +} + +void +skin_install_skin(const gchar * path) +{ + gchar *command; + + g_return_if_fail(path != NULL); + + command = g_strdup_printf("cp %s %s", path, bmp_paths[BMP_PATH_USER_SKIN_DIR]); + if (system(command)) { + g_message("Unable to install skin (%s) into user directory (%s)", + path, bmp_paths[BMP_PATH_USER_SKIN_DIR]); + } + g_free(command); +} + + +gboolean +skin_load(Skin * skin, const gchar * path) +{ + gboolean error; + + g_return_val_if_fail(skin != NULL, FALSE); + + if (!path) + return FALSE; + + skin_lock(skin); + error = skin_load_nolock(skin, path, FALSE); + skin_unlock(skin); + + return error; +} + +gboolean +skin_reload_forced(void) +{ + gboolean error; + + skin_lock(bmp_active_skin); + error = skin_load_nolock(bmp_active_skin, bmp_active_skin->path, TRUE); + skin_unlock(bmp_active_skin); + + return error; +} + +void +skin_reload(Skin * skin) +{ + g_return_if_fail(skin != NULL); + skin_load_nolock(skin, skin->path, TRUE); +} + + +static SkinPixmap * +skin_get_pixmap(Skin * skin, SkinPixmapId map_id) +{ + g_return_val_if_fail(skin != NULL, NULL); + g_return_val_if_fail(map_id < SKIN_PIXMAP_COUNT, NULL); + + return &skin->pixmaps[map_id]; +} + +GdkBitmap * +skin_get_mask(Skin * skin, SkinMaskId mi) +{ + g_return_val_if_fail(skin != NULL, NULL); + g_return_val_if_fail(mi < SKIN_PIXMAP_COUNT, NULL); + + return skin->masks[mi]; +} + +GdkColor * +skin_get_color(Skin * skin, SkinColorId color_id) +{ + GdkColor *ret = NULL; + + g_return_val_if_fail(skin != NULL, NULL); + + switch (color_id) { + case SKIN_TEXTBG: + if (skin->pixmaps[SKIN_TEXT].pixmap) + ret = skin->textbg; + else + ret = skin->def_textbg; + break; + case SKIN_TEXTFG: + if (skin->pixmaps[SKIN_TEXT].pixmap) + ret = skin->textfg; + else + ret = skin->def_textfg; + break; + default: + if (color_id < SKIN_COLOR_COUNT) + ret = skin->colors[color_id]; + break; + } + return ret; +} + +void +skin_get_viscolor(Skin * skin, guchar vis_color[24][3]) +{ + gint i; + + g_return_if_fail(skin != NULL); + + for (i = 0; i < 24; i++) { + vis_color[i][0] = skin->vis_color[i][0]; + vis_color[i][1] = skin->vis_color[i][1]; + vis_color[i][2] = skin->vis_color[i][2]; + } +} + +gint +skin_get_id(void) +{ + return skin_current_num; +} + +void +skin_draw_pixmap(Skin * skin, GdkDrawable * drawable, GdkGC * gc, + SkinPixmapId pixmap_id, + gint xsrc, gint ysrc, gint xdest, gint ydest, + gint width, gint height) +{ + SkinPixmap *pixmap; + + g_return_if_fail(skin != NULL); + + pixmap = skin_get_pixmap(skin, pixmap_id); + g_return_if_fail(pixmap != NULL); + g_return_if_fail(pixmap->pixmap != NULL); + + if (xsrc > pixmap->width || ysrc > pixmap->height) + return; + + width = MIN(width, pixmap->width - xsrc); + height = MIN(height, pixmap->height - ysrc); + gdk_draw_pixmap(drawable, gc, pixmap->pixmap, xsrc, ysrc, + xdest, ydest, width, height); +} + +void +skin_get_eq_spline_colors(Skin * skin, guint32 colors[19]) +{ + gint i; + GdkPixmap *pixmap; + GdkImage *img; + SkinPixmap *eqmainpm; + + g_return_if_fail(skin != NULL); + + eqmainpm = &skin->pixmaps[SKIN_EQMAIN]; + if (eqmainpm->pixmap && + eqmainpm->current_width >= 116 && eqmainpm->current_height >= 313) + pixmap = eqmainpm->pixmap; + else + return; + + if (!GDK_IS_DRAWABLE(pixmap)) + return; + + if (!(img = gdk_drawable_get_image(pixmap, 115, 294, 1, 19))) + return; + + for (i = 0; i < 19; i++) + colors[i] = gdk_image_get_pixel(img, 0, i); + + gdk_image_destroy(img); +} + + +static void +skin_draw_playlistwin_frame_top(Skin * skin, + GdkDrawable * drawable, + GdkGC * gc, + gint width, gint height, gboolean focus) +{ + /* The title bar skin consists of 2 sets of 4 images, 1 set + * for focused state and the other for unfocused. The 4 images + * are: + * + * a. right corner (25,20) + * b. left corner (25,20) + * c. tiler (25,20) + * d. title (100,20) + * + * min allowed width = 100+25+25 = 150 + */ + + gint i, y, c; + + /* get y offset of the pixmap set to use */ + if (focus) + y = 0; + else + y = 21; + + /* left corner */ + skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 0, y, 0, 0, 25, 20); + + /* titlebar title */ + skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 26, y, + (width - 100) / 2, 0, 100, 20); + + /* titlebar right corner */ + skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 153, y, + width - 25, 0, 25, 20); + + /* tile draw the remaining frame */ + + /* compute tile count */ + c = (width - (100 + 25 + 25)) / 25; + + for (i = 0; i < c / 2; i++) { + /* left of title */ + skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 127, y, + 25 + i * 25, 0, 25, 20); + + /* right of title */ + skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 127, y, + (width + 100) / 2 + i * 25, 0, 25, 20); + } + + if (c & 1) { + /* Odd tile count, so one remaining to draw. Here we split + * it into two and draw half on either side of the title */ + skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 127, y, + ((c / 2) * 25) + 25, 0, 12, 20); + skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 127, y, + (width / 2) + ((c / 2) * 25) + 50, 0, 13, 20); + } +} + +static void +skin_draw_playlistwin_frame_bottom(Skin * skin, + GdkDrawable * drawable, + GdkGC * gc, + gint width, gint height, gboolean focus) +{ + /* The bottom frame skin consists of 1 set of 4 images. The 4 + * images are: + * + * a. left corner with menu buttons (125,38) + * b. visualization window (75,38) + * c. right corner with play buttons (150,38) + * d. frame tile (25,38) + * + * (min allowed width = 125+150+25=300 + */ + + gint i, c; + + /* bottom left corner (menu buttons) */ + skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 0, 72, + 0, height - 38, 125, 38); + + c = (width - 275) / 25; + + /* draw visualization window, if width allows */ + if (c >= 3) { + c -= 3; + skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 205, 0, + width - (150 + 75), height - 38, 75, 38); + } + + /* Bottom right corner (playbuttons etc) */ + skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, + 126, 72, width - 150, height - 38, 150, 38); + + /* Tile draw the remaining undrawn portions */ + for (i = 0; i < c; i++) + skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 179, 0, + 125 + i * 25, height - 38, 25, 38); +} + +static void +skin_draw_playlistwin_frame_sides(Skin * skin, + GdkDrawable * drawable, + GdkGC * gc, + gint width, gint height, gboolean focus) +{ + /* The side frames consist of 2 tile images. 1 for the left, 1 for + * the right. + * a. left (12,29) + * b. right (19,29) + */ + + gint i; + + /* frame sides */ + for (i = 0; i < (height - (20 + 38)) / 29; i++) { + /* left */ + skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 0, 42, + 0, 20 + i * 29, 12, 29); + + /* right */ + skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 32, 42, + width - 19, 20 + i * 29, 19, 29); + } +} + + +void +skin_draw_playlistwin_frame(Skin * skin, + GdkDrawable * drawable, GdkGC * gc, + gint width, gint height, gboolean focus) +{ + skin_draw_playlistwin_frame_top(skin, drawable, gc, width, height, focus); + skin_draw_playlistwin_frame_bottom(skin, drawable, gc, width, height, + focus); + skin_draw_playlistwin_frame_sides(skin, drawable, gc, width, height, + focus); +} + + +void +skin_draw_playlistwin_shaded(Skin * skin, + GdkDrawable * drawable, GdkGC * gc, + gint width, gboolean focus) +{ + /* The shade mode titlebar skin consists of 4 images: + * a) left corner offset (72,42) size (25,14) + * b) right corner, focused offset (99,57) size (50,14) + * c) right corner, unfocused offset (99,42) size (50,14) + * d) bar tile offset (72,57) size (25,14) + */ + + gint i; + + /* left corner */ + skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 72, 42, 0, 0, 25, 14); + + /* bar tile */ + for (i = 0; i < (width - 75) / 25; i++) + skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 72, 57, + (i * 25) + 25, 0, 25, 14); + + /* right corner */ + skin_draw_pixmap(skin, drawable, gc, SKIN_PLEDIT, 99, focus ? 57 : 42, + width - 50, 0, 50, 14); +} + + +void +skin_draw_mainwin_titlebar(Skin * skin, + GdkDrawable * drawable, GdkGC * gc, + gboolean shaded, gboolean focus) +{ + /* The titlebar skin consists of 2 sets of 2 images, one for for + * shaded and the other for unshaded mode, giving a total of 4. + * The images are exactly 275x14 pixels, aligned and arranged + * vertically on each other in the pixmap in the following order: + * + * a) unshaded, focused offset (27, 0) + * b) unshaded, unfocused offset (27, 15) + * c) shaded, focused offset (27, 29) + * d) shaded, unfocused offset (27, 42) + */ + + gint y_offset; + + if (shaded) { + if (focus) + y_offset = 29; + else + y_offset = 42; + } + else { + if (focus) + y_offset = 0; + else + y_offset = 15; + } + + skin_draw_pixmap(skin, drawable, gc, SKIN_TITLEBAR, 27, y_offset, + 0, 0, MAINWIN_WIDTH, MAINWIN_TITLEBAR_HEIGHT); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/skin.h Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,149 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _WIDGETCORE_H_ +#error Please do not include me directly! Use widgetcore.h instead! +#endif + +#ifndef SKIN_H +#define SKIN_H + + +#include <glib.h> +#include <gdk/gdk.h> + + +#define BMP_DEFAULT_SKIN_PATH \ + DATA_DIR G_DIR_SEPARATOR_S "Skins" G_DIR_SEPARATOR_S "Default" + + +typedef enum { + SKIN_MAIN = 0, + SKIN_CBUTTONS, + SKIN_TITLEBAR, + SKIN_SHUFREP, + SKIN_TEXT, + SKIN_VOLUME, + SKIN_BALANCE, + SKIN_MONOSTEREO, + SKIN_PLAYPAUSE, + SKIN_NUMBERS, + SKIN_POSBAR, + SKIN_PLEDIT, + SKIN_EQMAIN, + SKIN_EQ_EX, + SKIN_PIXMAP_COUNT +} SkinPixmapId; + +typedef enum { + SKIN_MASK_MAIN = 0, + SKIN_MASK_MAIN_SHADE, + SKIN_MASK_EQ, + SKIN_MASK_EQ_SHADE, + SKIN_MASK_COUNT +} SkinMaskId; + +typedef enum { + SKIN_PLEDIT_NORMAL = 0, + SKIN_PLEDIT_CURRENT, + SKIN_PLEDIT_NORMALBG, + SKIN_PLEDIT_SELECTEDBG, + SKIN_TEXTBG, + SKIN_TEXTFG, + SKIN_COLOR_COUNT +} SkinColorId; + +typedef struct _SkinProperties { + gboolean mainwin_othertext; +} SkinProperties; + +#define SKIN_PIXMAP(x) ((SkinPixmap *)(x)) +typedef struct _SkinPixmap { + GdkPixmap *pixmap; + /* GdkPixmap *def_pixmap; */ + + /* The real size of the pixmap */ + gint width, height; + + /* The size of the pixmap from the current skin, + which might be smaller */ + gint current_width, current_height; +} SkinPixmap; + + +#define SKIN(x) ((Skin *)(x)) +typedef struct _Skin { + GMutex *lock; + gchar *path; + gchar *def_path; + SkinPixmap pixmaps[SKIN_PIXMAP_COUNT]; + GdkColor textbg[6], def_textbg[6]; + GdkColor textfg[6], def_textfg[6]; + GdkColor *colors[SKIN_COLOR_COUNT]; + guchar vis_color[24][3]; + GdkBitmap *masks[SKIN_MASK_COUNT]; + SkinProperties properties; +} Skin; + +extern Skin *bmp_active_skin; + +gboolean init_skins(const gchar * path); +void cleanup_skins(void); + +gboolean bmp_active_skin_load(const gchar * path); +gboolean bmp_active_skin_reload(void); + +Skin *skin_new(void); +gboolean skin_load(Skin * skin, const gchar * path); +void skin_reload(Skin * skin); +void skin_free(Skin * skin); + +GdkBitmap *skin_get_mask(Skin * skin, SkinMaskId mi); +GdkColor *skin_get_color(Skin * skin, SkinColorId color_id); + +void skin_get_viscolor(Skin * skin, guchar vis_color[24][3]); +gint skin_get_id(void); +void skin_draw_pixmap(Skin * skin, GdkDrawable * drawable, GdkGC * gc, + SkinPixmapId pixmap_id, + gint xsrc, gint ysrc, gint xdest, gint ydest, + gint width, gint height); +void skin_get_eq_spline_colors(Skin * skin, guint32 colors[19]); +void skin_install_skin(const gchar * path); + +void skin_draw_playlistwin_shaded(Skin * skin, + GdkDrawable * drawable, GdkGC * gc, + gint width, gboolean focus); +void skin_draw_playlistwin_frame(Skin * skin, + GdkDrawable * drawable, GdkGC * gc, + gint width, gint height, gboolean focus); + +void skin_draw_mainwin_titlebar(Skin * skin, + GdkDrawable * drawable, GdkGC * gc, + gboolean shaded, gboolean focus); + + +void skin_parse_hints(Skin * skin, gchar *path_p); + + +gboolean +skin_reload_forced(void); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/svis.c Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,204 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "widgetcore.h" + +#include <glib.h> +#include <gdk/gdk.h> +#include <string.h> + +#include "main.h" +#include "mainwin.h" +#include "plugin.h" +#include "widget.h" +#include "vis.h" + +static gint svis_redraw_delays[] = { 1, 2, 4, 8 }; + +/* FIXME: Are the svis_scope_colors correct? */ +static guint8 svis_scope_colors[] = { 20, 19, 18, 19, 20 }; +static guint8 svis_vu_normal_colors[] = { 17, 17, 17, 12, 12, 12, 2, 2 }; + +#define DRAW_DS_PIXEL(ptr,value) \ + *(ptr) = (value); \ + *((ptr) + 1) = (value); \ + *((ptr) + 76) = (value); \ + *((ptr) + 77) = (value); + +#define SVIS_HEIGHT 5 +#define SVIS_WIDTH 38 + +void +svis_timeout_func(SVis * svis, guchar * data) +{ + static GTimer *timer = NULL; + gulong micros = 9999999; + gboolean falloff = FALSE; + gint i; + + if (!timer) { + timer = g_timer_new(); + g_timer_start(timer); + } + else { + g_timer_elapsed(timer, µs); + if (micros > 14000) + g_timer_reset(timer); + + } + + if (cfg.vis_type == INPUT_VIS_ANALYZER) { + if (micros > 14000) + falloff = TRUE; + + for (i = 0; i < 2; i++) { + if (falloff || data) { + if (data && data[i] > svis->vs_data[i]) + svis->vs_data[i] = data[i]; + else if (falloff) { + if (svis->vs_data[i] >= 2) + svis->vs_data[i] -= 2; + else + svis->vs_data[i] = 0; + } + } + + } + } + else if (data) { + for (i = 0; i < 75; i++) + svis->vs_data[i] = data[i]; + } + + if (micros > 14000) { + if (!svis->vs_refresh_delay) { + svis_draw((Widget *) svis); + svis->vs_refresh_delay = svis_redraw_delays[cfg.vis_refresh]; + + } + svis->vs_refresh_delay--; + } +} + +void +svis_draw(Widget * w) +{ + SVis *svis = (SVis *) w; + gint x, y, h; + guchar svis_color[24][3]; + guchar rgb_data[SVIS_WIDTH * 2 * SVIS_HEIGHT * 2], *ptr, c; + guint32 colors[24]; + GdkRgbCmap *cmap; + + GDK_THREADS_ENTER(); + + skin_get_viscolor(bmp_active_skin, svis_color); + for (y = 0; y < 24; y++) { + colors[y] = + svis_color[y][0] << 16 | svis_color[y][1] << 8 | svis_color[y][2]; + } + cmap = gdk_rgb_cmap_new(colors, 24); + + memset(rgb_data, 0, SVIS_WIDTH * SVIS_HEIGHT); + if (cfg.vis_type == VIS_ANALYZER) { + switch (cfg.vu_mode) { + case VU_NORMAL: + for (y = 0; y < 2; y++) { + ptr = rgb_data + ((y * 3) * 38); + h = (svis->vs_data[y] * 7) / 37; + for (x = 0; x < h; x++, ptr += 5) { + c = svis_vu_normal_colors[x]; + *(ptr) = c; + *(ptr + 1) = c; + *(ptr + 2) = c; + *(ptr + 38) = c; + *(ptr + 39) = c; + *(ptr + 40) = c; + } + } + break; + case VU_SMOOTH: + for (y = 0; y < 2; y++) { + ptr = rgb_data + ((y * 3) * SVIS_WIDTH); + for (x = 0; x < svis->vs_data[y]; x++, ptr++) { + c = 17 - ((x * 15) / 37); + *(ptr) = c; + *(ptr + 38) = c; + } + } + break; + } + } + else if (cfg.vis_type == VIS_SCOPE) { + for (x = 0; x < 38; x++) { + h = svis->vs_data[x << 1] / 3; + ptr = rgb_data + ((4 - h) * 38) + x; + *ptr = svis_scope_colors[h]; + } + } + + gdk_draw_indexed_image(mainwin->window, mainwin_gc, + svis->vs_widget.x, svis->vs_widget.y, + svis->vs_widget.width, + svis->vs_widget.height, + GDK_RGB_DITHER_NORMAL, (guchar *) rgb_data, + 38, cmap); + + gdk_rgb_cmap_free(cmap); + GDK_THREADS_LEAVE(); +} + +void +svis_clear_data(SVis * svis) +{ + gint i; + + if (!svis) + return; + + for (i = 0; i < 75; i++) { + svis->vs_data[i] = (cfg.vis_type == VIS_SCOPE) ? 6 : 0; + } +} + +void +svis_clear(SVis * svis) +{ + gdk_window_clear_area(mainwin->window, svis->vs_widget.x, + svis->vs_widget.y, svis->vs_widget.width, + svis->vs_widget.height); +} + +SVis * +create_svis(GList ** wlist, + GdkPixmap * parent, + GdkGC * gc, + gint x, gint y) +{ + SVis *svis; + + svis = g_new0(SVis, 1); + widget_init(&svis->vs_widget, parent, gc, x, y, SVIS_WIDTH, SVIS_HEIGHT, + 1); + + widget_list_add(wlist, WIDGET(svis)); + return svis; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/svis.h Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,52 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _WIDGETCORE_H_ +#error Please do not include me directly! Use widgetcore.h instead! +#endif + +#ifndef SVIS_H +#define SVIS_H + +#include <glib.h> +#include <gdk/gdk.h> + +#include "svis.h" +#include "widget.h" + +#define SVIS(x) ((SVis *)(x)) +struct _SVis { + Widget vs_widget; + gint vs_data[75]; + gint vs_refresh_delay; +}; + +typedef struct _SVis SVis; + +void svis_draw(Widget * w); +void svis_timeout_func(SVis * svis, guchar * data); +SVis *create_svis(GList ** wlist, GdkPixmap * parent, GdkGC * gc, gint x, + gint y); +void svis_set_data(SVis * vis, guchar * data); +void svis_clear_data(SVis * vis); +void svis_clear(SVis * vis); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/tbutton.c Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,176 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "widgetcore.h" + +#include <glib.h> +#include <gdk/gdk.h> + +#include "widget.h" + +void +tbutton_draw(Widget * w) +{ + TButton *button = TBUTTON(w); + GdkPixmap *obj; + + obj = button->tb_widget.parent; + + if (button->tb_pressed && button->tb_inside) { + if (button->tb_selected) { + skin_draw_pixmap(bmp_active_skin, obj, + button->tb_widget.gc, + button->tb_skin_index, + button->tb_psx, button->tb_psy, + button->tb_widget.x, button->tb_widget.y, + button->tb_widget.width, + button->tb_widget.height); + } + else { + skin_draw_pixmap(bmp_active_skin, obj, + button->tb_widget.gc, + button->tb_skin_index, + button->tb_pux, button->tb_puy, + button->tb_widget.x, button->tb_widget.y, + button->tb_widget.width, + button->tb_widget.height); + } + } + else { + if (button->tb_selected) { + skin_draw_pixmap(bmp_active_skin, obj, + button->tb_widget.gc, + button->tb_skin_index, + button->tb_nsx, button->tb_nsy, + button->tb_widget.x, button->tb_widget.y, + button->tb_widget.width, + button->tb_widget.height); + } + else { + skin_draw_pixmap(bmp_active_skin, obj, + button->tb_widget.gc, + button->tb_skin_index, + button->tb_nux, button->tb_nuy, + button->tb_widget.x, button->tb_widget.y, + button->tb_widget.width, + button->tb_widget.height); + + } + } +} + +void +tbutton_button_press_cb(GtkWidget * widget, GdkEventButton * event, + TButton * button) +{ + if (event->button != 1) + return; + + if (widget_contains(&button->tb_widget, event->x, event->y)) { + button->tb_pressed = 1; + button->tb_inside = 1; + widget_draw(WIDGET(button)); + } +} + +void +tbutton_button_release_cb(GtkWidget * widget, GdkEventButton * event, + TButton * button) +{ + if (event->button != 1) + return; + + if (button->tb_inside && button->tb_pressed) { + button->tb_inside = 0; + button->tb_selected = !button->tb_selected; + + widget_draw(WIDGET(button)); + + if (button->tb_push_cb) + button->tb_push_cb(button->tb_selected); + } + + if (button->tb_pressed) + button->tb_pressed = 0; +} + +void +tbutton_motion_cb(GtkWidget * widget, GdkEventMotion * event, + TButton * button) +{ + gint inside; + + if (!button->tb_pressed) + return; + inside = widget_contains(&button->tb_widget, event->x, event->y); + if (inside != button->tb_inside) { + button->tb_inside = inside; + widget_draw(WIDGET(button)); + } +} + +TButton * +create_tbutton(GList ** wlist, GdkPixmap * parent, GdkGC * gc, + gint x, gint y, gint w, gint h, gint nux, gint nuy, + gint pux, gint puy, gint nsx, gint nsy, gint psx, + gint psy, void (*cb) (gboolean), SkinPixmapId si) +{ + TButton *b; + + b = g_new0(TButton, 1); + widget_init(&b->tb_widget, parent, gc, x, y, w, h, 1); + b->tb_widget.button_press_cb = + (void (*)(GtkWidget *, GdkEventButton *, gpointer)) + tbutton_button_press_cb; + b->tb_widget.button_release_cb = + (void (*)(GtkWidget *, GdkEventButton *, gpointer)) + tbutton_button_release_cb; + b->tb_widget.motion_cb = + (void (*)(GtkWidget *, GdkEventMotion *, gpointer)) + tbutton_motion_cb; + b->tb_widget.draw = tbutton_draw; + b->tb_nux = nux; + b->tb_nuy = nuy; + b->tb_pux = pux; + b->tb_puy = puy; + b->tb_nsx = nsx; + b->tb_nsy = nsy; + b->tb_psx = psx; + b->tb_psy = psy; + b->tb_push_cb = cb; + b->tb_skin_index = si; + + widget_list_add(wlist, WIDGET(b)); + return b; +} + +void +tbutton_set_toggled(TButton * tb, gboolean toggled) +{ + tb->tb_selected = toggled; + widget_draw(WIDGET(tb)); +} + +void +free_tbutton(TButton * b) +{ + g_free(b); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/tbutton.h Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,52 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _WIDGETCORE_H_ +#error Please do not include me directly! Use widgetcore.h instead! +#endif + +#ifndef TBUTTON_H +#define TBUTTON_H + +#include <glib.h> + +#include "skin.h" +#include "widget.h" + +#define TBUTTON(x) ((TButton *)(x)) +struct _TButton { + Widget tb_widget; + gint tb_nux, tb_nuy, tb_pux, tb_puy, tb_nsx, tb_nsy, tb_psx, tb_psy; + gint tb_pressed, tb_inside, tb_selected; + void (*tb_push_cb) (gboolean); + SkinPixmapId tb_skin_index; +}; + +typedef struct _TButton TButton; + +TButton *create_tbutton(GList ** wlist, GdkPixmap * parent, GdkGC * gc, + gint x, gint y, gint w, gint h, gint nux, gint nuy, + gint pux, gint puy, gint nsx, gint nsy, gint psx, + gint psy, void (*cb) (gboolean), SkinPixmapId si); +void tbutton_set_toggled(TButton * tb, gboolean toggled); +void free_tbutton(TButton * b); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/textbox.c Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,579 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "widgetcore.h" + +#include <glib.h> +#include <gtk/gtk.h> +#include <gdk/gdk.h> +#include <gdk/gdkprivate.h> +#include <string.h> +#include <ctype.h> + +#include "main.h" +#include "util.h" + +static void textbox_generate_pixmap(TextBox * tb); + +static void +textbox_draw(Widget * w) +{ + TextBox *tb = TEXT_BOX(w); + gint cw; + GdkPixmap *obj; + GdkPixmap *src; + + g_return_if_fail(tb != NULL); + + if (tb->tb_text && + (!tb->tb_pixmap_text || strcmp(tb->tb_text, tb->tb_pixmap_text))) + textbox_generate_pixmap(tb); + + if (tb->tb_pixmap) { + if (skin_get_id() != tb->tb_skin_id) { + tb->tb_skin_id = skin_get_id(); + textbox_generate_pixmap(tb); + } + obj = tb->tb_widget.parent; + src = tb->tb_pixmap; + + cw = tb->tb_pixmap_width - tb->tb_offset; + if (cw > tb->tb_widget.width) + cw = tb->tb_widget.width; + gdk_draw_pixmap(obj, tb->tb_widget.gc, src, tb->tb_offset, 0, + tb->tb_widget.x, tb->tb_widget.y, cw, + tb->tb_widget.height); + if (cw < tb->tb_widget.width) + gdk_draw_pixmap(obj, tb->tb_widget.gc, src, 0, 0, + tb->tb_widget.x + cw, tb->tb_widget.y, + tb->tb_widget.width - cw, tb->tb_widget.height); + } +} + +static gboolean +textbox_scroll(gpointer data) +{ + TextBox *tb = TEXT_BOX(data); + + if (!tb->tb_is_dragging) { + tb->tb_offset += 1; + if (tb->tb_offset >= tb->tb_pixmap_width) + tb->tb_offset -= tb->tb_pixmap_width; + widget_draw(WIDGET(tb)); + } + + return TRUE; +} + +static void +textbox_button_press(GtkWidget * w, GdkEventButton * event, gpointer data) +{ + TextBox *tb = TEXT_BOX(data); + + if (event->button != 1) + return; + if (widget_contains(&tb->tb_widget, event->x, event->y) && + tb->tb_scroll_allowed && + tb->tb_pixmap_width > tb->tb_widget.width && tb->tb_is_scrollable) { + tb->tb_is_dragging = TRUE; + tb->tb_drag_off = tb->tb_offset; + tb->tb_drag_x = event->x; + } +} + +static void +textbox_motion(GtkWidget * w, GdkEventMotion * event, gpointer data) +{ + TextBox *tb = TEXT_BOX(data); + + if (tb->tb_is_dragging) { + if (tb->tb_scroll_allowed && + tb->tb_pixmap_width > tb->tb_widget.width) { + tb->tb_offset = tb->tb_drag_off - (event->x - tb->tb_drag_x); + + while (tb->tb_offset < 0) + tb->tb_offset += tb->tb_pixmap_width; + + while (tb->tb_offset > tb->tb_pixmap_width) + tb->tb_offset -= tb->tb_pixmap_width; + + widget_draw(WIDGET(tb)); + } + } +} + +static void +textbox_button_release(GtkWidget * w, GdkEventButton * event, gpointer data) +{ + TextBox *tb = TEXT_BOX(data); + + if (event->button == 1) + tb->tb_is_dragging = FALSE; +} + +static gboolean +textbox_should_scroll(TextBox * tb) +{ + g_return_val_if_fail(tb != NULL, FALSE); + + if (!tb->tb_scroll_allowed) + return FALSE; + + if (tb->tb_font) { + gint width; + + text_get_extents(tb->tb_fontname, tb->tb_text, &width, NULL, NULL, + NULL); + + if (width <= tb->tb_widget.width) + return FALSE; + else + return TRUE; + } + + if (g_utf8_strlen(tb->tb_text, -1) * 5 > tb->tb_widget.width) + return TRUE; + + return FALSE; +} + +void +textbox_set_text(TextBox * tb, const gchar * text) +{ + g_return_if_fail(tb != NULL); + g_return_if_fail(text != NULL); + + widget_lock(WIDGET(tb)); + + if (tb->tb_text) { + if (!strcmp(text, tb->tb_text)) { + widget_unlock(WIDGET(tb)); + return; + } + g_free(tb->tb_text); + } + + tb->tb_text = str_to_utf8(text); + + widget_unlock(WIDGET(tb)); + widget_draw(WIDGET(tb)); +} + +static void +textbox_generate_xfont_pixmap(TextBox * tb, const gchar * pixmaptext) +{ + gint length, i; + GdkGC *gc, *maskgc; + GdkColor *c, pattern; + GdkBitmap *mask; + PangoLayout *layout; + gint width; + + g_return_if_fail(tb != NULL); + g_return_if_fail(pixmaptext != NULL); + + length = g_utf8_strlen(pixmaptext, -1); + + text_get_extents(tb->tb_fontname, pixmaptext, &width, NULL, NULL, NULL); + + tb->tb_pixmap_width = MAX(width, tb->tb_widget.width); + tb->tb_pixmap = gdk_pixmap_new(mainwin->window, tb->tb_pixmap_width, + tb->tb_widget.height, + gdk_rgb_get_visual()->depth); + gc = tb->tb_widget.gc; + c = skin_get_color(bmp_active_skin, SKIN_TEXTBG); + for (i = 0; i < tb->tb_widget.height; i++) { + gdk_gc_set_foreground(gc, &c[6 * i / tb->tb_widget.height]); + gdk_draw_line(tb->tb_pixmap, gc, 0, i, tb->tb_pixmap_width, i); + } + + mask = gdk_pixmap_new(mainwin->window, tb->tb_pixmap_width, + tb->tb_widget.height, 1); + maskgc = gdk_gc_new(mask); + pattern.pixel = 0; + gdk_gc_set_foreground(maskgc, &pattern); + + gdk_draw_rectangle(mask, maskgc, TRUE, 0, 0, + tb->tb_pixmap_width, tb->tb_widget.height); + pattern.pixel = 1; + gdk_gc_set_foreground(maskgc, &pattern); + + gdk_gc_set_foreground(gc, skin_get_color(bmp_active_skin, SKIN_TEXTFG)); + + layout = gtk_widget_create_pango_layout(mainwin, pixmaptext); + pango_layout_set_font_description(layout, tb->tb_font); + + gdk_draw_layout(tb->tb_pixmap, gc, 0, (tb->tb_font_descent / 2), layout); + g_object_unref(layout); + + g_object_unref(maskgc); + + gdk_gc_set_clip_mask(gc, mask); + c = skin_get_color(bmp_active_skin, SKIN_TEXTFG); + for (i = 0; i < tb->tb_widget.height; i++) { + gdk_gc_set_foreground(gc, &c[6 * i / tb->tb_widget.height]); + gdk_draw_line(tb->tb_pixmap, gc, 0, i, tb->tb_pixmap_width, i); + } + g_object_unref(mask); + gdk_gc_set_clip_mask(gc, NULL); +} + +static void +textbox_handle_special_char(gchar c, gint * x, gint * y) +{ + switch (c) { + case '"': + *x = 130; + *y = 0; + break; + case '\r': + *x = 50; + *y = 6; + break; + case ':': + case ';': + *x = 60; + *y = 6; + break; + case '(': + *x = 65; + *y = 6; + break; + case ')': + *x = 70; + *y = 6; + break; + case '-': + *x = 75; + *y = 6; + break; + case '`': + case '\'': + *x = 80; + *y = 6; + break; + case '!': + *x = 85; + *y = 6; + break; + case '_': + *x = 90; + *y = 6; + break; + case '+': + *x = 95; + *y = 6; + break; + case '\\': + *x = 100; + *y = 6; + break; + case '/': + *x = 105; + *y = 6; + break; + case '[': + *x = 110; + *y = 6; + break; + case ']': + *x = 115; + *y = 6; + break; + case '^': + *x = 120; + *y = 6; + break; + case '&': + *x = 125; + *y = 6; + break; + case '%': + *x = 130; + *y = 6; + break; + case '.': + case ',': + *x = 135; + *y = 6; + break; + case '=': + *x = 140; + *y = 6; + break; + case '$': + *x = 145; + *y = 6; + break; + case '#': + *x = 150; + *y = 6; + break; + case 'å': + case 'Å': + *x = 0; + *y = 12; + break; + case 'ö': + case 'Ö': + *x = 5; + *y = 12; + break; + case 'ä': + case 'Ä': + *x = 10; + *y = 12; + break; + case 'ü': + case 'Ü': + *x = 100; + *y = 0; + break; + case '?': + *x = 15; + *y = 12; + break; + case '*': + *x = 20; + *y = 12; + break; + default: + *x = 145; + *y = 0; + break; + } +} + +static void +textbox_generate_pixmap(TextBox * tb) +{ + gint length, i, x, y, wl; + gchar *pixmaptext; + GdkGC *gc; + + g_return_if_fail(tb != NULL); + + if (tb->tb_pixmap) { + g_object_unref(tb->tb_pixmap); + tb->tb_pixmap = NULL; + } + + /* + * Don't reset the offset if only text after the last '(' has + * changed. This is a hack to avoid visual noice on vbr files + * where we guess the length. + */ + if (!(tb->tb_pixmap_text && strrchr(tb->tb_text, '(') && + !strncmp(tb->tb_pixmap_text, tb->tb_text, + strrchr(tb->tb_text, '(') - tb->tb_text))) + tb->tb_offset = 0; + + g_free(tb->tb_pixmap_text); + tb->tb_pixmap_text = g_strdup(tb->tb_text); + + /* + * wl is the number of (partial) letters visible. Only makes + * sense when using skinned font. + */ + + wl = tb->tb_widget.width / 5; + if (wl * 5 != tb->tb_widget.width) + wl++; + + length = g_utf8_strlen(tb->tb_text, -1); + + tb->tb_is_scrollable = FALSE; + + if (textbox_should_scroll(tb)) { + tb->tb_is_scrollable = TRUE; + pixmaptext = g_strconcat(tb->tb_pixmap_text, " *** ", NULL); + length += 7; + } + else if (!tb->tb_font && length <= wl) { + gint pad = wl - length; + gchar *padchars = g_strnfill(pad, ' '); + + pixmaptext = g_strconcat(tb->tb_pixmap_text, padchars, NULL); + g_free(padchars); + length += pad; + } + else + pixmaptext = g_strdup(tb->tb_pixmap_text); + + + if (tb->tb_is_scrollable) { + if (tb->tb_scroll_enabled && !tb->tb_timeout_tag) { + gint tag; + tag = TEXTBOX_SCROLL_SMOOTH_TIMEOUT; + tb->tb_timeout_tag = gtk_timeout_add(tag, textbox_scroll, tb); + } + } + else { + if (tb->tb_timeout_tag) { + gtk_timeout_remove(tb->tb_timeout_tag); + tb->tb_timeout_tag = 0; + } + tb->tb_offset = 0; + } + + if (tb->tb_font) { + textbox_generate_xfont_pixmap(tb, pixmaptext); + g_free(pixmaptext); + return; + } + + tb->tb_pixmap_width = length * 5; + tb->tb_pixmap = gdk_pixmap_new(mainwin->window, + tb->tb_pixmap_width, 6, + gdk_rgb_get_visual()->depth); + gc = tb->tb_widget.gc; + + for (i = 0; i < length; i++) { + gchar c; + x = y = -1; + c = toupper(pixmaptext[i]); + if (c >= 'A' && c <= 'Z') { + x = 5 * (c - 'A'); + y = 0; + } + else if (c >= '0' && c <= '9') { + x = 5 * (c - '0'); + y = 6; + } + else + textbox_handle_special_char(c, &x, &y); + + skin_draw_pixmap(bmp_active_skin, + tb->tb_pixmap, gc, tb->tb_skin_index, + x, y, i * 5, 0, 5, 6); + } + g_free(pixmaptext); +} + +void +textbox_set_scroll(TextBox * tb, gboolean s) +{ + g_return_if_fail(tb != NULL); + + tb->tb_scroll_enabled = s; + if (tb->tb_scroll_enabled && tb->tb_is_scrollable + && tb->tb_scroll_allowed) { + gint tag; + tag = TEXTBOX_SCROLL_SMOOTH_TIMEOUT; + + if (tb->tb_timeout_tag) + { + gtk_timeout_remove(tb->tb_timeout_tag); + tb->tb_timeout_tag = 0; + } + + tb->tb_timeout_tag = gtk_timeout_add(tag, textbox_scroll, tb); + } + else + { + if (tb->tb_timeout_tag) + { + gtk_timeout_remove(tb->tb_timeout_tag); + tb->tb_timeout_tag = 0; + } + + tb->tb_offset = 0; + widget_draw(WIDGET(tb)); + } + +} + +void +textbox_set_xfont(TextBox * tb, gboolean use_xfont, const gchar * fontname) +{ + gint ascent, descent; + + g_return_if_fail(tb != NULL); + + if (tb->tb_font) { + pango_font_description_free(tb->tb_font); + tb->tb_font = NULL; + } + + tb->tb_widget.y = tb->tb_nominal_y; + tb->tb_widget.height = tb->tb_nominal_height; + + /* Make sure the pixmap is regenerated */ + if (tb->tb_pixmap_text) { + g_free(tb->tb_pixmap_text); + tb->tb_pixmap_text = NULL; + } + + if (!use_xfont || strlen(fontname) == 0) + return; + + tb->tb_font = pango_font_description_from_string(fontname); + tb->tb_fontname = g_strdup(fontname); + + text_get_extents(fontname, + "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz ", + NULL, NULL, &ascent, &descent); + tb->tb_font_ascent = ascent; + tb->tb_font_descent = descent; + + + if (tb->tb_font == NULL) + return; + + tb->tb_widget.height = tb->tb_font_ascent; + if (tb->tb_widget.height > tb->tb_nominal_height) + tb->tb_widget.y -= (tb->tb_widget.height - tb->tb_nominal_height) / 2; + else + tb->tb_widget.height = tb->tb_nominal_height; +} + +TextBox * +create_textbox(GList ** wlist, GdkPixmap * parent, GdkGC * gc, + gint x, gint y, gint w, gboolean allow_scroll, SkinPixmapId si) +{ + TextBox *tb; + + tb = g_new0(TextBox, 1); + widget_init(&tb->tb_widget, parent, gc, x, y, w, 6, 1); + tb->tb_widget.button_press_cb = textbox_button_press; + tb->tb_widget.button_release_cb = textbox_button_release; + tb->tb_widget.motion_cb = textbox_motion; + tb->tb_widget.draw = textbox_draw; + tb->tb_scroll_allowed = allow_scroll; + tb->tb_scroll_enabled = TRUE; + tb->tb_skin_index = si; + tb->tb_nominal_y = y; + tb->tb_nominal_height = tb->tb_widget.height; + widget_list_add(wlist, WIDGET(tb)); + tb->tb_timeout_tag = 0; + return tb; +} + +void +textbox_free(TextBox * tb) +{ + g_return_if_fail(tb != NULL); + + if (tb->tb_pixmap) + g_object_unref(tb->tb_pixmap); + g_free(tb->tb_text); + g_free(tb); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/textbox.h Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,68 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _WIDGETCORE_H_ +#error Please do not include me directly! Use widgetcore.h instead! +#endif + +#ifndef TEXTBOX_H +#define TEXTBOX_H + +#include <glib.h> +#include <gdk/gdk.h> +#include <pango/pango.h> + +#include "skin.h" +#include "widget.h" + +#define TEXTBOX_SCROLL_TIMEOUT 200 +#define TEXTBOX_SCROLL_SMOOTH_TIMEOUT 30 + +#define TEXT_BOX(x) ((TextBox *)(x)) +struct _TextBox { + Widget tb_widget; + GdkPixmap *tb_pixmap; + gchar *tb_text, *tb_pixmap_text; + gint tb_pixmap_width; + gint tb_offset; + gboolean tb_scroll_allowed, tb_scroll_enabled; + gboolean tb_is_scrollable, tb_is_dragging; + gint tb_timeout_tag, tb_drag_x, tb_drag_off; + gint tb_nominal_y, tb_nominal_height; + gint tb_skin_id; + SkinPixmapId tb_skin_index; + PangoFontDescription *tb_font; + gint tb_font_ascent, tb_font_descent; + gchar *tb_fontname; +}; + +typedef struct _TextBox TextBox; + +void textbox_set_text(TextBox * tb, const gchar * text); +void textbox_set_scroll(TextBox * tb, gboolean s); +TextBox *create_textbox(GList ** wlist, GdkPixmap * parent, GdkGC * gc, + gint x, gint y, gint w, gboolean allow_scroll, + SkinPixmapId si); +void textbox_set_xfont(TextBox * tb, gboolean use_xfont, + const gchar * fontname); +void textbox_free(TextBox * tb); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/vis.c Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,291 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "widgetcore.h" + +#include <glib.h> +#include <gdk/gdk.h> +#include <string.h> + +#include "main.h" +#include "skin.h" +#include "widget.h" + +static const gfloat vis_afalloff_speeds[] = { 0.34, 0.5, 1.0, 1.3, 1.6 }; +static const gfloat vis_pfalloff_speeds[] = { 1.2, 1.3, 1.4, 1.5, 1.6 }; +static const gint vis_redraw_delays[] = { 1, 2, 4, 8 }; +static const guint8 vis_scope_colors[] = + { 21, 21, 20, 20, 19, 19, 18, 19, 19, 20, 20, 21, 21 }; + +void +vis_timeout_func(Vis * vis, guchar * data) +{ + static GTimer *timer = NULL; + gulong micros = 9999999; + gboolean falloff = FALSE; + gint i; + + if (!timer) { + timer = g_timer_new(); + g_timer_start(timer); + } + else { + g_timer_elapsed(timer, µs); + if (micros > 14000) + g_timer_reset(timer); + + } + + if (cfg.vis_type == VIS_ANALYZER) { + if (micros > 14000) + falloff = TRUE; + if (data || falloff) { + for (i = 0; i < 75; i++) { + if (data && data[i] > vis->vs_data[i]) { + vis->vs_data[i] = data[i]; + if (vis->vs_data[i] > vis->vs_peak[i]) { + vis->vs_peak[i] = vis->vs_data[i]; + vis->vs_peak_speed[i] = 0.01; + + } + else if (vis->vs_peak[i] > 0.0) { + vis->vs_peak[i] -= vis->vs_peak_speed[i]; + vis->vs_peak_speed[i] *= + vis_pfalloff_speeds[cfg.peaks_falloff]; + if (vis->vs_peak[i] < vis->vs_data[i]) + vis->vs_peak[i] = vis->vs_data[i]; + if (vis->vs_peak[i] < 0.0) + vis->vs_peak[i] = 0.0; + } + } + else if (falloff) { + if (vis->vs_data[i] > 0.0) { + vis->vs_data[i] -= + vis_afalloff_speeds[cfg.analyzer_falloff]; + if (vis->vs_data[i] < 0.0) + vis->vs_data[i] = 0.0; + } + if (vis->vs_peak[i] > 0.0) { + vis->vs_peak[i] -= vis->vs_peak_speed[i]; + vis->vs_peak_speed[i] *= + vis_pfalloff_speeds[cfg.peaks_falloff]; + if (vis->vs_peak[i] < vis->vs_data[i]) + vis->vs_peak[i] = vis->vs_data[i]; + if (vis->vs_peak[i] < 0.0) + vis->vs_peak[i] = 0.0; + } + } + } + } + } + else if (data) { + for (i = 0; i < 75; i++) + vis->vs_data[i] = data[i]; + } + + if (micros > 14000) { + if (!vis->vs_refresh_delay) { + vis_draw((Widget *) vis); + vis->vs_refresh_delay = vis_redraw_delays[cfg.vis_refresh]; + + } + vis->vs_refresh_delay--; + } +} + +void +vis_draw(Widget * w) +{ + Vis *vis = (Vis *) w; + gint x, y, h = 0, h2; + guchar vis_color[24][3]; + guchar rgb_data[152 * 32], *ptr, c; + guint32 colors[24]; + GdkRgbCmap *cmap; + + if (!vis->vs_widget.visible) + return; + + skin_get_viscolor(bmp_active_skin, vis_color); + for (y = 0; y < 24; y++) { + colors[y] = + vis_color[y][0] << 16 | vis_color[y][1] << 8 | vis_color[y][2]; + } + cmap = gdk_rgb_cmap_new(colors, 24); + + memset(rgb_data, 0, 76 * 16); + for (y = 1; y < 16; y += 2) { + ptr = rgb_data + (y * 76); + for (x = 0; x < 76; x += 2, ptr += 2) + *ptr = 1; + } + if (cfg.vis_type == VIS_ANALYZER) { + for (x = 0; x < 75; x++) { + if (cfg.analyzer_type == ANALYZER_BARS && (x % 4) == 0) + h = vis->vs_data[x >> 2]; + else if (cfg.analyzer_type == ANALYZER_LINES) + h = vis->vs_data[x]; + if (h && (cfg.analyzer_type == ANALYZER_LINES || + (x % 4) != 3)) { + ptr = rgb_data + ((16 - h) * 76) + x; + switch (cfg.analyzer_mode) { + case ANALYZER_NORMAL: + for (y = 0; y < h; y++, ptr += 76) + *ptr = 18 - h + y; + break; + case ANALYZER_FIRE: + for (y = 0; y < h; y++, ptr += 76) + *ptr = y + 2; + break; + case ANALYZER_VLINES: + for (y = 0; y < h; y++, ptr += 76) + *ptr = 18 - h; + break; + } + } + } + if (cfg.analyzer_peaks) { + for (x = 0; x < 75; x++) { + if (cfg.analyzer_type == ANALYZER_BARS && (x % 4) == 0) + h = vis->vs_peak[x >> 2]; + else if (cfg.analyzer_type == ANALYZER_LINES) + h = vis->vs_peak[x]; + if (h + && (cfg.analyzer_type == ANALYZER_LINES + || (x % 4) != 3)) + rgb_data[(16 - h) * 76 + x] = 23; + } + } + } + else if (cfg.vis_type == VIS_SCOPE) { + for (x = 0; x < 75; x++) { + switch (cfg.scope_mode) { + case SCOPE_DOT: + h = vis->vs_data[x]; + ptr = rgb_data + ((15 - h) * 76) + x; + *ptr = vis_scope_colors[h]; + break; + case SCOPE_LINE: + if (x != 74) { + h = 15 - vis->vs_data[x]; + h2 = 15 - vis->vs_data[x + 1]; + if (h > h2) { + y = h; + h = h2; + h2 = y; + } + ptr = rgb_data + (h * 76) + x; + for (y = h; y <= h2; y++, ptr += 76) + *ptr = vis_scope_colors[y - 3]; + + } + else { + h = 15 - vis->vs_data[x]; + ptr = rgb_data + (h * 76) + x; + *ptr = vis_scope_colors[h]; + } + break; + case SCOPE_SOLID: + h = 15 - vis->vs_data[x]; + h2 = 9; + c = vis_scope_colors[(gint) vis->vs_data[x]]; + if (h > h2) { + y = h; + h = h2; + h2 = y; + } + ptr = rgb_data + (h * 76) + x; + for (y = h; y <= h2; y++, ptr += 76) + *ptr = c; + break; + } + } + } + + /* FIXME: The check "shouldn't" be neccessary? */ + /* if (GTK_IS_WINDOW(vis->vs_window)) { */ + GDK_THREADS_ENTER(); + gdk_draw_indexed_image(vis->vs_window, vis->vs_widget.gc, + vis->vs_widget.x, vis->vs_widget.y, + vis->vs_widget.width, vis->vs_widget.height, + GDK_RGB_DITHER_NORMAL, (guchar *) rgb_data, + 76, cmap); + GDK_THREADS_LEAVE(); + /* } else { + vis->vs_window = mainwin->window; + GDK_THREADS_ENTER(); + gdk_draw_indexed_image(vis->vs_window, vis->vs_widget.gc, + vis->vs_widget.x, vis->vs_widget.y, + vis->vs_widget.width, vis->vs_widget.height, + GDK_RGB_DITHER_NORMAL, (guchar *) rgb_data, + 76, cmap); + GDK_THREADS_LEAVE(); + } + */ + + gdk_rgb_cmap_free(cmap); +} + +void +vis_clear_data(Vis * vis) +{ + gint i; + + if (!vis) + return; + + for (i = 0; i < 75; i++) { + vis->vs_data[i] = (cfg.vis_type == VIS_SCOPE) ? 6 : 0; + vis->vs_peak[i] = 0; + } +} + +void +vis_clear(Vis * vis) +{ + gdk_window_clear_area(vis->vs_window, vis->vs_widget.x, + vis->vs_widget.y, vis->vs_widget.width, + vis->vs_widget.height); +} + +void +vis_set_window(Vis * vis, GdkWindow * window) +{ + vis->vs_window = window; +} + +Vis * +create_vis(GList ** wlist, + GdkPixmap * parent, + GdkWindow * window, + GdkGC * gc, + gint x, gint y, + gint width) +{ + Vis *vis; + + vis = g_new0(Vis, 1); + + widget_init(&vis->vs_widget, parent, gc, x, y, width, 16, 1); + widget_list_add(wlist, WIDGET(vis)); + + return vis; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/vis.h Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,82 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _WIDGETCORE_H_ +#error Please do not include me directly! Use widgetcore.h instead! +#endif + +#ifndef VIS_H +#define VIS_H + +#include <glib.h> +#include <gdk/gdk.h> + +#include "widget.h" + +typedef enum { + VIS_ANALYZER, VIS_SCOPE, VIS_OFF +} VisType; + +typedef enum { + ANALYZER_NORMAL, ANALYZER_FIRE, ANALYZER_VLINES +} AnalyzerMode; + +typedef enum { + ANALYZER_LINES, ANALYZER_BARS +} AnalyzerType; + +typedef enum { + SCOPE_DOT, SCOPE_LINE, SCOPE_SOLID +} ScopeMode; + +typedef enum { + VU_NORMAL, VU_SMOOTH +} VUMode; + +typedef enum { + REFRESH_FULL, REFRESH_HALF, REFRESH_QUARTER, REFRESH_EIGTH +} RefreshRate; + +typedef enum { + FALLOFF_SLOWEST, FALLOFF_SLOW, FALLOFF_MEDIUM, FALLOFF_FAST, + FALLOFF_FASTEST +} FalloffSpeed; + +#define VIS(x) ((Vis *)(x)) +struct _Vis { + Widget vs_widget; + GdkWindow *vs_window; + gfloat vs_data[75], vs_peak[75], vs_peak_speed[75]; + gint vs_refresh_delay; +}; + +typedef struct _Vis Vis; + +void vis_draw(Widget * w); + +Vis *create_vis(GList ** wlist, GdkPixmap * parent, GdkWindow * window, + GdkGC * gc, gint x, gint y, gint width); +void vis_timeout_func(Vis * vis, guchar * data); +void vis_clear_data(Vis * vis); +void vis_clear(Vis * vis); +void vis_set_window(Vis * vis, GdkWindow * window); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/widget.c Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,263 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "widgetcore.h" + +#include <glib.h> +#include <gdk/gdk.h> + +#include "debug.h" + + +void +widget_init(Widget * widget, GdkPixmap * parent, GdkGC * gc, + gint x, gint y, gint width, gint height, gint visible) +{ + widget->parent = parent; + widget->gc = gc; + widget_set_position(widget, x, y); + widget_set_size(widget, width, height); + widget->visible = visible; + widget->redraw = TRUE; + widget->mutex = g_mutex_new(); +} + +void +widget_set_position(Widget * widget, gint x, gint y) +{ + widget->x = x; + widget->y = y; + widget_queue_redraw(widget); +} + +void +widget_set_size(Widget * widget, gint width, gint height) +{ + widget->width = width; + widget->height = height; + widget_queue_redraw(widget); +} + +void +widget_queue_redraw(Widget * widget) +{ + widget->redraw = TRUE; +} + +gboolean +widget_contains(Widget * widget, gint x, gint y) +{ + return (widget->visible && + x >= widget->x && + y >= widget->y && + x < widget->x + widget->width && + y < widget->y + widget->height); +} + +void +widget_show(Widget * widget) +{ + if (!widget) + return; + + widget->visible = TRUE; + widget_draw(widget); +} + +void +widget_hide(Widget * widget) +{ + if (!widget) + return; + + widget->visible = FALSE; +} + +gboolean +widget_is_visible(Widget * widget) +{ + if (!widget) + return FALSE; + + return widget->visible; +} + +void +widget_resize(Widget * widget, gint width, gint height) +{ + widget_set_size(widget, width, height); +} + +void +widget_move(Widget * widget, gint x, gint y) +{ + widget_lock(widget); + widget_set_position(widget, x, y); + widget_unlock(widget); +} + +void +widget_draw(Widget * widget) +{ + widget_lock(widget); + WIDGET(widget)->redraw = TRUE; + widget_unlock(widget); +} + +void +widget_draw_quick(Widget * widget) +{ + widget_lock(widget); + if (WIDGET(widget)->draw != NULL) + { + WIDGET(widget)->draw(widget); + gdk_flush(); + } + widget_unlock(widget); +} + +void +widget_list_add(GList ** list, Widget * widget) +{ + (*list) = g_list_append(*list, widget); +} + +void +handle_press_cb(GList * widget_list, GtkWidget * widget, + GdkEventButton * event) +{ + GList *wl; + + for (wl = widget_list; wl; wl = g_list_next(wl)) { + if (WIDGET(wl->data)->button_press_cb) + WIDGET(wl->data)->button_press_cb(widget, event, wl->data); + } +} + +void +handle_release_cb(GList * widget_list, GtkWidget * widget, + GdkEventButton * event) +{ + GList *wl; + + for (wl = widget_list; wl; wl = g_list_next(wl)) { + if (WIDGET(wl->data)->button_release_cb) + WIDGET(wl->data)->button_release_cb(widget, event, wl->data); + } +} + +void +handle_motion_cb(GList * widget_list, GtkWidget * widget, + GdkEventMotion * event) +{ + GList *wl; + + for (wl = widget_list; wl; wl = g_list_next(wl)) { + if (WIDGET(wl->data)->motion_cb) + WIDGET(wl->data)->motion_cb(widget, event, wl->data); + } +} + +void +handle_scroll_cb(GList * wlist, GtkWidget * widget, GdkEventScroll * event) +{ + GList *wl; + + for (wl = wlist; wl; wl = g_list_next(wl)) { + if (WIDGET(wl->data)->mouse_scroll_cb) + WIDGET(wl->data)->mouse_scroll_cb(widget, event, wl->data); + } +} + +void +widget_list_draw(GList * widget_list, gboolean * redraw, gboolean force) +{ + GList *wl; + Widget *w; + + *redraw = FALSE; + wl = widget_list; + + for (wl = widget_list; wl; wl = g_list_next(wl)) { + w = WIDGET(wl->data); + + REQUIRE_LOCK(w->mutex); + + if (!w->draw) + continue; + + if (!w->visible) + continue; + + if (w->redraw || force) { + w->draw(w); +/* w->redraw = FALSE; */ + *redraw = TRUE; + } + } +} + +void +widget_list_change_pixmap(GList * widget_list, GdkPixmap * pixmap) +{ + GList *wl; + + for (wl = widget_list; wl; wl = g_list_next(wl)) { + Widget *widget = wl->data; + widget->parent = pixmap; + widget_queue_redraw(widget); + } +} + +void +widget_list_clear_redraw(GList * widget_list) +{ + GList *wl; + + for (wl = widget_list; wl; wl = g_list_next(wl)) { + REQUIRE_LOCK(WIDGET(wl->data)->mutex); + WIDGET(wl->data)->redraw = FALSE; + } +} + +void +widget_lock(Widget * widget) +{ + g_mutex_lock(WIDGET(widget)->mutex); +} + +void +widget_unlock(Widget * widget) +{ + g_mutex_unlock(WIDGET(widget)->mutex); +} + +void +widget_list_lock(GList * widget_list) +{ + g_list_foreach(widget_list, (GFunc) widget_lock, NULL); +} + +void +widget_list_unlock(GList * widget_list) +{ + g_list_foreach(widget_list, (GFunc) widget_unlock, NULL); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/widget.h Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,107 @@ +/* BMP - Cross-platform multimedia player + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _WIDGETCORE_H_ +#error Please do not include me directly! Use widgetcore.h instead! +#endif + +#ifndef WIDGET_H +#define WIDGET_H + + +#include <glib.h> +#include <gdk/gdk.h> +#include <gtk/gtk.h> + + +typedef struct _Widget Widget; + + +typedef void (*WidgetButtonPressFunc) (GtkWidget *, GdkEventButton *, + gpointer); +typedef void (*WidgetButtonReleaseFunc) (GtkWidget *, GdkEventButton *, + gpointer); +typedef void (*WidgetMotionFunc) (GtkWidget *, GdkEventMotion *, gpointer); +typedef void (*WidgetDrawFunc) (Widget *); +typedef void (*WidgetScrollFunc) (GtkWidget *, GdkEventScroll *, gpointer); + + +#define WIDGET(x) ((Widget *)(x)) +struct _Widget { + GdkPixmap *parent; + GdkGC *gc; + + gint x, y; + gint width, height; + + gint visible; + gboolean redraw; + + GMutex *mutex; + + WidgetButtonPressFunc button_press_cb; + WidgetButtonReleaseFunc button_release_cb; + WidgetMotionFunc motion_cb; + WidgetDrawFunc draw; + WidgetScrollFunc mouse_scroll_cb; +}; + + +void widget_init(Widget * widget, GdkPixmap * parent, GdkGC * gc, + gint x, gint y, gint width, gint height, gint visible); + +void widget_set_position(Widget * widget, gint x, gint y); +void widget_set_size(Widget * widget, gint width, gint height); +void widget_queue_redraw(Widget * widget); + +void widget_lock(Widget * widget); +void widget_unlock(Widget * widget); + +gboolean widget_contains(Widget * widget, gint x, gint y); + +void widget_show(Widget * widget); +void widget_hide(Widget * widget); +gboolean widget_is_visible(Widget * widget); + +void widget_resize(Widget * widget, gint width, gint height); +void widget_move(Widget * widget, gint x, gint y); +void widget_draw(Widget * widget); +void widget_draw_quick(Widget * widget); + +void handle_press_cb(GList * wlist, GtkWidget * widget, + GdkEventButton * event); +void handle_release_cb(GList * wlist, GtkWidget * widget, + GdkEventButton * event); +void handle_motion_cb(GList * wlist, GtkWidget * widget, + GdkEventMotion * event); +void handle_scroll_cb(GList * wlist, GtkWidget * widget, + GdkEventScroll * event); + +void widget_list_add(GList ** list, Widget * widget); +void widget_list_draw(GList * list, gboolean * redraw, gboolean force); +void widget_list_change_pixmap(GList * list, GdkPixmap * pixmap); +void widget_list_clear_redraw(GList * list); +void widget_list_lock(GList * list); +void widget_list_unlock(GList * list); + + + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/audacious/widgets/widgetcore.h Thu Sep 07 22:26:54 2006 -0700 @@ -0,0 +1,41 @@ +/* + * Audacious - a cross-platform multimedia player + * Copyright (c) 2006 Audacious development team. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef _WIDGETCORE_H_ +#define _WIDGETCORE_H_ + +#include "tbutton.h" +#include "eq_graph.h" +#include "eq_slider.h" +#include "hslider.h" +#include "menurow.h" +#include "monostereo.h" +#include "number.h" +#include "pbutton.h" +#include "playlist_list.h" +#include "playlist_slider.h" +#include "playstatus.h" +#include "sbutton.h" +#include "skin.h" +#include "svis.h" +#include "textbox.h" +#include "vis.h" +#include "widget.h" + +#endif