/*                                                     -*- linux-c -*-
    Copyright (C) 2004 Tom Szilagyi

    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., 675 Mass Ave, Cambridge, MA 02139, USA.

    $Id: gui_main.c 1159 2011-05-30 12:29:49Z tszilagyi $
*/

#include <config.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <math.h>
#include <sys/stat.h>
#include <errno.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>

#ifdef _WIN32
#include <glib.h>
#else
#include <pthread.h>
#endif /* _WIN32 */

#ifdef HAVE_LADSPA
#include <lrdf.h>
#endif /* HAVE_LADSPA */

#ifdef HAVE_SRC
#include <samplerate.h>
#endif /* HAVE_SRC */

#include "ext_lua.h"
#include "common.h"
#include "utils.h"
#include "utils_gui.h"
#include "core.h"
#include "rb.h"
#include "cover.h"
#include "transceiver.h"
#include "decoder/file_decoder.h"
#include "about.h"
#include "options.h"
#include "skin.h"
#include "ports.h"
#include "music_browser.h"
#include "store_file.h"
#include "store_podcast.h"
#include "playlist.h"
#include "plugin.h"
#include "file_info.h"
#include "i18n.h"
#include "cdda.h"
#include "store_cdda.h"
#include "loop_bar.h"
#include "httpc.h"
#include "metadata.h"
#include "gui_main.h"
#include "version.h"

/* receive at most this much remote messages in one run of timeout_callback() */
#define MAX_RCV_COUNT 32

/* period of main timeout callback [ms] */
#define TIMEOUT_PERIOD 100

extern options_t options;

char pl_color_active[14];
char pl_color_inactive[14];

PangoFontDescription *fd_playlist;
PangoFontDescription *fd_browser;
PangoFontDescription *fd_bigtimer;
PangoFontDescription *fd_smalltimer;
PangoFontDescription *fd_songtitle;
PangoFontDescription *fd_songinfo;
PangoFontDescription *fd_statusbar;

/* Communication between gui thread and disk thread */
extern AQUALUNG_MUTEX_DECLARE(disk_thread_lock)
extern AQUALUNG_COND_DECLARE(disk_thread_wake)
extern rb_t * rb_gui2disk;
extern rb_t * rb_disk2gui;

#ifdef HAVE_JACK
extern jack_client_t * jack_client;
extern char * client_name;
extern int jack_is_shutdown;
extern char * jack_shutdown_reason;
#endif /* HAVE_JACK */

extern int aqualung_socket_fd;
extern int aqualung_session_id;

extern GtkTreeStore * music_store;
extern GtkListStore * running_store;

extern GtkWidget * plist_menu;
extern GtkWidget * playlist_notebook;

void init_plist_menu(GtkWidget *append_menu);

/* the physical name of the file that is playing, or a '\0'. */
char current_file[MAXLEN];

/* embedded picture in the file that is playing */
void * embedded_picture = NULL;
int embedded_picture_size = 0;

/* default window title */
char win_title[MAXLEN];

/* current application title when a file is playing */
char playing_app_title[MAXLEN];

char send_cmd, recv_cmd;
char command[RB_CONTROL_SIZE];
fileinfo_t fileinfo;
status_t status;
unsigned long out_SR;
extern int output;
unsigned long rb_size;
unsigned long long total_samples;
unsigned long long sample_pos;

/* this flag set to 1 in core.c if --play
   for current instance is specified. */
int immediate_start = 0;

/* Whether to stop playing after currently played song ends. */
int stop_after_current_song = 0;

/* Whether the systray is used in this instance */
int systray_used = 0;

/* the tab to load remote files */
char * tab_name;

/* volume & balance sliders */
double vol_prev = 0.0f;
double vol_lin = 1.0f;
double bal_prev = 0.0f;
extern double left_gain;
extern double right_gain;

/* label/display data */
fileinfo_t disp_info;
unsigned long disp_samples;
unsigned long disp_pos;

GtkWidget * main_window;

extern GtkWidget * browser_window;
extern GtkWidget * playlist_window;
extern GtkWidget * fxbuilder_window;
extern GtkWidget * ports_window;
extern GtkWidget * vol_window;
extern GtkWidget * browser_paned;

extern int music_store_changed;

extern int fxbuilder_on;

GtkObject * adj_pos;
GtkObject * adj_vol;
GtkObject * adj_bal;
GtkWidget * scale_pos;
GtkWidget * scale_vol;
GtkWidget * scale_bal;

GtkWidget * vbox_sep;

#ifdef HAVE_LOOP
GtkWidget * loop_bar;
#endif /* HAVE_LOOP */

GtkWidget * time_labels[3];
int refresh_time_label = 1;


gulong play_id;
gulong pause_id;

GtkWidget * play_button;
GtkWidget * pause_button;
GtkWidget * prev_button;
GtkWidget * stop_button;
GtkWidget * next_button;
GtkWidget * repeat_button;
GtkWidget * repeat_all_button;
GtkWidget * shuffle_button;

GtkWidget * label_title;
GtkWidget * label_format;
GtkWidget * label_samplerate;
GtkWidget * label_bps;
GtkWidget * label_mono;
GtkWidget * label_output;
GtkWidget * label_src_type;

int x_scroll_start;
int x_scroll_pos;
int scroll_btn;

GtkWidget * plugin_toggle;
GtkWidget * musicstore_toggle;
GtkWidget * playlist_toggle;

guint timeout_tag;
guint vol_bal_timeout_tag = 0;

gint timeout_callback(gpointer data);

/* whether we are refreshing the scale on STATUS commands recv'd from disk thread */
int refresh_scale = 1;
/* suppress scale refreshing after seeking (discard this much STATUS packets).
   Prevents position slider to momentarily jump back to original position. */
int refresh_scale_suppress = 0;

/* whether we allow seeks (depending on if we are at the end of the track) */
int allow_seeks = 1;

/* controls when to load the new file's data on display */
int fresh_new_file = 1;
int fresh_new_file_prev = 1;

/* whether we have a file loaded, that is currently playing (or paused) */
int is_file_loaded = 0;

/* whether playback is paused */
int is_paused = 0;

/* Whether the next key binding should be for a custom key binding */
int custom_main_keybinding_expected = 0;

/* popup menu for configuration */
GtkWidget * conf_menu;
GtkWidget * conf__options;
GtkWidget * conf__skin;
GtkWidget * conf__jack;
GtkWidget * conf__fileinfo;
GtkWidget * conf__about;
GtkWidget * conf__quit;

GtkWidget * bigtimer_label;
GtkWidget * smalltimer_label_1;
GtkWidget * smalltimer_label_2;

/* systray stuff */
#ifdef HAVE_SYSTRAY

GtkStatusIcon * systray_icon;

GtkWidget * systray_menu;
GtkWidget * systray__show;
GtkWidget * systray__hide;
GtkWidget * systray__play;
GtkWidget * systray__pause;
GtkWidget * systray__stop;
GtkWidget * systray__prev;
GtkWidget * systray__next;
GtkWidget * systray__quit;

int wm_not_systray_capable = 0;

void hide_all_windows(gpointer data);

#if (GTK_CHECK_VERSION(2,15,0))

/* Used for not reacting too quickly to consecutive mouse wheel events */
guint32 last_systray_scroll_event_time = 0;

#endif /* GTK_CHECK_VERSION */

#endif /* HAVE_SYSTRAY */

int systray_main_window_on = 1;

void create_main_window(char * skin_path);

void toggle_noeffect(int id, int state);

gint prev_event(GtkWidget * widget, GdkEvent * event, gpointer data);
gint play_event(GtkWidget * widget, GdkEvent * event, gpointer data);
gint pause_event(GtkWidget * widget, GdkEvent * event, gpointer data);
gint stop_event(GtkWidget * widget, GdkEvent * event, gpointer data);
gint next_event(GtkWidget * widget, GdkEvent * event, gpointer data);

void load_config(void);

void playlist_toggled(GtkWidget * widget, gpointer data);

GtkWidget * cover_align;
GtkWidget * c_event_box;
GtkWidget * cover_image_area;
gint cover_show_flag;

extern char fileinfo_name[MAXLEN];
extern char fileinfo_file[MAXLEN];

extern GtkWidget * plist__fileinfo;
extern GtkWidget * plist__rva;

extern gint playlist_state;
extern gint browser_state;


void
try_waking_disk_thread(void) {

	if (AQUALUNG_MUTEX_TRYLOCK(disk_thread_lock)) {
		AQUALUNG_COND_SIGNAL(disk_thread_wake)
		AQUALUNG_MUTEX_UNLOCK(disk_thread_lock)
	}
}


void
set_title_label(char * str) {

	gchar default_title[MAXLEN];
        char tmp[MAXLEN];

	tmp[0] = '\0';

	if (is_file_loaded) {
		/* Remember current title. */
		strncpy(playing_app_title, str, MAXLEN-1);

		gtk_label_set_text(GTK_LABEL(label_title), str);
		if (options.show_sn_title) {
			if (stop_after_current_song) {
				strncat(tmp, "[", MAXLEN-1);
				strncat(tmp, _("STOPPING"), MAXLEN-1);
				strncat(tmp, "] ", MAXLEN-1);
			}
			strncat(tmp, str, MAXLEN-1);
			strncat(tmp, " - ", MAXLEN-1);
			strncat(tmp, win_title, MAXLEN-1);
			gtk_window_set_title(GTK_WINDOW(main_window), tmp);
#ifdef HAVE_SYSTRAY
			if (systray_used) {
				aqualung_status_icon_set_tooltip_text(systray_icon, tmp);
			}
#endif /* HAVE_SYSTRAY */
		} else {
			gtk_window_set_title(GTK_WINDOW(main_window), win_title);
#ifdef HAVE_SYSTRAY
			if (systray_used) {
				aqualung_status_icon_set_tooltip_text(systray_icon, win_title);
			}
#endif /* HAVE_SYSTRAY */
		}
	} else {
		sprintf(default_title, "Aqualung %s", AQUALUNG_VERSION);
		gtk_label_set_text(GTK_LABEL(label_title), default_title);
		gtk_window_set_title(GTK_WINDOW(main_window), win_title);
#ifdef HAVE_SYSTRAY
		if (systray_used) {
			aqualung_status_icon_set_tooltip_text(systray_icon, win_title);
		}
#endif /* HAVE_SYSTRAY */
        }
}

void
hide_cover_thumbnail(void) {
        cover_show_flag = 0;
        gtk_widget_hide(cover_image_area);
        gtk_widget_hide(c_event_box);
        gtk_widget_hide(cover_align);
}

void
set_format_label(char * format_str) {

	if (!is_file_loaded) {
		gtk_label_set_text(GTK_LABEL(label_format), "");
	} else {
		gtk_label_set_text(GTK_LABEL(label_format), format_str);
	}
}


void
format_bps_label(int bps, int format_flags, char * str) {

	if (bps == 0) {
		strcpy(str, "N/A kbit/s");
		return;
	}

	if (format_flags & FORMAT_VBR) {
		sprintf(str, "%.1f kbit/s VBR", bps/1000.0);
	} else {
		if (format_flags & FORMAT_UBR) {
			sprintf(str, "%.1f kbit/s UBR", bps/1000.0);
		} else {
			sprintf(str, "%.1f kbit/s", bps/1000.0);
		}
	}
}

void
set_bps_label(int bps, int format_flags) {

	char str[MAXLEN];

	format_bps_label(bps, format_flags, str);

	if (is_file_loaded) {
		gtk_label_set_text(GTK_LABEL(label_bps), str);
	} else {
		gtk_label_set_text(GTK_LABEL(label_bps), "");
	}
}


void
set_samplerate_label(int sr) {

	char str[MAXLEN];

	snprintf(str, MAXLEN-1, "%d Hz", sr);

	if (is_file_loaded) {
		gtk_label_set_text(GTK_LABEL(label_samplerate), str);
	} else {
		gtk_label_set_text(GTK_LABEL(label_samplerate), "");
	}
}


void
set_mono_label(int is_mono) {

	if (is_file_loaded) {
		if (is_mono) {
			gtk_label_set_text(GTK_LABEL(label_mono), _("MONO"));
		} else {
			gtk_label_set_text(GTK_LABEL(label_mono), _("STEREO"));
		}
	} else {
		gtk_label_set_text(GTK_LABEL(label_mono), "");
	}
}



void
set_output_label(int output, int out_SR) {

	char str[MAXLEN];

	switch (output) {
#ifdef HAVE_PULSE
	case PULSE_DRIVER:
		snprintf(str, MAXLEN-1, "%s PulseAudio @ %d Hz", _("Output:"), out_SR);
		break;
#endif /* HAVE_PULSE */
#ifdef HAVE_SNDIO
	case SNDIO_DRIVER:
		snprintf(str, MAXLEN-1, "%s sndio @ %d Hz", _("Output:"), out_SR);
		break;
#endif /* HAVE_SNDIO */
#ifdef HAVE_OSS
	case OSS_DRIVER:
		snprintf(str, MAXLEN-1, "%s OSS @ %d Hz", _("Output:"), out_SR);
		break;
#endif /* HAVE_OSS */
#ifdef HAVE_ALSA
	case ALSA_DRIVER:
		snprintf(str, MAXLEN-1, "%s ALSA @ %d Hz", _("Output:"), out_SR);
		break;
#endif /* HAVE_ALSA */
#ifdef HAVE_JACK
	case JACK_DRIVER:
		snprintf(str, MAXLEN-1, "%s JACK @ %d Hz", _("Output:"), out_SR);
		break;
#endif /* HAVE_JACK */
#ifdef _WIN32
	case WIN32_DRIVER:
		snprintf(str, MAXLEN-1, "%s Win32 @ %d Hz", _("Output:"), out_SR);
		break;
#endif /* _WIN32 */
	default:
		strncpy(str, _("No output"), MAXLEN-1);
		break;
	}

	gtk_label_set_text(GTK_LABEL(label_output), str);
}



void
set_src_type_label(int src_type) {

	char str[MAXLEN];

	strcpy(str, _("SRC Type: "));
#ifdef HAVE_SRC
	strcat(str, src_get_name(src_type));
#else
	strcat(str, _("None"));
#endif /* HAVE_SRC */

	gtk_label_set_text(GTK_LABEL(label_src_type), str);
}


void
refresh_time_displays(void) {

	char str[MAXLEN];

	if (is_file_loaded) {
		if (refresh_time_label || options.time_idx[0] != 0) {
			sample2time(disp_info.sample_rate, disp_pos, str, 0);
			gtk_label_set_text(GTK_LABEL(time_labels[0]), str);

		}

		if (refresh_time_label || options.time_idx[0] != 1) {
			if (disp_samples == 0) {
				strcpy(str, " N/A ");
			} else {
				sample2time(disp_info.sample_rate, disp_samples - disp_pos, str, 1);
			}
			gtk_label_set_text(GTK_LABEL(time_labels[1]), str);

		}

		if (refresh_time_label || options.time_idx[0] != 2) {
			if (disp_samples == 0) {
				strcpy(str, " N/A ");
			} else {
				sample2time(disp_info.sample_rate, disp_samples, str, 0);
			}
			gtk_label_set_text(GTK_LABEL(time_labels[2]), str);

		}
	} else {
		int i;
		for (i = 0; i < 3; i++) {
			gtk_label_set_text(GTK_LABEL(time_labels[i]), " 00:00 ");
		}
	}
}

#ifdef HAVE_LOOP

void
loop_bar_update_tooltip(void) {

        if (options.enable_tooltips) {
		char str[MAXLEN];

		if (is_file_loaded) {
			char start[32];
			char end[32];
			sample2time(disp_info.sample_rate, total_samples * options.loop_range_start, start, 0);
			sample2time(disp_info.sample_rate, total_samples * options.loop_range_end, end, 0);
			snprintf(str, MAXLEN-1, _("Loop range: %d-%d%% [%s - %s]"),
				 (int)(100 * options.loop_range_start),
				 (int)(100 * options.loop_range_end),
				 start, end);
		} else {
			snprintf(str, MAXLEN-1, _("Loop range: %d-%d%%"),
				 (int)(100 * options.loop_range_start),
				 (int)(100 * options.loop_range_end));
		}

                aqualung_widget_set_tooltip_text(loop_bar, str);
        }
}

void
loop_range_changed_cb(AqualungLoopBar * bar, float start, float end, gpointer data) {

	options.loop_range_start = start;
	options.loop_range_end = end;
	loop_bar_update_tooltip();
}

#endif /* HAVE_LOOP */

void
refresh_displays(void) {

	GtkTreePath * p;
	GtkTreeIter iter;
	playlist_t * pl;

	refresh_time_displays();

#ifdef HAVE_LOOP
	loop_bar_update_tooltip();
#endif /* HAVE_LOOP */

	set_format_label(disp_info.format_str);
	set_samplerate_label(disp_info.sample_rate);
	set_bps_label(disp_info.bps, disp_info.format_flags);
	set_mono_label(disp_info.is_mono);
	set_output_label(output, out_SR);
	set_src_type_label(options.src_type);

	if ((pl = playlist_get_playing()) == NULL) {
		if ((pl = playlist_get_current()) == NULL) {
			set_title_label("");
                        hide_cover_thumbnail();
			return;
		}
	}

	p = playlist_get_playing_path(pl);
	if (p != NULL) {
		playlist_data_t * pldata;

		gtk_tree_model_get_iter(GTK_TREE_MODEL(pl->store), &iter, p);
		gtk_tree_model_get(GTK_TREE_MODEL(pl->store), &iter, PL_COL_DATA, &pldata, -1);
		gtk_tree_path_free(p);

		if (!httpc_is_url(pldata->file) && !options.use_ext_title_format) {
			char list_str[MAXLEN];
			playlist_data_get_display_name(list_str, pldata);
			set_title_label(list_str);
		} else if (!is_file_loaded) {
			char * name;
			gtk_tree_model_get(GTK_TREE_MODEL(pl->store), &iter, PL_COL_NAME, &name, -1);
			set_title_label(name);
			g_free(name);
		}

		if (is_file_loaded) {
                        if (!options.dont_show_cover) {
				if (embedded_picture != NULL && (find_cover_filename(pldata->file) == NULL || !options.use_external_cover_first)) {
					display_cover_from_binary(cover_image_area, c_event_box, cover_align, 48, 48,
						      embedded_picture, embedded_picture_size, TRUE, TRUE);
				} else {
					if (options.show_cover_for_ms_tracks_only) {
						if (IS_PL_COVER(pldata)) {
							display_cover(cover_image_area, c_event_box, cover_align,
								      48, 48, pldata->file, TRUE, TRUE);
						} else {
							hide_cover_thumbnail();
						}
					} else {
						display_cover(cover_image_area, c_event_box, cover_align,
							      48, 48, pldata->file, TRUE, TRUE);
					}
				}
                        }
		}
	} else if (!is_file_loaded) {
		set_title_label("");
                hide_cover_thumbnail();
	}
}


void
zero_displays(void) {

	disp_info.total_samples = 0;
	disp_info.sample_rate = 0;
	disp_info.channels = 2;
	disp_info.is_mono = 0;
	disp_info.bps = 0;

	disp_samples = 0;
	disp_pos = 0;

	refresh_displays();
}


void
save_window_position(void) {

	gtk_window_get_position(GTK_WINDOW(main_window), &options.main_pos_x, &options.main_pos_y);

	if (!options.playlist_is_embedded && options.playlist_on) {
		gtk_window_get_position(GTK_WINDOW(playlist_window), &options.playlist_pos_x, &options.playlist_pos_y);
	}

	if (options.browser_on) {
		gtk_window_get_position(GTK_WINDOW(browser_window), &options.browser_pos_x, &options.browser_pos_y);
	}

	gtk_window_get_size(GTK_WINDOW(main_window), &options.main_size_x, &options.main_size_y);
	gtk_window_get_size(GTK_WINDOW(browser_window), &options.browser_size_x, &options.browser_size_y);

	if (!options.playlist_is_embedded) {
		gtk_window_get_size(GTK_WINDOW(playlist_window), &options.playlist_size_x, &options.playlist_size_y);
	} else {
		options.playlist_size_x = playlist_window->allocation.width;
		options.playlist_size_y = playlist_window->allocation.height;
	}

	if (!options.hide_comment_pane) {
		options.browser_paned_pos = gtk_paned_get_position(GTK_PANED(browser_paned));
	}
}


void
restore_window_position(void) {

	gtk_window_move(GTK_WINDOW(main_window), options.main_pos_x, options.main_pos_y);
	gtk_window_move(GTK_WINDOW(browser_window), options.browser_pos_x, options.browser_pos_y);
	if (!options.playlist_is_embedded) {
		gtk_window_move(GTK_WINDOW(playlist_window), options.playlist_pos_x, options.playlist_pos_y);
	}

	gtk_window_resize(GTK_WINDOW(main_window), options.main_size_x, options.main_size_y);
	gtk_window_resize(GTK_WINDOW(browser_window), options.browser_size_x, options.browser_size_y);
	if (!options.playlist_is_embedded) {
		gtk_window_resize(GTK_WINDOW(playlist_window), options.playlist_size_x, options.playlist_size_y);
	}

	if (!options.hide_comment_pane) {
		gtk_paned_set_position(GTK_PANED(browser_paned), options.browser_paned_pos);
	}
}


gboolean
main_window_close(GtkWidget * widget, GdkEvent * event, gpointer data) {

	send_cmd = CMD_FINISH;
	rb_write(rb_gui2disk, &send_cmd, 1);
	try_waking_disk_thread();

#ifdef HAVE_CDDA
	cdda_shutdown();
#endif /* HAVE_CDDA */

	if (systray_main_window_on) {
		save_window_position();
	}

	save_config();

#ifdef HAVE_LADSPA
	save_plugin_data();
	lrdf_cleanup();
#endif /* HAVE_LADSPA */

#ifdef HAVE_SYSTRAY
	if (systray_used) {
		gtk_status_icon_set_visible(GTK_STATUS_ICON(systray_icon), FALSE);
	}
#endif /* HAVE_SYSTRAY */

	if (options.auto_save_playlist) {
		char playlist_name[MAXLEN];

		snprintf(playlist_name, MAXLEN-1, "%s/%s", options.confdir, "playlist.xml");
		playlist_save_all(playlist_name);
	}

        pango_font_description_free(fd_playlist);
        pango_font_description_free(fd_browser);

	finalize_options();

	gtk_main_quit();

	return FALSE;
}

void
main_window_closing(void) {

	if (music_store_changed) {
		GtkWidget * dialog;
		int resp;

                dialog = gtk_message_dialog_new(GTK_WINDOW(main_window),
                                                GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
                                                GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE,
                                                _("One or more stores in Music Store have been modified.\n"
                                                "Do you want to save them before exiting?"));

		gtk_dialog_add_buttons(GTK_DIALOG(dialog),
				       _("Save"), GTK_RESPONSE_YES,
				       _("Discard changes"), GTK_RESPONSE_NO,
				       _("Do not exit"), GTK_RESPONSE_CANCEL,
				       NULL);

		gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER);
		gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_YES);
		gtk_container_set_border_width(GTK_CONTAINER(dialog), 5);
                gtk_window_set_title(GTK_WINDOW(dialog), _("Quit"));

		resp = aqualung_dialog_run(GTK_DIALOG(dialog));
		gtk_widget_destroy(dialog);

		if (resp == GTK_RESPONSE_CANCEL) {
			return;
		}

		if (resp == GTK_RESPONSE_YES) {
			music_store_save_all();
		}
	}

	main_window_close(NULL, NULL, NULL);
}

gboolean
main_window_event(GtkWidget * widget, GdkEvent * event, gpointer data) {

	if (event->type == GDK_DELETE) {
#ifdef HAVE_SYSTRAY
		if (systray_used) {
			hide_all_windows(NULL);
		} else {
			main_window_closing();
		}
#else
		main_window_closing();
#endif /* HAVE_SYSTRAY */
		return TRUE;
	}

	return FALSE;
}

void
main_window_resize(void) {

        if (options.playlist_is_embedded && !options.playlist_on) {
		gtk_window_resize(GTK_WINDOW(main_window), options.main_size_x,
				  options.main_size_y - 14 - 6 - 6 -
				  playlist_window->allocation.height);
	}

	if (!options.playlist_is_embedded) {
		gtk_window_resize(GTK_WINDOW(main_window),
				  options.main_size_x, options.main_size_y - 14 - 6);
	}
}

/***********************************************************************************/


void
conf__options_cb(gpointer data) {

	create_options_window();
}


void
conf__skin_cb(gpointer data) {

	create_skin_window();
}


void
conf__jack_cb(gpointer data) {

#ifdef HAVE_JACK
	port_setup_dialog();
#endif /* HAVE_JACK */
}


void
conf__fileinfo_cb(gpointer data) {

	if (is_file_loaded) {

		GtkTreeIter dummy;
		const char * name = gtk_label_get_text(GTK_LABEL(label_title));

		show_file_info((char *)name, current_file, 0, NULL, dummy, TRUE);
	}
}


void
conf__about_cb(gpointer data) {

	create_about_window();
}


void
conf__quit_cb(gpointer data) {

	main_window_closing();
}


gint
vol_bal_timeout_callback(gpointer data) {

	refresh_time_label = 1;
	vol_bal_timeout_tag = 0;
	refresh_time_displays();

	return FALSE;
}


void
musicstore_toggled(GtkWidget * widget, gpointer data) {

	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
		show_music_browser();
	} else {
		hide_music_browser();
	}
}


void
playlist_toggled(GtkWidget * widget, gpointer data) {

	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
		show_playlist();
	} else {
		hide_playlist();
	}
}


void
plugin_toggled(GtkWidget * widget, gpointer data) {

#ifdef HAVE_LADSPA
	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))) {
		show_fxbuilder();
	} else {
		hide_fxbuilder();
	}
#else
	/* XXX */
	printf("Aqualung compiled without LADSPA plugin support.\n");
#endif /* HAVE_LADSPA */
}

void
toggle_stop_after_current_song() {

	if (is_file_loaded) {
		stop_after_current_song = !stop_after_current_song;
		set_title_label(playing_app_title);
	}
}

void
move_song_position_slider(GtkScrollType scroll_type) {

	if (is_file_loaded && allow_seeks && total_samples != 0) {
		refresh_scale = 0;
		g_signal_emit_by_name(G_OBJECT(scale_pos), "move-slider",
				      scroll_type, NULL);
	}
}

void
seek_song(void) {

	if (is_file_loaded && allow_seeks && refresh_scale == 0 && total_samples != 0) {
		seek_t seek;

		refresh_scale = 1;
		send_cmd = CMD_SEEKTO;
		seek.seek_to_pos = gtk_adjustment_get_value(GTK_ADJUSTMENT(adj_pos)) / 100.0f * total_samples;
		rb_write(rb_gui2disk, &send_cmd, 1);
		rb_write(rb_gui2disk, (char *)&seek, sizeof(seek_t));
		try_waking_disk_thread();
		refresh_scale_suppress = 2;
        }
}

void
change_volume_or_balance(GtkWidget * slider_widget, GtkScrollType scroll_type) {

	refresh_time_label = 0;
	if (vol_bal_timeout_tag) {
		g_source_remove(vol_bal_timeout_tag);
	}

	vol_bal_timeout_tag = aqualung_timeout_add(1000, vol_bal_timeout_callback, NULL);

	g_signal_emit_by_name(G_OBJECT(slider_widget), "move-slider",
			      scroll_type, NULL);
}

gint
main_window_key_pressed(GtkWidget * widget, GdkEventKey * event) {

        int playlist_tabs = gtk_notebook_get_n_pages(GTK_NOTEBOOK(playlist_notebook));

	if (custom_main_keybinding_expected) {
		switch (event->keyval) {
		case GDK_Shift_L:
		case GDK_Shift_R: 
		case GDK_Control_L:
		case GDK_Control_R: 
		case GDK_Alt_L:
		case GDK_Alt_R: 
		case GDK_Super_L:
		case GDK_Super_R: 
			return FALSE;
		default:
			run_custom_main_keybinding(gdk_keyval_name(event->keyval), event->state);
			custom_main_keybinding_expected = 0;
			return TRUE;
		}
	}

	switch (event->keyval) {
	case GDK_KP_Divide:
	case GDK_slash:
                if (event->state & GDK_MOD1_MASK) {  /* ALT + KP_Divide */
			change_volume_or_balance(scale_bal, GTK_SCROLL_STEP_BACKWARD);
		} else {
			change_volume_or_balance(scale_vol, GTK_SCROLL_STEP_BACKWARD);
		}
		return TRUE;
	case GDK_KP_Multiply:
	case GDK_asterisk:
                if (event->state & GDK_MOD1_MASK) {  /* ALT + KP_Multiply */
			change_volume_or_balance(scale_bal, GTK_SCROLL_STEP_FORWARD);
		} else {
			change_volume_or_balance(scale_vol, GTK_SCROLL_STEP_FORWARD);
		}
		return TRUE;
	case GDK_Right:
		move_song_position_slider(GTK_SCROLL_STEP_FORWARD);
		return TRUE;
	case GDK_Left:
		move_song_position_slider(GTK_SCROLL_STEP_BACKWARD);
		return TRUE;
	case GDK_b:
	case GDK_B:
	case GDK_period:
	case GDK_AudioNext:
		next_event(NULL, NULL, NULL);
		return TRUE;
	case GDK_z:
	case GDK_Z:
		if (event->state & GDK_CONTROL_MASK) {
			custom_main_keybinding_expected = 1;
			return TRUE;
		}
	case GDK_y:
	case GDK_Y:
	case GDK_comma:
	case GDK_AudioPrev:
		prev_event(NULL, NULL, NULL);
		return TRUE;
	case GDK_s:
	case GDK_S:
                if (event->state & GDK_MOD1_MASK) {  /* ALT + s */
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(musicstore_toggle),
						     !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(musicstore_toggle)));
		} else {
			if (!(event->state & GDK_CONTROL_MASK)) {
				stop_event(NULL, NULL, NULL);
			} else {
				toggle_stop_after_current_song();
			}
		}
		return TRUE;
	case GDK_v:
	case GDK_V:
		if (!(event->state & GDK_CONTROL_MASK)) {
			stop_event(NULL, NULL, NULL);
			return TRUE;
		}
		break;
	case GDK_AudioStop:
		stop_event(NULL, NULL, NULL);
		return TRUE;
	case GDK_c:
	case GDK_C:
	case GDK_space:
		if (!(event->state & GDK_CONTROL_MASK)) {
			if (!options.combine_play_pause) {
				gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pause_button),
		                !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pause_button)));
			} else if (is_file_loaded) {
				gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(play_button),
		                !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(play_button)));
			}
			return TRUE;
		}
		break;
	case GDK_p:
	case GDK_P:
	case GDK_AudioPlay:
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(play_button),
					     !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(play_button)));
		return TRUE;
	case GDK_i:
	case GDK_I:
		if (!options.playlist_is_embedded) {
                        conf__fileinfo_cb(NULL);
        		return TRUE;
                }
                break;
	case GDK_BackSpace:
		if (allow_seeks && total_samples != 0) {
			seek_t seek;

			send_cmd = CMD_SEEKTO;
			seek.seek_to_pos = 0.0f;
			rb_write(rb_gui2disk, &send_cmd, 1);
			rb_write(rb_gui2disk, (char *)&seek, sizeof(seek_t));
			try_waking_disk_thread();
			refresh_scale_suppress = 2;
		}

		if ((!options.combine_play_pause) && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pause_button))) {
			gtk_adjustment_set_value(GTK_ADJUSTMENT(adj_pos), 0.0f);
		}
		return TRUE;
	case GDK_l:
	case GDK_L:
                if (event->state & GDK_MOD1_MASK) {  /* ALT + l */
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(playlist_toggle),
						     !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(playlist_toggle)));
		}
		return TRUE;
	case GDK_x:
	case GDK_X:
		if (event->state & GDK_CONTROL_MASK) {
			break;
		}
                if (event->state & GDK_MOD1_MASK) {  /* ALT + x */
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(plugin_toggle),
						     !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(plugin_toggle)));
		} else {
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(play_button),
						     !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(play_button)));
		}
		return TRUE;
	case GDK_q:
	case GDK_Q:
                if (event->state & GDK_CONTROL_MASK) {  /* CTRL + q */
			main_window_closing();
		}
		return TRUE;
	case GDK_k:
	case GDK_K:
                if (!options.disable_skin_support_settings) {
                	create_skin_window();
                }
                return TRUE;
	case GDK_o:
	case GDK_O:
                create_options_window();
                return TRUE;
	case GDK_1:
                if (event->state & GDK_MOD1_MASK) {  /* ALT + 1 */
                        if(playlist_tabs >= 1) {
                                gtk_notebook_set_current_page(GTK_NOTEBOOK(playlist_notebook), 1-1);
                        }
                } else {
                        if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(repeat_button))) {
                                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(repeat_button), FALSE);
                        } else {
                                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(repeat_button), TRUE);
                        }
                }
                return TRUE;
	case GDK_2:
                if (event->state & GDK_MOD1_MASK) {  /* ALT + 2 */
                        if(playlist_tabs >= 2) {
                                gtk_notebook_set_current_page(GTK_NOTEBOOK(playlist_notebook), 2-1);
                        }
                } else {
                        if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(repeat_all_button))) {
                                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(repeat_all_button), FALSE);
                        } else {
                                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(repeat_all_button), TRUE);
                        }
                }
                return TRUE;
	case GDK_3:
                if (event->state & GDK_MOD1_MASK) {  /* ALT + 3 */
                        if(playlist_tabs >= 3) {
                                gtk_notebook_set_current_page(GTK_NOTEBOOK(playlist_notebook), 3-1);
                        }
                } else {
                        if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(shuffle_button))) {
                                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(shuffle_button), FALSE);
                        } else {
                                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(shuffle_button), TRUE);
                        }
                }
                return TRUE;
	case GDK_4:
	case GDK_5:
	case GDK_6:
	case GDK_7:
	case GDK_8:
	case GDK_9:
	case GDK_0:
                if (event->state & GDK_MOD1_MASK) {  /* ALT */

                        int val = event->keyval - GDK_0;
                        val = (val == 0) ? 10 : val;

                        if(playlist_tabs >= val) {
                                gtk_notebook_set_current_page(GTK_NOTEBOOK(playlist_notebook), val-1);
                        }
                }
                return TRUE;
#ifdef HAVE_SYSTRAY
	case GDK_Escape:
		if (systray_used) {
			hide_all_windows(NULL);
		}
		return TRUE;
#endif /* HAVE_SYSTRAY */

#ifdef HAVE_LOOP
	case GDK_less:
		if (options.repeat_on && is_file_loaded) {
			float pos = (total_samples > 0) ? ((double)sample_pos / total_samples) : 0;
			aqualung_loop_bar_adjust_start(AQUALUNG_LOOP_BAR(loop_bar), pos);
		}
		return TRUE;
	case GDK_greater:
		if (options.repeat_on && is_file_loaded) {
			float pos = (total_samples > 0) ? ((double)sample_pos / total_samples) : 1;
			aqualung_loop_bar_adjust_end(AQUALUNG_LOOP_BAR(loop_bar), pos);
		}
		return TRUE;
#endif /* HAVE_LOOP */
	}

        if (options.playlist_is_embedded) {
		playlist_window_key_pressed(widget, event);
        }

	return FALSE;
}


gint
main_window_key_released(GtkWidget * widget, GdkEventKey * event) {

	switch (event->keyval) {
        case GDK_Right:
        case GDK_Left:
		seek_song();
                break;
	}

	return FALSE;
}


gint
main_window_focus_out(GtkWidget * widget, GdkEventFocus * event, gpointer data) {

        refresh_scale = 1;

        return FALSE;
}


gint
main_window_state_changed(GtkWidget * widget, GdkEventWindowState * event, gpointer data) {

        if (!options.united_minimization)
		return FALSE;

	if (event->new_window_state == GDK_WINDOW_STATE_ICONIFIED) {
		if (options.browser_on) {
			gtk_window_iconify(GTK_WINDOW(browser_window));
		}

		if (!options.playlist_is_embedded && options.playlist_on) {
			gtk_window_iconify(GTK_WINDOW(playlist_window));
		}

		if (vol_window) {
			gtk_window_iconify(GTK_WINDOW(vol_window));
		}
#ifdef HAVE_LADSPA
		if (fxbuilder_on) {
			GtkTreeIter iter;
			gpointer gp_instance;
			int i = 0;

			gtk_window_iconify(GTK_WINDOW(fxbuilder_window));

			while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(running_store), &iter, NULL, i) &&
			       i < MAX_PLUGINS) {
				gtk_tree_model_get(GTK_TREE_MODEL(running_store), &iter, 1, &gp_instance, -1);
				gtk_widget_hide(((plugin_instance *)gp_instance)->window);
				++i;
			}
		}
#endif /* HAVE_LADSPA */
	}

	if (event->new_window_state == 0) {
		if (options.browser_on) {
			gtk_window_deiconify(GTK_WINDOW(browser_window));
		}

		if (!options.playlist_is_embedded && options.playlist_on) {
			gtk_window_deiconify(GTK_WINDOW(playlist_window));
		}

		if (vol_window) {
			gtk_window_deiconify(GTK_WINDOW(vol_window));
		}
		if (fxbuilder_on) {
			gtk_window_deiconify(GTK_WINDOW(fxbuilder_window));
		}
	}
        return FALSE;
}


gint
main_window_button_pressed(GtkWidget * widget, GdkEventButton * event) {

	if (event->button == 3) {

		GtkWidget * fileinfo;

		if (options.playlist_is_embedded) {
			fileinfo = plist__fileinfo;
		} else {
			fileinfo = conf__fileinfo;
		}

		if (is_file_loaded && (current_file[0] != '\0')) {

			const char * name = gtk_label_get_text(GTK_LABEL(label_title));

			strncpy(fileinfo_name, name, MAXLEN-1);
			strncpy(fileinfo_file, current_file, MAXLEN-1);

			gtk_widget_set_sensitive(fileinfo, TRUE);
		} else {
			gtk_widget_set_sensitive(fileinfo, FALSE);
		}

		if (options.playlist_is_embedded) {
			playlist_menu_set_popup_sensitivity(playlist_get_current());
                }

                gtk_menu_popup(GTK_MENU(conf_menu), NULL, NULL, NULL, NULL,
			       event->button, event->time);
	}

	return TRUE;
}


static gint
scale_button_press_event(GtkWidget * widget, GdkEventButton * event) {

	if (event->button == 3)
		return FALSE;

	if (!is_file_loaded)
		return FALSE;

	if (!allow_seeks)
		return FALSE;

	if (total_samples == 0)
		return FALSE;

	refresh_scale = 0;
	return FALSE;
}


static gint
scale_button_release_event(GtkWidget * widget, GdkEventButton * event) {

	seek_t seek;

	if (is_file_loaded) {

		if (!allow_seeks)
			return FALSE;

		if (total_samples == 0)
			return FALSE;

		if (refresh_scale == 0) {
			refresh_scale = 1;

			send_cmd = CMD_SEEKTO;
			seek.seek_to_pos = gtk_adjustment_get_value(GTK_ADJUSTMENT(adj_pos))
				/ 100.0f * total_samples;
			rb_write(rb_gui2disk, &send_cmd, 1);
			rb_write(rb_gui2disk, (char *)&seek, sizeof(seek_t));
			try_waking_disk_thread();
			refresh_scale_suppress = 2;
		}
	}

	return FALSE;
}


void
changed_pos(GtkAdjustment * adj, gpointer data) {
	static int pos = -1;

	if (!is_file_loaded) {
		gtk_adjustment_set_value(adj, 0.0f);
	}

        if (options.enable_tooltips) {
		int newpos = (int)gtk_adjustment_get_value(adj);
		if (pos != newpos) {
			char str[32];
			snprintf(str, 31, _("Position: %d%%"), newpos);
			aqualung_widget_set_tooltip_text(scale_pos, str);
			pos = newpos;
		}
        }
}


gint
scale_vol_button_press_event(GtkWidget * widget, GdkEventButton * event) {

	char str[32];
	options.vol = gtk_adjustment_get_value(GTK_ADJUSTMENT(adj_vol));

        if (event->state & GDK_SHIFT_MASK) {  /* SHIFT */
		gtk_adjustment_set_value(GTK_ADJUSTMENT(adj_vol), 0);
		return TRUE;
	}

	if (options.vol < -40.5f) {
		snprintf(str, 31, _("Mute"));
	} else {
		snprintf(str, 31, _("%d dB"), (int)options.vol);
	}

	gtk_label_set_text(GTK_LABEL(time_labels[options.time_idx[0]]), str);

	refresh_time_label = 0;

	if (event->button == 3) {
		return TRUE;
	} else {
		return FALSE;
	}
}


void
changed_vol(GtkAdjustment * adj, gpointer date) {

	char str[32];
	char str2[32];

	options.vol = (int)gtk_adjustment_get_value(GTK_ADJUSTMENT(adj_vol));
	gtk_adjustment_set_value(GTK_ADJUSTMENT(adj_vol), options.vol);

        if (options.vol < -40.5f) {
                snprintf(str, 31, _("Mute"));
        } else {
                snprintf(str, 31, _("%d dB"), (int)options.vol);
        }

        if (!refresh_time_label) {
		gtk_label_set_text(GTK_LABEL(time_labels[options.time_idx[0]]), str);
        }

        snprintf(str2, 31, _("Volume: %s"), str);
        aqualung_widget_set_tooltip_text(scale_vol, str2);
}


gint
scale_vol_button_release_event(GtkWidget * widget, GdkEventButton * event) {

	refresh_time_label = 1;
	refresh_time_displays();

	return FALSE;
}


gint
scale_bal_button_press_event(GtkWidget * widget, GdkEventButton * event) {

	char str[32];
	options.bal = gtk_adjustment_get_value(GTK_ADJUSTMENT(adj_bal));

        if (event->state & GDK_SHIFT_MASK) {  /* SHIFT */
		gtk_adjustment_set_value(GTK_ADJUSTMENT(adj_bal), 0);
		return TRUE;
	}

	if (options.bal != 0.0f) {
		if (options.bal > 0.0f) {
			snprintf(str, 31, _("%d%% R"), (int)options.bal);
		} else {
			snprintf(str, 31, _("%d%% L"), -1*(int)options.bal);
		}
	} else {
		snprintf(str, 31, _("C"));
	}

	gtk_label_set_text(GTK_LABEL(time_labels[options.time_idx[0]]), str);

	refresh_time_label = 0;

	if (event->button == 3) {
		return TRUE;
	} else {
		return FALSE;
	}
}


void
changed_bal(GtkAdjustment * adj, gpointer date) {

	char str[32];
	char str2[32];

	options.bal = (int)gtk_adjustment_get_value(GTK_ADJUSTMENT(adj_bal));
	gtk_adjustment_set_value(GTK_ADJUSTMENT(adj_bal), options.bal);

        if (options.bal != 0.0f) {
                if (options.bal > 0.0f) {
                        snprintf(str, 31, _("%d%% R"), (int)options.bal);
                } else {
                        snprintf(str, 31, _("%d%% L"), -1*(int)options.bal);
                }
        } else {
                snprintf(str, 31, _("C"));
        }

        if (!refresh_time_label) {
		gtk_label_set_text(GTK_LABEL(time_labels[options.time_idx[0]]), str);
	}

        snprintf(str2, 31, _("Balance: %s"), str);
        aqualung_widget_set_tooltip_text(scale_bal, str2);
}


gint
scale_bal_button_release_event(GtkWidget * widget, GdkEventButton * event) {

	refresh_time_label = 1;
	refresh_time_displays();

	return FALSE;
}


void
show_scale_pos(gboolean state) {

        if (state == FALSE) {
                gtk_widget_hide(GTK_WIDGET(scale_pos));
                gtk_widget_show(GTK_WIDGET(vbox_sep));
                main_window_resize();
        } else {
                gtk_widget_show(GTK_WIDGET(scale_pos));
                gtk_widget_hide(GTK_WIDGET(vbox_sep));
        }
}


/******** Cue functions *********/

void
toggle_noeffect(int id, int state) {
	switch (id) {
	case PLAY:
		g_signal_handler_block(G_OBJECT(play_button), play_id);
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(play_button), state);
		g_signal_handler_unblock(G_OBJECT(play_button), play_id);
		break;
	case PAUSE:
		g_signal_handler_block(G_OBJECT(pause_button), pause_id);
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pause_button), state);
		g_signal_handler_unblock(G_OBJECT(pause_button), pause_id);
		break;
	default:
		printf("error in gui_main.c/toggle_noeffect(): unknown id value %d\n", id);
		break;
	}
}


void
cue_track_for_playback(GtkTreeStore * store, GtkTreeIter * piter, cue_t * cue) {

	playlist_data_t * data;

	gtk_tree_model_get(GTK_TREE_MODEL(store), piter, PL_COL_DATA, &data, -1);
	cue->filename = strdup(data->file);
	cue->voladj = options.rva_is_enabled ? data->voladj : 0.0f;
	strncpy(current_file, cue->filename, MAXLEN-1);
	run_hooks("track_change");
}


/* retcode for choose_*_track(): 1->success, 0->empty list */
int
choose_first_track(GtkTreeStore * store, GtkTreeIter * piter) {

	if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), piter)) {
		if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(store), piter)) {
			GtkTreeIter iter_parent = *piter;
			gtk_tree_model_iter_children(GTK_TREE_MODEL(store), piter, &iter_parent);
		}
		return 1;
	}
	return 0;
}


/* get first or last child iter */
void
get_child_iter(GtkTreeStore * store, GtkTreeIter * piter, int first) {

	GtkTreeIter iter;
	if (first) {
		gtk_tree_model_iter_children(GTK_TREE_MODEL(store), &iter, piter);
	} else {
		int n = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), piter);
		gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &iter, piter, n-1);
	}
	*piter = iter;
}


int
choose_prev_track(GtkTreeStore * store, GtkTreeIter * piter) {

	GtkTreePath * p = gtk_tree_model_get_path(GTK_TREE_MODEL(store), piter);

 try_again_prev:
	if (gtk_tree_path_prev(p)) {
		if (gtk_tree_path_get_depth(p) == 1) { /* toplevel */
			GtkTreeIter iter;
			gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, p);
			if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(store), &iter)) {
				get_child_iter(store, &iter, 0/* last */);
			}
			*piter = iter;
		} else {
			gtk_tree_model_get_iter(GTK_TREE_MODEL(store), piter, p);
		}
		gtk_tree_path_free(p);
		return 1;
	} else {
		if (gtk_tree_path_get_depth(p) == 1) { /* toplevel */
			GtkTreeIter iter;
			int n = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), NULL);
			if (n) {
				gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &iter, NULL, n-1);
				if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(store), &iter)) {
					get_child_iter(store, &iter, 0/* last */);
				}
				*piter = iter;
				gtk_tree_path_free(p);
				return 1;
			} else {
				gtk_tree_path_free(p);
				return 0;
			}
		} else {
			gtk_tree_path_up(p);
			goto try_again_prev;
		}
	}
}


int
choose_next_track(GtkTreeStore * store, GtkTreeIter * piter) {
	GtkTreeIter iter = *piter;

	if (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter)) {
		*piter = iter;
		if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(store), piter)) {
			get_child_iter(store, piter, 1/* first */);
		}
		return 1;
	} else {
		GtkTreePath * p = gtk_tree_model_get_path(GTK_TREE_MODEL(store), piter);

		if (gtk_tree_path_get_depth(p) == 1) { /* toplevel */
			int n = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), NULL);
			if (n) {
				gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), piter);
				if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(store), piter)) {
					get_child_iter(store, piter, 1/* first */);
				}
				gtk_tree_path_free(p);
				return 1;
			} else {
				gtk_tree_path_free(p);
				return 0;
			}
		} else {
			gtk_tree_path_up(p);
			gtk_tree_model_get_iter(GTK_TREE_MODEL(store), piter, p);
			gtk_tree_path_free(p);
			return choose_next_track(store, piter);
		}
	}
}


/* simpler case than choose_next_track(); no support for wrap-around at end of list.
 * used by the timeout callback for track flow-through */
int
choose_adjacent_track(GtkTreeStore * store, GtkTreeIter * piter) {
	GtkTreeIter iter = *piter;

	if (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter)) {
		*piter = iter;
		if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(store), piter)) {
			get_child_iter(store, piter, 1/* first */);
		}
		return 1;
	} else {
		GtkTreePath * p = gtk_tree_model_get_path(GTK_TREE_MODEL(store), piter);
		if (gtk_tree_path_get_depth(p) == 1) { /* toplevel */
			gtk_tree_path_free(p);
			return 0;
		} else {
			gtk_tree_path_up(p);
			gtk_tree_model_get_iter(GTK_TREE_MODEL(store), piter, p);
			gtk_tree_path_free(p);
			return choose_adjacent_track(store, piter);
		}
	}
}


/* also used to pick the track numbered n_stop in the flattened tree */
long
count_playlist_tracks(GtkTreeStore * store, GtkTreeIter * piter, long n_stop) {

	GtkTreeIter iter;
	long i = 0;
	long n = 0;

	while (gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), &iter, NULL, i++)) {
		long c = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), &iter);
		long d = c;
		if (!c) ++c;
		if (n_stop > -1) {
			if (n_stop > c) {
				n_stop -= c;
			} else {
				if (d) {
					gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store),
								      piter, &iter, n_stop-1);
				} else {
					*piter = iter;
				}
				return 0;
			}
		}
		n += c;
	}
	return n;
}


int
random_toplevel_item(GtkTreeStore * store, GtkTreeIter * piter) {

	long n_items;
	long n;

	n_items = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(store), NULL);
	if (!n_items) {
		return 0;
	}

	n = (double)rand() * n_items / RAND_MAX;
	if (n == n_items)
		--n;

	gtk_tree_model_iter_nth_child(GTK_TREE_MODEL(store), piter, NULL, n);
	return 1;
}


int
random_first_track(GtkTreeStore * store, GtkTreeIter * piter) {

	if (random_toplevel_item(store, piter)) {
		if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(store), piter)) {
			get_child_iter(store, piter, 1/* first */);
		}
		return 1;
	} else {
		return 0;
	}
}


int
choose_random_track(GtkTreeStore * store, GtkTreeIter * piter) {

	long n_items;
	long n;

	if (options.album_shuffle_mode) {
		if (gtk_tree_store_iter_is_valid(store, piter)) {
			int d = gtk_tree_store_iter_depth(store, piter);
			if (d) {
				if (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), piter)) {
					return 1;
				}
			}
		}
		return random_first_track(store, piter);
	} else {
		n_items = count_playlist_tracks(store, NULL, -1);
		if (n_items) {
			n = (double)rand() * n_items / RAND_MAX;
			if (n == n_items) {
				--n;
			}
			count_playlist_tracks(store, piter, n+1);
			return 1;
		}
		return 0;
	}
}


void
prepare_playback(playlist_t * pl, GtkTreeIter * piter, cue_t * pcue) {

	mark_track(pl, piter);
	cue_track_for_playback(pl->store, piter, pcue);
	is_file_loaded = 1;
	toggle_noeffect(PLAY, TRUE);
}


void
unprepare_playback(void) {

	is_file_loaded = 0;
	stop_after_current_song = 0;
	current_file[0] = '\0';
	zero_displays();
	toggle_noeffect(PLAY, FALSE);
}


gint
prev_event(GtkWidget * widget, GdkEvent * event, gpointer data) {

	GtkTreeIter iter;
	GtkTreePath * p;
	char cmd;
	cue_t cue;
	playlist_t * pl;

	if (!allow_seeks)
		return FALSE;

	if (is_file_loaded) {
		pl = playlist_get_playing();
	} else {
		pl = playlist_get_current();
	}

	if (pl == NULL) {
		return FALSE;
	}

	if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(shuffle_button))) {
		/* normal or repeat mode */
		p = playlist_get_playing_path(pl);
		if (p != NULL) {
			gtk_tree_model_get_iter(GTK_TREE_MODEL(pl->store), &iter, p);
			gtk_tree_path_free(p);
			unmark_track(pl, &iter);
			if (choose_prev_track(pl->store, &iter)) {
				mark_track(pl, &iter);
			}
		} else {
			if (choose_first_track(pl->store, &iter)) {
				mark_track(pl, &iter);
			}
		}
	} else {
		/* shuffle mode */
		p = playlist_get_playing_path(pl);
		if (p != NULL) {
			gtk_tree_model_get_iter(GTK_TREE_MODEL(pl->store), &iter, p);
			gtk_tree_path_free(p);
			unmark_track(pl, &iter);
		}
		if (choose_random_track(pl->store, &iter)) {
			mark_track(pl, &iter);
		}
	}

	if (is_file_loaded) {
		if ((p = playlist_get_playing_path(pl)) == NULL) {
			if (is_paused) {
				is_paused = 0;
				toggle_noeffect(PAUSE, FALSE);
				stop_event(NULL, NULL, NULL);
			}
			return FALSE;
		}

		gtk_tree_model_get_iter(GTK_TREE_MODEL(pl->store), &iter, p);
		gtk_tree_path_free(p);
		cue_track_for_playback(pl->store, &iter, &cue);

		stop_after_current_song = 0;
		if (is_paused) {
			is_paused = 0;
			toggle_noeffect(PAUSE, FALSE);
			toggle_noeffect(PLAY, TRUE);
		}

		cmd = CMD_CUE;
		flush_rb_disk2gui();
		rb_write(rb_gui2disk, &cmd, sizeof(char));
		rb_write(rb_gui2disk, (void *)&cue, sizeof(cue_t));
		try_waking_disk_thread();
	}
	return FALSE;
}


gint
next_event(GtkWidget * widget, GdkEvent * event, gpointer data) {

	GtkTreeIter iter;
	GtkTreePath * p;
	char cmd;
	cue_t cue;
	playlist_t * pl;

	if (!allow_seeks)
		return FALSE;

	if (is_file_loaded) {
		pl = playlist_get_playing();
	} else {
		pl = playlist_get_current();
	}

	if (pl == NULL) {
		return FALSE;
	}

	if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(shuffle_button))) {
		/* normal or repeat mode */
		p = playlist_get_playing_path(pl);
		if (p != NULL) {
			gtk_tree_model_get_iter(GTK_TREE_MODEL(pl->store), &iter, p);
			gtk_tree_path_free(p);
			unmark_track(pl, &iter);
			if (choose_next_track(pl->store, &iter)) {
				mark_track(pl, &iter);
			}
		} else {
			if (choose_first_track(pl->store, &iter)) {
				mark_track(pl, &iter);
			}
		}
	} else {
		/* shuffle mode */
		p = playlist_get_playing_path(pl);
		if (p != NULL) {
			gtk_tree_model_get_iter(GTK_TREE_MODEL(pl->store), &iter, p);
			gtk_tree_path_free(p);
			unmark_track(pl, &iter);
		}
		if (choose_random_track(pl->store, &iter)) {
			mark_track(pl, &iter);
		}
	}

	if (is_file_loaded) {
		if ((p = playlist_get_playing_path(pl)) == NULL) {
			if (is_paused) {
				is_paused = 0;
				toggle_noeffect(PAUSE, FALSE);
				stop_event(NULL, NULL, NULL);
			}
			return FALSE;
		}

		gtk_tree_model_get_iter(GTK_TREE_MODEL(pl->store), &iter, p);
		gtk_tree_path_free(p);
		cue_track_for_playback(pl->store, &iter, &cue);

		stop_after_current_song = 0;
		if (is_paused) {
			is_paused = 0;
			toggle_noeffect(PAUSE, FALSE);
			toggle_noeffect(PLAY, TRUE);
		}

		cmd = CMD_CUE;
		flush_rb_disk2gui();
		rb_write(rb_gui2disk, &cmd, sizeof(char));
		rb_write(rb_gui2disk, (void *)&cue, sizeof(cue_t));
		try_waking_disk_thread();
	}
	return FALSE;
}


gint
play_event(GtkWidget * widget, GdkEvent * event, gpointer data) {

	GtkTreeIter iter;
	GtkTreePath * p;
	char cmd;
	cue_t cue;
	playlist_t * pl;

	if (is_paused) {
		is_paused = 0;
		if (!options.combine_play_pause) {
			toggle_noeffect(PAUSE, FALSE);
		}
		send_cmd = CMD_RESUME;
		rb_write(rb_gui2disk, &send_cmd, 1);
		try_waking_disk_thread();
		return FALSE;
	}
	if (options.combine_play_pause && is_file_loaded) {
		return pause_event(widget, event, data);
	}

	cmd = CMD_CUE;
	cue.filename = NULL;

	while ((pl = playlist_get_playing()) != NULL) {
		playlist_set_playing(pl, 0);
	}

	if ((pl = playlist_get_current()) == NULL) {
		return FALSE;
	}

	playlist_set_playing(pl, 1);

	p = playlist_get_playing_path(pl);
	if (p != NULL) {
		gtk_tree_model_get_iter(GTK_TREE_MODEL(pl->store), &iter, p);
		gtk_tree_path_free(p);
		prepare_playback(pl, &iter, &cue);
	} else {
		if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(shuffle_button))) {
			/* normal or repeat mode */
			if (choose_first_track(pl->store, &iter)) {
				prepare_playback(pl, &iter, &cue);
			} else {
				unprepare_playback();
			}
		} else { /* shuffle mode */
			if (choose_random_track(pl->store, &iter)) {
				prepare_playback(pl, &iter, &cue);
			} else {
				unprepare_playback();
			}
		}
	}
	if (cue.filename == NULL) {
		stop_event(NULL, NULL, NULL);
	} else {
		flush_rb_disk2gui();
		rb_write(rb_gui2disk, &cmd, sizeof(char));
		rb_write(rb_gui2disk, (void *)&cue, sizeof(cue_t));
		try_waking_disk_thread();
	}
	return FALSE;
}


gint
pause_event(GtkWidget * widget, GdkEvent * event, gpointer data) {

	if ((!allow_seeks) || (!is_file_loaded)) {
		if (!options.combine_play_pause) {
			toggle_noeffect(PAUSE, FALSE);
		}
		return FALSE;
	}

	if (!is_paused) {
		is_paused = 1;
		toggle_noeffect(PLAY, FALSE);
		send_cmd = CMD_PAUSE;
		rb_write(rb_gui2disk, &send_cmd, 1);

	} else {
		is_paused = 0;
		toggle_noeffect(PLAY, TRUE);
		send_cmd = CMD_RESUME;
		rb_write(rb_gui2disk, &send_cmd, 1);
	}

	try_waking_disk_thread();
	return FALSE;
}


gint
stop_event(GtkWidget * widget, GdkEvent * event, gpointer data) {

	char cmd;
	cue_t cue;

	is_file_loaded = 0;
	stop_after_current_song = 0;
	current_file[0] = '\0';
	gtk_adjustment_set_value(GTK_ADJUSTMENT(adj_pos), 0.0f);
	zero_displays();
	toggle_noeffect(PLAY, FALSE);
	if (!options.combine_play_pause) {
		toggle_noeffect(PAUSE, FALSE);
	}

	is_paused = 0;
	allow_seeks = 1;

	cmd = CMD_CUE;
	cue.filename = NULL;
	flush_rb_disk2gui();
        rb_write(rb_gui2disk, &cmd, sizeof(char));
        rb_write(rb_gui2disk, (void *)&cue, sizeof(cue_t));
	try_waking_disk_thread();

 	show_scale_pos(TRUE);

        /* hide cover */
        hide_cover_thumbnail();

	if (embedded_picture != NULL) {
		free(embedded_picture);
		embedded_picture = NULL;
		embedded_picture_size = 0;
	}

	return FALSE;
}


/* called when a track ends without user intervention */
void
decide_next_track(cue_t * pcue) {

	GtkTreePath * p;
	GtkTreeIter iter;
	playlist_t * pl;

	if ((pl = playlist_get_playing()) == NULL) {
		unprepare_playback();
		return;
	}

	if (stop_after_current_song) {
		unprepare_playback();
		return;
	}

	p = playlist_get_playing_path(pl);
	if (p != NULL) { /* there is a marked track in playlist */
		if ((!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(shuffle_button))) &&
		    (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(repeat_button)))) {
			/* normal or list repeat mode */
			gtk_tree_model_get_iter(GTK_TREE_MODEL(pl->store), &iter, p);
			gtk_tree_path_free(p);
			unmark_track(pl, &iter);
			if (choose_adjacent_track(pl->store, &iter)) {
				prepare_playback(pl, &iter, pcue);
			} else {
				if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(repeat_all_button))) {
					/* normal mode */
					allow_seeks = 1;
					changed_pos(GTK_ADJUSTMENT(adj_pos), NULL);
					unprepare_playback();
				} else {
					/* list repeat mode */
					if (choose_first_track(pl->store, &iter)) {
						prepare_playback(pl, &iter, pcue);
					} else {
						allow_seeks = 1;
						changed_pos(GTK_ADJUSTMENT(adj_pos), NULL);
						unprepare_playback();
					}
				}
			}
		} else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(repeat_button))) {
			/* track repeat mode */
			gtk_tree_model_get_iter(GTK_TREE_MODEL(pl->store), &iter, p);
			gtk_tree_path_free(p);
			prepare_playback(pl, &iter, pcue);
		} else {
			/* shuffle mode */
			gtk_tree_model_get_iter(GTK_TREE_MODEL(pl->store), &iter, p);
			gtk_tree_path_free(p);
			unmark_track(pl, &iter);
			if (choose_random_track(pl->store, &iter)) {
				prepare_playback(pl, &iter, pcue);
			} else {
				unprepare_playback();
			}
		}
	} else { /* no marked track in playlist */
		if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(shuffle_button))) {
			/* normal or repeat mode */
			if (choose_first_track(pl->store, &iter)) {
				prepare_playback(pl, &iter, pcue);
			} else {
				allow_seeks = 1;
				changed_pos(GTK_ADJUSTMENT(adj_pos), NULL);
				unprepare_playback();
			}
		} else { /* shuffle mode */
			if (choose_random_track(pl->store, &iter)) {
				prepare_playback(pl, &iter, pcue);
			} else {
				unprepare_playback();
			}
		}
	}
}


/********************************************/

void
swap_labels(int a, int b) {

	GtkWidget * tmp;
	int t;

	tmp = time_labels[options.time_idx[a]];
	time_labels[options.time_idx[a]] = time_labels[options.time_idx[b]] ;
	time_labels[options.time_idx[b]] = tmp;

	t = options.time_idx[b];
	options.time_idx[b] = options.time_idx[a];
	options.time_idx[a] = t;
}


gint
time_label0_clicked(GtkWidget * widget, GdkEventButton * event) {

	switch (event->button) {
	case 1:
		swap_labels(0, 1);
		refresh_time_displays();
		break;
	case 3:
		swap_labels(0, 2);
		refresh_time_displays();
		break;
	}

	return TRUE;
}


gint
time_label1_clicked(GtkWidget * widget, GdkEventButton * event) {

	switch (event->button) {
	case 1:
		swap_labels(0, 1);
		refresh_time_displays();
		break;
	case 3:
		swap_labels(1, 2);
		refresh_time_displays();
		break;
	}

	return TRUE;
}


gint
time_label2_clicked(GtkWidget * widget, GdkEventButton * event) {

	switch (event->button) {
	case 1:
		swap_labels(1, 2);
		refresh_time_displays();
		break;
	case 3:
		swap_labels(0, 2);
		refresh_time_displays();
		break;
	}

	return TRUE;
}


gint
scroll_btn_pressed(GtkWidget * widget, GdkEventButton * event) {

	if (event->button != 1)
		return FALSE;

	x_scroll_start = event->x;
	x_scroll_pos = event->x;
	scroll_btn = event->button;

	return TRUE;
}

gint
scroll_btn_released(GtkWidget * widget, GdkEventButton * event, gpointer * win) {

	scroll_btn = 0;
	gdk_window_set_cursor(gtk_widget_get_parent_window(GTK_WIDGET(win)), NULL);

	return TRUE;
}

gint
scroll_motion_notify(GtkWidget * widget, GdkEventMotion * event, gpointer * win) {

	int dx = event->x - x_scroll_start;
	gboolean dummy;

	if (scroll_btn != 1)
		return FALSE;

	if (!scroll_btn)
		return TRUE;

	if (dx < -10) {
		GtkRange * range = GTK_RANGE(gtk_scrolled_window_get_hscrollbar(GTK_SCROLLED_WINDOW(win)));
		g_signal_emit_by_name(G_OBJECT(range), "move-slider", GTK_SCROLL_STEP_RIGHT, &dummy);
		x_scroll_start = event->x;
		gdk_window_set_cursor(gtk_widget_get_parent_window(GTK_WIDGET(win)),
				      gdk_cursor_new(GDK_SB_H_DOUBLE_ARROW));
	}

	if (dx > 10) {
		GtkRange * range = GTK_RANGE(gtk_scrolled_window_get_hscrollbar(GTK_SCROLLED_WINDOW(win)));
		g_signal_emit_by_name(G_OBJECT(range), "move-slider", GTK_SCROLL_STEP_LEFT, &dummy);
		x_scroll_start = event->x;
		gdk_window_set_cursor(gtk_widget_get_parent_window(GTK_WIDGET(win)),
				      gdk_cursor_new(GDK_SB_H_DOUBLE_ARROW));
	}

	x_scroll_pos = event->x;

	return TRUE;
}

#ifdef HAVE_LOOP
void
hide_loop_bar() {
	gtk_widget_hide(loop_bar);
        main_window_resize();
}
#endif /* HAVE_LOOP */

void
repeat_toggled(GtkWidget * widget, gpointer data) {

	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(repeat_button))) {
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(repeat_all_button), FALSE);
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(shuffle_button), FALSE);
		options.repeat_on = 1;
	} else {
		options.repeat_on = 0;
	}

#ifdef HAVE_LOOP
	if (options.repeat_on) {
		gtk_widget_show(loop_bar);
	} else {
		hide_loop_bar();
	}
#endif /* HAVE_LOOP */
}


void
repeat_all_toggled(GtkWidget * widget, gpointer data) {

	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(repeat_all_button))) {
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(repeat_button), FALSE);
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(shuffle_button), FALSE);
		options.repeat_all_on = 1;
	} else {
		options.repeat_all_on = 0;
	}
}


void
shuffle_toggled(GtkWidget * widget, gpointer data) {

	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(shuffle_button))) {
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(repeat_button), FALSE);
                gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(repeat_all_button), FALSE);
		options.shuffle_on = 1;
	} else {
		options.shuffle_on = 0;
	}
}


void
button_set_content(GtkWidget * button, char * imgpath, char * alt) {

        GdkPixbuf * pixbuf;
	GtkWidget * widget;

	if ((widget = gtk_bin_get_child(GTK_BIN(button))) != NULL) {
		gtk_widget_destroy(widget);
	}

	if ((pixbuf = gdk_pixbuf_new_from_file(imgpath, NULL)) != NULL) {
		widget = gtk_image_new_from_pixbuf(pixbuf);
	} else {
		widget = gtk_label_new(alt);
	}

	gtk_container_add(GTK_CONTAINER(button), widget);
	gtk_widget_show(widget);
}

void
main_buttons_set_content(char * skin_path) {

	char path[MAXLEN];

	sprintf(path, "%s/%s", skin_path, "prev.png");
	button_set_content(prev_button, path, "prev");
	sprintf(path, "%s/%s", skin_path, "stop.png");
	button_set_content(stop_button, path, "stop");
	sprintf(path, "%s/%s", skin_path, "next.png");
	button_set_content(next_button, path, "next");
	if (options.combine_play_pause) {
		sprintf(path, "%s/%s", skin_path, "play_pause.png");
		button_set_content(play_button, path, "play/pause");
	} else {
		sprintf(path, "%s/%s", skin_path, "play.png");
		button_set_content(play_button, path, "play");
		sprintf(path, "%s/%s", skin_path, "pause.png");
		button_set_content(pause_button, path, "pause");
	}
	sprintf(path, "%s/%s", skin_path, "repeat.png");
	button_set_content(repeat_button, path, "repeat");
	sprintf(path, "%s/%s", skin_path, "repeat_all.png");
	button_set_content(repeat_all_button, path, "rep_all");
	sprintf(path, "%s/%s", skin_path, "shuffle.png");
	button_set_content(shuffle_button, path, "shuffle");

	sprintf(path, "%s/%s", skin_path, "pl.png");
	button_set_content(playlist_toggle, path, "PL");
	sprintf(path, "%s/%s", skin_path, "ms.png");
	button_set_content(musicstore_toggle, path, "MS");
	sprintf(path, "%s/%s", skin_path, "fx.png");
	button_set_content(plugin_toggle, path, "FX");
}


#ifdef HAVE_JACK
void
jack_shutdown_window(void) {

	message_dialog(_("JACK connection lost"),
		       main_window,
		       GTK_MESSAGE_ERROR,
		       GTK_BUTTONS_OK,
		       NULL,
		       _("JACK has either been shutdown or it "
			 "disconnected Aqualung. Reason: %s"),
		       jack_shutdown_reason);
}
#endif /* HAVE_JACK */


void
set_win_title(void) {

	char str_session_id[32];

	strcpy(win_title, "Aqualung");
	if (aqualung_session_id > 0) {
		sprintf(str_session_id, ".%d", aqualung_session_id);
		strcat(win_title, str_session_id);
	}
#ifdef HAVE_JACK
	if ((output == JACK_DRIVER) && (strcmp(client_name, "aqualung") != 0)) {
		strcat(win_title, " [");
		strcat(win_title, client_name);
		strcat(win_title, "]");
	}
#endif /* HAVE_JACK */
}


#ifdef HAVE_SYSTRAY
void
wm_systray_warn_cb(GtkWidget * widget, gpointer data) {

	set_option_from_toggle(widget, &options.wm_systray_warn);
}

void
hide_all_windows(gpointer data) {

	if (wm_not_systray_capable) {
		main_window_closing();
		return;
	}

	if (gtk_status_icon_is_embedded(systray_icon) == FALSE) {

		if (!wm_not_systray_capable && options.wm_systray_warn) {

			GtkWidget * check = gtk_check_button_new_with_label(_("Warn me if the Window Manager does not support system tray"));

			gtk_widget_set_name(check, "check_on_window");
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), TRUE);
			g_signal_connect(G_OBJECT(check), "toggled",
					 G_CALLBACK(wm_systray_warn_cb), NULL);

			message_dialog(_("Warning"),
				       options.playlist_is_embedded ? main_window : playlist_window,
				       GTK_MESSAGE_WARNING,
				       GTK_BUTTONS_OK,
				       check,
				       _("Aqualung is compiled with system tray support, but "
					 "the status icon could not be embedded in the notification "
					 "area. Your desktop may not have support for a system tray, "
					 "or it has not been configured correctly."));

			wm_not_systray_capable = 1;
		} else {
			main_window_closing();
		}

		return;

	} else {
		options.wm_systray_warn = 1;
	}

	if (!systray_main_window_on) {
		return;
	}

	systray_main_window_on = 0;

	save_window_position();
	toplevel_window_foreach(TOP_WIN_TRAY, gtk_widget_hide);

	gtk_widget_hide(systray__hide);
	gtk_widget_show(systray__show);
}

void
show_all_windows(gpointer data) {

	systray_main_window_on = 1;

	toplevel_window_foreach(TOP_WIN_TRAY, gtk_widget_show);
	restore_window_position();

	gtk_widget_hide(systray__show);
	gtk_widget_show(systray__hide);
}
#endif /* HAVE_SYSTRAY */


gboolean
cover_press_button_cb (GtkWidget *widget, GdkEventButton *event, gpointer user_data) {

        GtkTreePath * p;
	GtkTreeIter iter;
	playlist_t * pl;

	if ((pl = playlist_get_playing()) == NULL) {
		return FALSE;
	}

	if (event->type == GDK_BUTTON_PRESS && event->button == 1) { /* LMB ? */
		p = playlist_get_playing_path(pl);
		if (p != NULL) {
			gtk_tree_model_get_iter(GTK_TREE_MODEL(pl->store), &iter, p);
			gtk_tree_path_free(p);
			playlist_data_t * pldata;
			gtk_tree_model_get(GTK_TREE_MODEL(pl->store), &iter, PL_COL_DATA, &pldata, -1);
			if (is_file_loaded) {
				if (embedded_picture != NULL && (find_cover_filename(pldata->file) == NULL || !options.use_external_cover_first)) {
					display_zoomed_cover_from_binary(main_window, c_event_box, embedded_picture, embedded_picture_size);
				} else {
					display_zoomed_cover(main_window, c_event_box, pldata->file);
				}
			}
		}
        }
        return TRUE;
}

void
main_window_set_font(int cond) {

        if (cond) {
		gtk_widget_modify_font(bigtimer_label, fd_bigtimer);
		gtk_widget_modify_font(smalltimer_label_1, fd_smalltimer);
		gtk_widget_modify_font(smalltimer_label_2, fd_smalltimer);
		gtk_widget_modify_font(label_title, fd_songtitle);
		gtk_widget_modify_font(label_mono, fd_songinfo);
		gtk_widget_modify_font(label_samplerate, fd_songinfo);
		gtk_widget_modify_font(label_bps, fd_songinfo);
		gtk_widget_modify_font(label_format, fd_songinfo);
		gtk_widget_modify_font(label_output, fd_songinfo);
		gtk_widget_modify_font(label_src_type, fd_songinfo);
	}
}

void
create_main_window(char * skin_path) {

	GtkWidget * vbox;
	GtkWidget * disp_hbox;
	GtkWidget * btns_hbox;
	GtkWidget * title_hbox;
	GtkWidget * info_hbox;
	GtkWidget * vb_table;

	GtkWidget * conf__separator1;
	GtkWidget * conf__separator2;

	GtkWidget * time_table;
	GtkWidget * time0_viewp;
	GtkWidget * time1_viewp;
	GtkWidget * time2_viewp;
	GtkWidget * time_hbox1;
	GtkWidget * time_hbox2;

	GtkWidget * disp_vbox;
	GtkWidget * title_viewp;
	GtkWidget * title_scrolledwin;
	GtkWidget * info_viewp;
	GtkWidget * info_scrolledwin;
	GtkWidget * info_vsep;

	GtkWidget * sr_table;

        char path[MAXLEN];

	set_win_title();
	playing_app_title[0] = '\0';
	
	main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_widget_set_name(main_window, "main_window");
	register_toplevel_window(main_window, TOP_WIN_SKIN | TOP_WIN_TRAY);
	gtk_window_set_title(GTK_WINDOW(main_window), win_title);
#ifdef HAVE_SYSTRAY
	if (systray_used) {
		aqualung_status_icon_set_tooltip_text(systray_icon, win_title);
	}
#endif /* HAVE_SYSTRAY */

        g_signal_connect(G_OBJECT(main_window), "event", G_CALLBACK(main_window_event), NULL);

        g_signal_connect(G_OBJECT(main_window), "key_press_event", G_CALLBACK(main_window_key_pressed), NULL);
        g_signal_connect(G_OBJECT(main_window), "key_release_event",
			 G_CALLBACK(main_window_key_released), NULL);
        g_signal_connect(G_OBJECT(main_window), "button_press_event",
			 G_CALLBACK(main_window_button_pressed), NULL);
        g_signal_connect(G_OBJECT(main_window), "focus_out_event",
                         G_CALLBACK(main_window_focus_out), NULL);
        g_signal_connect(G_OBJECT(main_window), "window-state-event",
                         G_CALLBACK(main_window_state_changed), NULL);

	gtk_widget_set_events(main_window, GDK_BUTTON_PRESS_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK);
	gtk_container_set_border_width(GTK_CONTAINER(main_window), 5);

        /* always on top ? */

	if (options.main_window_always_on_top) {
                gtk_window_set_keep_above (GTK_WINDOW(main_window), TRUE);
        }

        /* initialize fonts */

	fd_playlist = pango_font_description_from_string(options.playlist_font);
 	fd_browser = pango_font_description_from_string(options.browser_font);
 	fd_bigtimer = pango_font_description_from_string(options.bigtimer_font);
 	fd_smalltimer = pango_font_description_from_string(options.smalltimer_font);
 	fd_songtitle = pango_font_description_from_string(options.songtitle_font);
 	fd_songinfo = pango_font_description_from_string(options.songinfo_font);
 	fd_statusbar = pango_font_description_from_string(options.statusbar_font);

	aqualung_tooltips_init();

        conf_menu = gtk_menu_new();
	register_toplevel_window(conf_menu, TOP_WIN_SKIN);

        if (options.playlist_is_embedded) {
                init_plist_menu(conf_menu);
                plist_menu = conf_menu;
        }

        conf__options = gtk_menu_item_new_with_label(_("Settings"));
        conf__skin = gtk_menu_item_new_with_label(_("Skin chooser"));
        conf__jack = gtk_menu_item_new_with_label(_("JACK port setup"));
        if (!options.playlist_is_embedded) {
                conf__fileinfo = gtk_menu_item_new_with_label(_("File info"));
	}
        conf__separator1 = gtk_separator_menu_item_new();
        conf__about = gtk_menu_item_new_with_label(_("About"));
        conf__separator2 = gtk_separator_menu_item_new();
        conf__quit = gtk_menu_item_new_with_label(_("Quit"));

        if (options.playlist_is_embedded) {
                gtk_menu_shell_append(GTK_MENU_SHELL(conf_menu), conf__separator1);
	}
        gtk_menu_shell_append(GTK_MENU_SHELL(conf_menu), conf__options);
        gtk_menu_shell_append(GTK_MENU_SHELL(conf_menu), conf__skin);

#ifdef HAVE_JACK
        if (output == JACK_DRIVER) {
                gtk_menu_shell_append(GTK_MENU_SHELL(conf_menu), conf__jack);
        }
#endif /* HAVE_JACK */

        if (!options.playlist_is_embedded) {
                gtk_menu_shell_append(GTK_MENU_SHELL(conf_menu), conf__fileinfo);
                gtk_menu_shell_append(GTK_MENU_SHELL(conf_menu), conf__separator1);
        }
        gtk_menu_shell_append(GTK_MENU_SHELL(conf_menu), conf__about);
	gtk_menu_shell_append(GTK_MENU_SHELL(conf_menu), conf__separator2);
        gtk_menu_shell_append(GTK_MENU_SHELL(conf_menu), conf__quit);

        g_signal_connect_swapped(G_OBJECT(conf__options), "activate", G_CALLBACK(conf__options_cb), NULL);
        g_signal_connect_swapped(G_OBJECT(conf__skin), "activate", G_CALLBACK(conf__skin_cb), NULL);
        g_signal_connect_swapped(G_OBJECT(conf__jack), "activate", G_CALLBACK(conf__jack_cb), NULL);

        g_signal_connect_swapped(G_OBJECT(conf__about), "activate", G_CALLBACK(conf__about_cb), NULL);
        g_signal_connect_swapped(G_OBJECT(conf__quit), "activate", G_CALLBACK(conf__quit_cb), NULL);

        if (!options.playlist_is_embedded) {
                g_signal_connect_swapped(G_OBJECT(conf__fileinfo), "activate", G_CALLBACK(conf__fileinfo_cb), NULL);
                gtk_widget_set_sensitive(conf__fileinfo, FALSE);
                gtk_widget_show(conf__fileinfo);
        }

        gtk_widget_show(conf__options);
        if (!options.disable_skin_support_settings) {
                gtk_widget_show(conf__skin);
        }
        gtk_widget_show(conf__jack);
        gtk_widget_show(conf__separator1);
        gtk_widget_show(conf__about);
        gtk_widget_show(conf__separator2);
        gtk_widget_show(conf__quit);


	vbox = gtk_vbox_new(FALSE, 0);
	gtk_container_add(GTK_CONTAINER(main_window), vbox);


	disp_hbox = gtk_hbox_new(FALSE, 3);
	gtk_box_pack_start(GTK_BOX(vbox), disp_hbox, FALSE, FALSE, 0);


	time_table = gtk_table_new(2, 2, FALSE);
	disp_vbox = gtk_vbox_new(FALSE, 0);

	gtk_box_pack_start(GTK_BOX(disp_hbox), time_table, FALSE, FALSE, 0);
	gtk_box_pack_start(GTK_BOX(disp_hbox), disp_vbox, TRUE, TRUE, 0);

	time0_viewp = gtk_viewport_new(NULL, NULL);
        time1_viewp = gtk_viewport_new(NULL, NULL);
	time2_viewp = gtk_viewport_new(NULL, NULL);

	gtk_widget_set_name(time0_viewp, "time_viewport");
	gtk_widget_set_name(time1_viewp, "time_viewport");
	gtk_widget_set_name(time2_viewp, "time_viewport");

	g_signal_connect(G_OBJECT(time0_viewp), "button_press_event", G_CALLBACK(time_label0_clicked), NULL);
	g_signal_connect(G_OBJECT(time1_viewp), "button_press_event", G_CALLBACK(time_label1_clicked), NULL);
	g_signal_connect(G_OBJECT(time2_viewp), "button_press_event", G_CALLBACK(time_label2_clicked), NULL);


	gtk_table_attach(GTK_TABLE(time_table), time0_viewp, 0, 2, 0, 1,
			 GTK_FILL | GTK_EXPAND, 0, 0, 0);
	gtk_table_attach(GTK_TABLE(time_table), time1_viewp, 0, 1, 1, 2,
			 GTK_FILL | GTK_EXPAND, 0, 0, 0);
	gtk_table_attach(GTK_TABLE(time_table), time2_viewp, 1, 2, 1, 2,
			 GTK_FILL | GTK_EXPAND, 0, 0, 0);


	info_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
        gtk_widget_set_size_request(info_scrolledwin, 1, -1);           /* MAGIC */
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(info_scrolledwin),
				       GTK_POLICY_NEVER, GTK_POLICY_NEVER);

	info_viewp = gtk_viewport_new(NULL, NULL);
	gtk_widget_set_name(info_viewp, "info_viewport");
	gtk_container_add(GTK_CONTAINER(info_scrolledwin), info_viewp);
	gtk_widget_set_events(info_viewp, GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK);

        g_signal_connect(G_OBJECT(info_viewp), "button_press_event",
			 G_CALLBACK(scroll_btn_pressed), NULL);
        g_signal_connect(G_OBJECT(info_viewp), "button_release_event",
			 G_CALLBACK(scroll_btn_released), (gpointer)info_scrolledwin);
        g_signal_connect(G_OBJECT(info_viewp), "motion_notify_event",
			 G_CALLBACK(scroll_motion_notify), (gpointer)info_scrolledwin);

	info_hbox = gtk_hbox_new(FALSE, 0);
	gtk_container_set_border_width(GTK_CONTAINER(info_hbox), 1);
	gtk_container_add(GTK_CONTAINER(info_viewp), info_hbox);


	title_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
        gtk_widget_set_size_request(title_scrolledwin, 1, -1);          /* MAGIC */
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(title_scrolledwin),
				       GTK_POLICY_NEVER, GTK_POLICY_NEVER);

	title_viewp = gtk_viewport_new(NULL, NULL);
	gtk_widget_set_name(title_viewp, "title_viewport");
	gtk_container_add(GTK_CONTAINER(title_scrolledwin), title_viewp);
	gtk_widget_set_events(title_viewp, GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK);
	g_signal_connect(G_OBJECT(title_viewp), "button_press_event", G_CALLBACK(scroll_btn_pressed), NULL);
	g_signal_connect(G_OBJECT(title_viewp), "button_release_event", G_CALLBACK(scroll_btn_released),
			 (gpointer)title_scrolledwin);
	g_signal_connect(G_OBJECT(title_viewp), "motion_notify_event", G_CALLBACK(scroll_motion_notify),
			 (gpointer)title_scrolledwin);

	title_hbox = gtk_hbox_new(FALSE, 0);
	gtk_container_set_border_width(GTK_CONTAINER(title_hbox), 1);
	gtk_container_add(GTK_CONTAINER(title_viewp), title_hbox);

	gtk_box_pack_start(GTK_BOX(disp_vbox), title_scrolledwin, TRUE, FALSE, 0);
	gtk_box_pack_start(GTK_BOX(disp_vbox), info_scrolledwin, TRUE, FALSE, 0);

	/* labels */
	bigtimer_label = time_labels[options.time_idx[0]] = gtk_label_new("");

        gtk_widget_set_name(time_labels[options.time_idx[0]], "big_timer_label");
	gtk_container_add(GTK_CONTAINER(time0_viewp), time_labels[options.time_idx[0]]);

	time_hbox1 = gtk_hbox_new(FALSE, 3);
	gtk_container_set_border_width(GTK_CONTAINER(time_hbox1), 2);
	gtk_container_add(GTK_CONTAINER(time1_viewp), time_hbox1);

	smalltimer_label_1 = time_labels[options.time_idx[1]] = gtk_label_new("");

        gtk_widget_set_name(time_labels[options.time_idx[1]], "small_timer_label");
	gtk_box_pack_start(GTK_BOX(time_hbox1), time_labels[options.time_idx[1]], TRUE, TRUE, 0);

	time_hbox2 = gtk_hbox_new(FALSE, 3);
	gtk_container_set_border_width(GTK_CONTAINER(time_hbox2), 2);
	gtk_container_add(GTK_CONTAINER(time2_viewp), time_hbox2);

	smalltimer_label_2 = time_labels[options.time_idx[2]] = gtk_label_new("");

        gtk_widget_set_name(time_labels[options.time_idx[2]], "small_timer_label");
	gtk_box_pack_start(GTK_BOX(time_hbox2), time_labels[options.time_idx[2]], TRUE, TRUE, 0);

	label_title = gtk_label_new("");
        gtk_widget_set_name(label_title, "label_title");
	gtk_box_pack_start(GTK_BOX(title_hbox), label_title, FALSE, FALSE, 3);

	label_mono = gtk_label_new("");
	gtk_box_pack_start(GTK_BOX(info_hbox), label_mono, FALSE, FALSE, 3);
	gtk_widget_set_name(label_mono, "label_info");

	label_samplerate = gtk_label_new("0");
	gtk_widget_set_name(label_samplerate, "label_info");
	gtk_box_pack_start(GTK_BOX(info_hbox), label_samplerate, FALSE, FALSE, 3);

	label_bps = gtk_label_new("");
	gtk_widget_set_name(label_bps, "label_info");
	gtk_box_pack_start(GTK_BOX(info_hbox), label_bps, FALSE, FALSE, 3);

	label_format = gtk_label_new("");
	gtk_widget_set_name(label_format, "label_info");
	gtk_box_pack_start(GTK_BOX(info_hbox), label_format, FALSE, FALSE, 3);

	info_vsep = gtk_vseparator_new();
	gtk_box_pack_start(GTK_BOX(info_hbox), info_vsep, FALSE, FALSE, 3);

	label_output = gtk_label_new("");
	gtk_widget_set_name(label_output, "label_info");
	gtk_box_pack_start(GTK_BOX(info_hbox), label_output, FALSE, FALSE, 3);

	label_src_type = gtk_label_new("");
	gtk_widget_set_name(label_src_type, "label_info");
	gtk_box_pack_start(GTK_BOX(info_hbox), label_src_type, FALSE, FALSE, 3);

	main_window_set_font(options.override_skin_settings);

	/* Volume and balance slider */
	vb_table = gtk_table_new(1, 3, FALSE);
        gtk_table_set_col_spacings(GTK_TABLE(vb_table), 3);
	gtk_box_pack_start(GTK_BOX(disp_vbox), vb_table, TRUE, FALSE, 0);

	adj_vol = gtk_adjustment_new(0.0f, -41.0f, 6.0f, 1.0f, 3.0f, 0.0f);
	g_signal_connect(G_OBJECT(adj_vol), "value_changed", G_CALLBACK(changed_vol), NULL);
	scale_vol = gtk_hscale_new(GTK_ADJUSTMENT(adj_vol));
	gtk_widget_set_name(scale_vol, "scale_vol");
	g_signal_connect(GTK_OBJECT(scale_vol), "button_press_event",
			   (GtkSignalFunc)scale_vol_button_press_event, NULL);
	g_signal_connect(GTK_OBJECT(scale_vol), "button_release_event",
			   (GtkSignalFunc)scale_vol_button_release_event, NULL);
	gtk_widget_set_size_request(scale_vol, -1, 8);
	gtk_scale_set_digits(GTK_SCALE(scale_vol), 0);
	gtk_scale_set_draw_value(GTK_SCALE(scale_vol), FALSE);
	gtk_table_attach(GTK_TABLE(vb_table), scale_vol, 0, 2, 0, 1,
			 GTK_FILL | GTK_EXPAND, 0, 0, 0);

        adj_bal = gtk_adjustment_new(0.0f, -100.0f, 100.0f, 1.0f, 10.0f, 0.0f);
        g_signal_connect(G_OBJECT(adj_bal), "value_changed", G_CALLBACK(changed_bal), NULL);
        scale_bal = gtk_hscale_new(GTK_ADJUSTMENT(adj_bal));
	gtk_scale_set_digits(GTK_SCALE(scale_bal), 0);
	gtk_widget_set_size_request(scale_bal, -1, 8);
	gtk_widget_set_name(scale_bal, "scale_bal");
	g_signal_connect(GTK_OBJECT(scale_bal), "button_press_event",
			   (GtkSignalFunc)scale_bal_button_press_event, NULL);
	g_signal_connect(GTK_OBJECT(scale_bal), "button_release_event",
			   (GtkSignalFunc)scale_bal_button_release_event, NULL);
        gtk_scale_set_draw_value(GTK_SCALE(scale_bal), FALSE);
	gtk_table_attach(GTK_TABLE(vb_table), scale_bal, 2, 3, 0, 1,
			 GTK_FILL | GTK_EXPAND, 0, 0, 0);

	/* Loop bar */

#ifdef HAVE_LOOP
	loop_bar = aqualung_loop_bar_new(options.loop_range_start, options.loop_range_end);
	g_signal_connect(loop_bar, "range-changed", G_CALLBACK(loop_range_changed_cb), NULL);
	gtk_widget_set_size_request(loop_bar, -1, 14);
	gtk_box_pack_start(GTK_BOX(vbox), loop_bar, FALSE, FALSE, 3);
#endif /* HAVE_LOOP */


	/* Position slider */
        adj_pos = gtk_adjustment_new(0, 0, 100, 1, 5, 0);
        g_signal_connect(G_OBJECT(adj_pos), "value_changed", G_CALLBACK(changed_pos), NULL);
        scale_pos = gtk_hscale_new(GTK_ADJUSTMENT(adj_pos));
	gtk_widget_set_name(scale_pos, "scale_pos");
	g_signal_connect(GTK_OBJECT(scale_pos), "button_press_event",
			   (GtkSignalFunc)scale_button_press_event, NULL);
	g_signal_connect(GTK_OBJECT(scale_pos), "button_release_event",
			   (GtkSignalFunc)scale_button_release_event, NULL);
	gtk_scale_set_digits(GTK_SCALE(scale_pos), 0);
        gtk_scale_set_draw_value(GTK_SCALE(scale_pos), FALSE);
	gtk_range_set_update_policy(GTK_RANGE(scale_pos), GTK_UPDATE_DISCONTINUOUS);
	gtk_box_pack_start(GTK_BOX(vbox), scale_pos, FALSE, FALSE, 3);

        vbox_sep = gtk_vbox_new (FALSE, 0);
	gtk_box_pack_start(GTK_BOX(vbox), vbox_sep, FALSE, FALSE, 2);

        GTK_WIDGET_UNSET_FLAGS(scale_vol, GTK_CAN_FOCUS);
        GTK_WIDGET_UNSET_FLAGS(scale_bal, GTK_CAN_FOCUS);
        GTK_WIDGET_UNSET_FLAGS(scale_pos, GTK_CAN_FOCUS);

        /* cover display widget */

	cover_align = gtk_alignment_new(0.5f, 0.5f, 0.0f, 0.0f);
	gtk_box_pack_start(GTK_BOX(disp_hbox), cover_align, FALSE, FALSE, 0);
        cover_image_area = gtk_image_new();
        c_event_box = gtk_event_box_new();
	gtk_container_add(GTK_CONTAINER(cover_align), c_event_box);
        gtk_container_add(GTK_CONTAINER(c_event_box), cover_image_area);
        g_signal_connect(G_OBJECT(c_event_box), "button_press_event",
                         G_CALLBACK(cover_press_button_cb), cover_image_area);

        /* Embedded playlist */

        if (options.playlist_is_embedded && options.buttons_at_the_bottom) {
		playlist_window = gtk_vbox_new(FALSE, 0);
		gtk_widget_set_name(playlist_window, "playlist_window");
		gtk_box_pack_start(GTK_BOX(vbox), playlist_window, TRUE, TRUE, 3);
	}

        /* Button box with prev, play, pause, stop, next buttons */

	btns_hbox = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(vbox), btns_hbox, FALSE, FALSE, 0);

	prev_button = gtk_button_new();
        aqualung_widget_set_tooltip_text(prev_button, _("Previous song"));

	stop_button = gtk_button_new();
        aqualung_widget_set_tooltip_text(stop_button, _("Stop"));

	next_button = gtk_button_new();
        aqualung_widget_set_tooltip_text(next_button, _("Next song"));

	play_button = gtk_toggle_button_new();

	if (options.combine_play_pause) {
		aqualung_widget_set_tooltip_text(play_button, _("Play/Pause"));
	} else {
		aqualung_widget_set_tooltip_text(play_button, _("Play"));
		pause_button = gtk_toggle_button_new();
		aqualung_widget_set_tooltip_text(pause_button, _("Pause"));
	}

	GTK_WIDGET_UNSET_FLAGS(prev_button, GTK_CAN_FOCUS);
	GTK_WIDGET_UNSET_FLAGS(stop_button, GTK_CAN_FOCUS);
	GTK_WIDGET_UNSET_FLAGS(next_button, GTK_CAN_FOCUS);
	GTK_WIDGET_UNSET_FLAGS(play_button, GTK_CAN_FOCUS);
	if (!options.combine_play_pause) {
		GTK_WIDGET_UNSET_FLAGS(pause_button, GTK_CAN_FOCUS);
	}

	gtk_box_pack_start(GTK_BOX(btns_hbox), prev_button, FALSE, FALSE, 0);
	gtk_box_pack_start(GTK_BOX(btns_hbox), play_button, FALSE, FALSE, 0);
	if (!options.combine_play_pause) {
		gtk_box_pack_start(GTK_BOX(btns_hbox), pause_button, FALSE, FALSE, 0);
	}
	gtk_box_pack_start(GTK_BOX(btns_hbox), stop_button, FALSE, FALSE, 0);
	gtk_box_pack_start(GTK_BOX(btns_hbox), next_button, FALSE, FALSE, 0);

	g_signal_connect(prev_button, "clicked", G_CALLBACK(prev_event), NULL);
	play_id = g_signal_connect(play_button, "toggled", G_CALLBACK(play_event), NULL);
	if (!options.combine_play_pause) {
		pause_id = g_signal_connect(pause_button, "toggled", G_CALLBACK(pause_event), NULL);
	}
	g_signal_connect(stop_button, "clicked", G_CALLBACK(stop_event), NULL);
	g_signal_connect(next_button, "clicked", G_CALLBACK(next_event), NULL);


	/* toggle buttons for shuffle and repeat */
	sr_table = gtk_table_new(2, 2, FALSE);

	repeat_button = gtk_toggle_button_new();
        aqualung_widget_set_tooltip_text(repeat_button, _("Repeat current song"));
	gtk_widget_set_size_request(repeat_button, -1, 1);
	gtk_table_attach(GTK_TABLE(sr_table), repeat_button, 0, 1, 0, 1,
			 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
	g_signal_connect(repeat_button, "toggled", G_CALLBACK(repeat_toggled), NULL);

	repeat_all_button = gtk_toggle_button_new();
        aqualung_widget_set_tooltip_text(repeat_all_button, _("Repeat all songs"));
	gtk_widget_set_size_request(repeat_all_button, -1, 1);
	gtk_table_attach(GTK_TABLE(sr_table), repeat_all_button, 0, 1, 1, 2,
			 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
	g_signal_connect(repeat_all_button, "toggled", G_CALLBACK(repeat_all_toggled), NULL);

	shuffle_button = gtk_toggle_button_new();
        aqualung_widget_set_tooltip_text(shuffle_button, _("Shuffle songs"));
	gtk_widget_set_size_request(shuffle_button, -1, 1);
	gtk_table_attach(GTK_TABLE(sr_table), shuffle_button, 1, 2, 0, 2,
			 GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 0, 0);
	g_signal_connect(shuffle_button, "toggled", G_CALLBACK(shuffle_toggled), NULL);

	GTK_WIDGET_UNSET_FLAGS(repeat_button, GTK_CAN_FOCUS);
	GTK_WIDGET_UNSET_FLAGS(repeat_all_button, GTK_CAN_FOCUS);
	GTK_WIDGET_UNSET_FLAGS(shuffle_button, GTK_CAN_FOCUS);

        /* toggle buttons for sub-windows visibility */
	playlist_toggle = gtk_toggle_button_new();
        aqualung_widget_set_tooltip_text(playlist_toggle, _("Toggle playlist"));
	gtk_box_pack_end(GTK_BOX(btns_hbox), playlist_toggle, FALSE, FALSE, 0);

	musicstore_toggle = gtk_toggle_button_new();
        aqualung_widget_set_tooltip_text(musicstore_toggle, _("Toggle music store"));
	gtk_box_pack_end(GTK_BOX(btns_hbox), musicstore_toggle, FALSE, FALSE, 3);

	plugin_toggle = gtk_toggle_button_new();
        aqualung_widget_set_tooltip_text(plugin_toggle, _("Toggle LADSPA patch builder"));
#ifdef HAVE_LADSPA
	gtk_box_pack_end(GTK_BOX(btns_hbox), plugin_toggle, FALSE, FALSE, 0);
#endif /* HAVE_LADSPA */

	g_signal_connect(playlist_toggle, "toggled", G_CALLBACK(playlist_toggled), NULL);
	g_signal_connect(musicstore_toggle, "toggled", G_CALLBACK(musicstore_toggled), NULL);
	g_signal_connect(plugin_toggle, "toggled", G_CALLBACK(plugin_toggled), NULL);

	GTK_WIDGET_UNSET_FLAGS(playlist_toggle, GTK_CAN_FOCUS);
	GTK_WIDGET_UNSET_FLAGS(musicstore_toggle, GTK_CAN_FOCUS);
	GTK_WIDGET_UNSET_FLAGS(plugin_toggle, GTK_CAN_FOCUS);

	gtk_box_pack_end(GTK_BOX(btns_hbox), sr_table, FALSE, FALSE, 3);

        if (options.disable_skin_support_settings) {
	        sprintf(path, "%s/no_skin", AQUALUNG_SKINDIR);
	        main_buttons_set_content(path);
        } else {
	        main_buttons_set_content(skin_path);
        }
        set_buttons_relief();

	/* Embedded playlist */
	if (options.playlist_is_embedded && !options.buttons_at_the_bottom) {
		playlist_window = gtk_vbox_new(FALSE, 0);
		gtk_widget_set_name(playlist_window, "playlist_window");
		gtk_box_pack_start(GTK_BOX(vbox), playlist_window, TRUE, TRUE, 3);
	}

	aqualung_tooltips_set_enabled(options.enable_tooltips);
}


void
process_filenames(GSList * list, int enqueue, int start_playback, int has_tab, char * tab_name) {

	int mode;
	char * name = NULL;

	if (!has_tab) {
		if (enqueue) {
			mode = PLAYLIST_ENQUEUE;
		} else {
			mode = PLAYLIST_LOAD;
		}
	} else {
		if (strlen(tab_name) == 0) {
			mode = PLAYLIST_LOAD_TAB;
		} else {
			if (g_utf8_validate(tab_name, -1, NULL)) {
				name = g_strdup(tab_name);
			} else {
				name = g_locale_to_utf8(tab_name, -1, NULL, NULL, NULL);
			}
			if (name == NULL) {
				fprintf(stderr, "process_filenames(): unable to convert tab name to UTF-8\n");
			}

			if (enqueue) {
				mode = PLAYLIST_ENQUEUE;
			} else {
				mode = PLAYLIST_LOAD;
			}
		}
	}

	playlist_load(list, mode, name, start_playback);

	if (name != NULL) {
		g_free(name);
	}
}


/*** Systray support ***/
#ifdef HAVE_SYSTRAY
void
systray_popup_menu_cb(GtkStatusIcon * systray_icon, guint button, guint time, gpointer data) {

	if (get_systray_semaphore() == 0) {
		gtk_menu_popup(GTK_MENU(systray_menu), NULL, NULL,
			       gtk_status_icon_position_menu, data,
			       button, time);
	}
}

void
systray__show_cb(gpointer data) {

	show_all_windows(NULL);
}

void
systray__hide_cb(gpointer data) {

	hide_all_windows(NULL);
}

void
systray__play_cb(gpointer data) {

	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(play_button),
				     !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(play_button)));
}

void
systray__pause_cb(gpointer data) {

	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pause_button),
				     !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pause_button)));
}

void
systray__stop_cb(gpointer data) {

	stop_event(NULL, NULL, NULL);
}

void
systray__prev_cb(gpointer data) {

	prev_event(NULL, NULL, NULL);
}

void
systray__next_cb(gpointer data) {

	next_event(NULL, NULL, NULL);
}

void
systray__quit_cb(gpointer data) {

	main_window_closing();
}

void
systray_activate_cb(GtkStatusIcon * systray_icon, gpointer data) {

	if (get_systray_semaphore() == 0) {
		if (!systray_main_window_on) {
			systray__show_cb(NULL);
		} else {
			systray__hide_cb(NULL);
		}
	}
}

#if (GTK_CHECK_VERSION(2,15,0))
gboolean
systray_mouse_wheel(int mouse_wheel_option, gboolean vertical_wheel,
       	guint32 time_since_last_event, GtkScrollType scroll_type) {

	GtkScrollType reverse_direction(GtkScrollType scroll_type) {
		switch (scroll_type) {
		case GTK_SCROLL_STEP_BACKWARD:
			return GTK_SCROLL_STEP_FORWARD;
		case GTK_SCROLL_STEP_FORWARD:
			return GTK_SCROLL_STEP_BACKWARD;
		default:
			return scroll_type;
		}
	}

	switch (mouse_wheel_option) {
	case SYSTRAY_MW_CMD_VOLUME:
		if (time_since_last_event > 100) {
			if (vertical_wheel)
				scroll_type = reverse_direction(scroll_type);
			change_volume_or_balance(scale_vol, scroll_type);
			return TRUE;
		}
		break;
	case SYSTRAY_MW_CMD_BALANCE:
		if (time_since_last_event > 100) {
			if (vertical_wheel)
				scroll_type = reverse_direction(scroll_type);
			change_volume_or_balance(scale_bal, scroll_type);
			return TRUE;
		}
		break;
	case SYSTRAY_MW_CMD_SONG_POSITION:
		move_song_position_slider(scroll_type);
		seek_song();
		return TRUE;
	case SYSTRAY_MW_CMD_NEXT_PREV_SONG:
		// At most one per second.
		if (time_since_last_event > 1000) {
			if (scroll_type == GTK_SCROLL_STEP_BACKWARD) {
				prev_event(NULL, NULL, NULL);
				return TRUE;
			} else if (scroll_type == GTK_SCROLL_STEP_FORWARD) {
				next_event(NULL, NULL, NULL);
				return TRUE;
			}
		}
		break;
	}

	return FALSE;
}

gboolean
systray_scroll_event_cb(GtkStatusIcon * systray_icon,
			GdkEventScroll * event,
			gpointer user_data) {

	gboolean result = FALSE;
	guint32 time_since_last_event;

	if (!options.use_systray)
		return FALSE;

	time_since_last_event = event->time - last_systray_scroll_event_time;

	switch (event->direction) {
	case GDK_SCROLL_LEFT:
		result = systray_mouse_wheel(options.systray_mouse_wheel_horizontal,
		        FALSE, time_since_last_event, GTK_SCROLL_STEP_BACKWARD);
		break;
	case GDK_SCROLL_RIGHT:
		result = systray_mouse_wheel(options.systray_mouse_wheel_horizontal,
		        FALSE, time_since_last_event, GTK_SCROLL_STEP_FORWARD);
		break;
	case GDK_SCROLL_UP:
		result = systray_mouse_wheel(options.systray_mouse_wheel_vertical,
		        TRUE, time_since_last_event, GTK_SCROLL_STEP_BACKWARD);
		break;
	case GDK_SCROLL_DOWN:
		result = systray_mouse_wheel(options.systray_mouse_wheel_vertical,
		        TRUE, time_since_last_event, GTK_SCROLL_STEP_FORWARD);
		break;
	}

	if (result) {
		last_systray_scroll_event_time = event->time;
	}

	return result;
}

gboolean
systray_mouse_button(int mouse_button_option) {

	switch (mouse_button_option) {
	case SYSTRAY_MB_CMD_PLAY_STOP_SONG:
		if (is_file_loaded) {
			systray__stop_cb(NULL);
		} else {
			systray__play_cb(NULL);
		}
		return TRUE;
	case SYSTRAY_MB_CMD_PLAY_PAUSE_SONG:
		if (is_file_loaded && !options.combine_play_pause) {
			systray__pause_cb(NULL);
		} else {
			systray__play_cb(NULL);
		}
		return TRUE;
	case SYSTRAY_MB_CMD_PREV_SONG:
		systray__prev_cb(NULL);
		return TRUE;
	case SYSTRAY_MB_CMD_NEXT_SONG:
		systray__next_cb(NULL);
		return TRUE;
	}
	return FALSE;
}

gboolean
systray_button_press_event_cb(GtkStatusIcon * systray_icon,
                              GdkEventButton * event,
                              gpointer user_data) {

	int i;

	if (!options.use_systray)
		return FALSE;

	if (event->type == GDK_BUTTON_PRESS) {
		for (i = 0; i < options.systray_mouse_buttons_count; i++) {
			if (options.systray_mouse_buttons[i].button_nr == event->button) {
				return systray_mouse_button(options.systray_mouse_buttons[i].command);
			}
		}
	}

	return FALSE;
}
#endif /* GTK_CHECK_VERSION */

/* returns a hbox with a stock image and label in it */
GtkWidget *
create_systray_menu_item(const gchar * stock_id, char * text) {

	GtkWidget * hbox;
	GtkWidget * widget;

	hbox = gtk_hbox_new(FALSE, 5);
	gtk_widget_show(hbox);
	widget = gtk_image_new_from_stock(stock_id, GTK_ICON_SIZE_MENU);
	gtk_widget_show(widget);
	gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 2);
	widget = gtk_label_new(text);
	gtk_widget_show(widget);
	gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 2);

	return hbox;
}

void
setup_systray(void) {

	char path[MAXLEN];

	GtkWidget * systray__separator1;
	GtkWidget * systray__separator2;

	sprintf(path, "%s/icon_64.png", AQUALUNG_DATADIR);
	systray_icon = gtk_status_icon_new_from_file(path);

        g_signal_connect_swapped(G_OBJECT(systray_icon), "activate",
				 G_CALLBACK(systray_activate_cb), NULL);
        g_signal_connect_swapped(G_OBJECT(systray_icon), "popup-menu",
				 G_CALLBACK(systray_popup_menu_cb), (gpointer)systray_icon);
#if (GTK_CHECK_VERSION(2,15,0))
	g_signal_connect(G_OBJECT(systray_icon), "scroll-event",
			 G_CALLBACK(systray_scroll_event_cb), NULL);
	g_signal_connect(G_OBJECT(systray_icon), "button-press-event",
			 G_CALLBACK(systray_button_press_event_cb), NULL);
#endif

        systray_menu = gtk_menu_new();
	register_toplevel_window(systray_menu, TOP_WIN_SKIN);

        systray__show = gtk_menu_item_new_with_label(_("Show Aqualung"));
        systray__hide = gtk_menu_item_new_with_label(_("Hide Aqualung"));

        systray__separator1 = gtk_separator_menu_item_new();

        systray__play = gtk_menu_item_new();
	gtk_container_add(GTK_CONTAINER(systray__play),
			create_systray_menu_item(GTK_STOCK_MEDIA_PLAY,
			options.combine_play_pause ? _("Play/Pause") : _("Play")));

	if (!options.combine_play_pause) {
		systray__pause = gtk_menu_item_new();
		gtk_container_add(GTK_CONTAINER(systray__pause),
				  create_systray_menu_item(GTK_STOCK_MEDIA_PAUSE, _("Pause")));
	}

        systray__stop = gtk_menu_item_new();
	gtk_container_add(GTK_CONTAINER(systray__stop),
			  create_systray_menu_item(GTK_STOCK_MEDIA_STOP, _("Stop")));

        systray__prev = gtk_menu_item_new();
	gtk_container_add(GTK_CONTAINER(systray__prev),
			  create_systray_menu_item(GTK_STOCK_MEDIA_PREVIOUS, _("Previous")));

        systray__next = gtk_menu_item_new();
	gtk_container_add(GTK_CONTAINER(systray__next),
			  create_systray_menu_item(GTK_STOCK_MEDIA_NEXT, _("Next")));

        systray__separator2 = gtk_separator_menu_item_new();

        systray__quit = gtk_menu_item_new();
	gtk_container_add(GTK_CONTAINER(systray__quit),
			  create_systray_menu_item(GTK_STOCK_STOP, _("Quit")));

        gtk_menu_shell_append(GTK_MENU_SHELL(systray_menu), systray__show);
        gtk_menu_shell_append(GTK_MENU_SHELL(systray_menu), systray__hide);
        gtk_menu_shell_append(GTK_MENU_SHELL(systray_menu), systray__separator1);
        gtk_menu_shell_append(GTK_MENU_SHELL(systray_menu), systray__play);
	if (!options.combine_play_pause) {
		gtk_menu_shell_append(GTK_MENU_SHELL(systray_menu), systray__pause);
	}
        gtk_menu_shell_append(GTK_MENU_SHELL(systray_menu), systray__stop);
        gtk_menu_shell_append(GTK_MENU_SHELL(systray_menu), systray__prev);
        gtk_menu_shell_append(GTK_MENU_SHELL(systray_menu), systray__next);
        gtk_menu_shell_append(GTK_MENU_SHELL(systray_menu), systray__separator2);
        gtk_menu_shell_append(GTK_MENU_SHELL(systray_menu), systray__quit);

        g_signal_connect_swapped(G_OBJECT(systray__show), "activate", G_CALLBACK(systray__show_cb), NULL);
        g_signal_connect_swapped(G_OBJECT(systray__hide), "activate", G_CALLBACK(systray__hide_cb), NULL);
        g_signal_connect_swapped(G_OBJECT(systray__play), "activate", G_CALLBACK(systray__play_cb), NULL);
        if (!options.combine_play_pause) {
		g_signal_connect_swapped(G_OBJECT(systray__pause), "activate", G_CALLBACK(systray__pause_cb), NULL);
	}
        g_signal_connect_swapped(G_OBJECT(systray__stop), "activate", G_CALLBACK(systray__stop_cb), NULL);
        g_signal_connect_swapped(G_OBJECT(systray__prev), "activate", G_CALLBACK(systray__prev_cb), NULL);
        g_signal_connect_swapped(G_OBJECT(systray__next), "activate", G_CALLBACK(systray__next_cb), NULL);
        g_signal_connect_swapped(G_OBJECT(systray__quit), "activate", G_CALLBACK(systray__quit_cb), NULL);

        gtk_widget_show(systray_menu);
        gtk_widget_show(systray__hide);
        gtk_widget_show(systray__separator1);
	gtk_widget_show(systray__play);
	if (!options.combine_play_pause) {
		gtk_widget_show(systray__pause);
	}
	gtk_widget_show(systray__stop);
	gtk_widget_show(systray__prev);
	gtk_widget_show(systray__next);

        gtk_widget_show(systray__separator2);
        gtk_widget_show(systray__quit);
}
#endif /* HAVE_SYSTRAY */

void
create_gui(int argc, char ** argv, int optind, int enqueue,
	   unsigned long rate, unsigned long rb_audio_size) {

	char path[MAXLEN];
	GList * glist = NULL;
	GdkPixbuf * pixbuf = NULL;

	srand(time(0));
	sample_pos = 0;
	out_SR = rate;
	rb_size = rb_audio_size;

	gdk_threads_enter();
	gtk_init(&argc, &argv);
#ifdef HAVE_LADSPA
	lrdf_init();
#endif /* HAVE_LADSPA */

	if (chdir(options.confdir) != 0) {
		if (errno == ENOENT) {
			fprintf(stderr, "Creating directory %s\n", options.confdir);
			mkdir(options.confdir, S_IRUSR | S_IWUSR | S_IXUSR);
			chdir(options.confdir);
		} else {
			fprintf(stderr, "An error occured while attempting chdir(\"%s\"). errno = %d\n",
				options.confdir, errno);
		}
	}

	load_config();

	if (options.title_format[0] == '\0')
		sprintf(options.title_format, "%%a: %%t [%%r]");
	if (options.skin[0] == '\0') {
		sprintf(options.skin, "%s/plain", AQUALUNG_SKINDIR);
		options.main_pos_x = 280;
		options.main_pos_y = 30;
		options.main_size_x = 380;
		options.main_size_y = 380;
		options.browser_pos_x = 30;
		options.browser_pos_y = 30;
		options.browser_size_x = 240;
		options.browser_size_y = 380;
		options.browser_on = 1;
		options.playlist_pos_x = 300;
		options.playlist_pos_y = 180;
		options.playlist_size_x = 400;
		options.playlist_size_y = 500;
		options.playlist_on = 1;
	}

	if (options.cddb_server[0] == '\0') {
		sprintf(options.cddb_server, "freedb.org");
	}

	if (options.src_type == -1) {
		options.src_type = 4;
	}

        if (options.disable_skin_support_settings) {
	        sprintf(path, "%s/no_skin/rc", AQUALUNG_SKINDIR);
        } else {
	        sprintf(path, "%s/rc", options.skin);
        }
	gtk_rc_parse(path);

#ifdef HAVE_SYSTRAY
	if (options.use_systray) {
		systray_used = 1;
		setup_systray();
	}
#endif /* HAVE_SYSTRAY */

	create_main_window(options.skin);

	vol_prev = -101.0f;
	gtk_adjustment_set_value(GTK_ADJUSTMENT(adj_vol), options.vol);
	bal_prev = -101.0f;
	gtk_adjustment_set_value(GTK_ADJUSTMENT(adj_bal), options.bal);

	create_playlist();
	playlist_auto_save_reset();

	create_music_browser();

#ifdef HAVE_LADSPA
	create_fxbuilder();
	load_plugin_data();
#endif /* HAVE_LADSPA */

#ifdef HAVE_CDDA
	create_cdda_node();
	cdda_scanner_start();
#endif /* HAVE_CDDA */

#ifdef HAVE_PODCAST
	create_podcast_node();
	store_podcast_load();
	store_podcast_updater_start();
#endif /* HAVE_PODCAST */

	music_store_load_all();

	options_store_watcher_start();

	sprintf(path, "%s/icon_16.png", AQUALUNG_DATADIR);
	if ((pixbuf = gdk_pixbuf_new_from_file(path, NULL)) != NULL) {
		glist = g_list_append(glist, gdk_pixbuf_new_from_file(path, NULL));
	}

	sprintf(path, "%s/icon_24.png", AQUALUNG_DATADIR);
	if ((pixbuf = gdk_pixbuf_new_from_file(path, NULL)) != NULL) {
		glist = g_list_append(glist, gdk_pixbuf_new_from_file(path, NULL));
	}

	sprintf(path, "%s/icon_32.png", AQUALUNG_DATADIR);
	if ((pixbuf = gdk_pixbuf_new_from_file(path, NULL)) != NULL) {
		glist = g_list_append(glist, gdk_pixbuf_new_from_file(path, NULL));
	}

	sprintf(path, "%s/icon_48.png", AQUALUNG_DATADIR);
	if ((pixbuf = gdk_pixbuf_new_from_file(path, NULL)) != NULL) {
		glist = g_list_append(glist, gdk_pixbuf_new_from_file(path, NULL));
	}

	sprintf(path, "%s/icon_64.png", AQUALUNG_DATADIR);
	if ((pixbuf = gdk_pixbuf_new_from_file(path, NULL)) != NULL) {
		glist = g_list_append(glist, gdk_pixbuf_new_from_file(path, NULL));
	}

	if (glist != NULL) {
		gtk_window_set_default_icon_list(glist);
		g_list_free(glist);
	}

	if (options.repeat_on) {
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(repeat_button), TRUE);
	}

	if (options.repeat_all_on) {
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(repeat_all_button), TRUE);
	}

	if (options.shuffle_on) {
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(shuffle_button), TRUE);
	}

        if (playlist_state != -1) {
                options.playlist_on = playlist_state;
	}

        if (browser_state != -1) {
                options.browser_on = browser_state;
	}

	if (options.browser_on) {
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(musicstore_toggle), TRUE);
	}

	if (options.playlist_on) {
		if (options.playlist_is_embedded) {
			g_signal_handlers_block_by_func(G_OBJECT(playlist_toggle), playlist_toggled, NULL);
		}
		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(playlist_toggle), TRUE);
		if (options.playlist_is_embedded) {
			g_signal_handlers_unblock_by_func(G_OBJECT(playlist_toggle), playlist_toggled, NULL);
		}
	}

	restore_window_position();

	gtk_widget_show_all(main_window);

#ifdef HAVE_SYSTRAY
	if (options.use_systray && options.systray_start_minimized) {
		toplevel_window_foreach(TOP_WIN_TRAY, gtk_widget_hide);
		systray_main_window_on = 0;
	}
#endif /* HAVE_SYSTRAY */

        hide_cover_thumbnail();

        gtk_widget_hide(vbox_sep);

	if (options.playlist_is_embedded) {
		if (!options.playlist_on) {
                        hide_playlist();
		}
	}

#ifdef HAVE_LOOP
	if (!options.repeat_on) {
		hide_loop_bar();
	}
#endif /* HAVE_LOOP */


        /* update sliders' tooltips */
        if (options.enable_tooltips) {
                changed_vol(GTK_ADJUSTMENT(adj_vol), NULL);
                changed_bal(GTK_ADJUSTMENT(adj_bal), NULL);
	        changed_pos(GTK_ADJUSTMENT(adj_pos), NULL);
        }

	zero_displays();
	if (options.auto_save_playlist) {
		/* start playback only if no files to be loaded on command line */
		int start_playback = (argv[optind] == NULL) && immediate_start;
		char file[MAXLEN];

		snprintf(file, MAXLEN-1, "%s/%s", options.confdir, "playlist.xml");
		if (g_file_test(file, G_FILE_TEST_EXISTS) == TRUE) {
			GSList * list = g_slist_append(NULL, strdup(file));
			playlist_load(list, PLAYLIST_LOAD_TAB, NULL, start_playback);
		}
	}

	/* read command line filenames */
	{
		int i;
		GSList * list = NULL;

		for (i = optind; argv[i] != NULL; i++) {
			list = g_slist_append(list, strdup(argv[i]));
		}

		if (list != NULL) {
			process_filenames(list, enqueue, immediate_start, (tab_name != NULL), tab_name);
		}
	}

	/* ensure that at least one playlist has been created */
	playlist_ensure_tab_exists();

	playlist_set_color();

	/* activate jack client and connect ports */
#ifdef HAVE_JACK
	if (output == JACK_DRIVER) {
		jack_client_start();
	}
#endif /* HAVE_JACK */

	/* set timeout function */
	timeout_tag = aqualung_timeout_add(TIMEOUT_PERIOD, timeout_callback, NULL);

        if (options.playlist_is_embedded) {
                gtk_widget_set_sensitive(plist__fileinfo, FALSE);
        }

	/* re-apply skin to override possible WM theme */
	if (!options.disable_skin_support_settings) {
		apply_skin(options.skin);
	}
}


void
adjust_remote_volume(char * str) {

	char * endptr = NULL;
	int val = gtk_adjustment_get_value(GTK_ADJUSTMENT(adj_vol));

	switch (str[0]) {
	case 'm':
	case 'M':
		val = -41;
		break;
	case '=':
		val = strtol(str + 1, &endptr, 10);
		if (endptr[0] != '\0') {
			fprintf(stderr, "Cannot convert to integer value: %s\n", str + 1);
			return;
		}
		break;
	default:
		val += strtol(str, &endptr, 10);
		if (endptr[0] != '\0') {
			fprintf(stderr, "Cannot convert to integer value: %s\n", str);
			return;
		}
		break;
	}

	gtk_adjustment_set_value(GTK_ADJUSTMENT(adj_vol), val);
}


void
process_metablock(metadata_t * meta) {

	char buf[MAXLEN];
	char tmp[MAXLEN];
	playlist_t * pl;
	file_decoder_t * fdec = (file_decoder_t *)meta->fdec;
	char * artist = NULL;
	char * album = NULL;
	char * title = NULL;
	char * icy_name = NULL;
	char * icy_descr = NULL;
	meta_frame_t * frame;

	frame = metadata_get_frame_by_type(meta, META_FIELD_APIC, NULL);
	if (frame != NULL) {
		if (embedded_picture != NULL) {
			free(embedded_picture);
		}
		embedded_picture_size = frame->length;
		embedded_picture = malloc(embedded_picture_size);
		if (embedded_picture == NULL) {
			embedded_picture_size = 0;
		} else {
			memcpy(embedded_picture, frame->data, embedded_picture_size);
		}
	} else {
		if (embedded_picture != NULL) {
			free(embedded_picture);
			embedded_picture = NULL;
		}
		embedded_picture_size = 0;
	}

	if ((!httpc_is_url(fdec->filename)) && !options.use_ext_title_format) {
		return;
	}

	buf[0] = '\0';
	tmp[0] = '\0';

	// Abuse artist variable
	if ((artist = application_title_format(fdec)) != NULL) {
		strncpy(buf, artist, MAXLEN-1);
		free(artist);
		artist = NULL;
	}

	if (buf[0] == '\0') {
		metadata_get_artist(meta, &artist);
		metadata_get_album(meta, &album);
		metadata_get_title(meta, &title);
		metadata_get_icy_name(meta, &icy_name);

		if ((artist && !is_all_wspace(artist)) ||
		    (album && !is_all_wspace(album)) ||
		    (title && !is_all_wspace(title))) {
			make_title_string(tmp, options.title_format, artist, album, title);
			if (icy_name != NULL) {
				snprintf(buf, MAXLEN-1, "%s (%s)", tmp, icy_name);
			}
		} else if (icy_name != NULL) {
			strncpy(buf, icy_name, MAXLEN-1);
		}
	}

	if (buf[0] != '\0') {
		set_title_label(buf);
	} else {
		set_title_label(fdec->filename);
	}

	if (icy_name == NULL) {
		return;
	}

	buf[0] = '\0';

	metadata_get_icy_descr(meta, &icy_descr);

	if (icy_descr != NULL) {
		snprintf(buf, MAXLEN-1, "%s (%s)", icy_name, icy_descr);
	} else {
		strncpy(buf, icy_name, MAXLEN-1);
	}

	/* set playlist_str for playlist entry */
	if ((pl = playlist_get_playing()) != NULL) {
		GtkTreePath * p = playlist_get_playing_path(pl);
		if (p != NULL) {
			GtkTreeIter iter;
			playlist_data_t * data;

			gtk_tree_model_get_iter(GTK_TREE_MODEL(pl->store), &iter, p);
			gtk_tree_model_get(GTK_TREE_MODEL(pl->store), &iter, PL_COL_DATA, &data, -1);

			free_strdup(&data->title, buf);
			gtk_tree_store_set(pl->store, &iter, PL_COL_NAME, buf, -1);

			gtk_tree_path_free(p);
		}
	}
}

void
flush_rb_disk2gui(void) {

	char recv_cmd;

	while (rb_read_space(rb_disk2gui)) {
		rb_read(rb_disk2gui, &recv_cmd, 1);
	}
}

gint
timeout_callback(gpointer data) {

	char cmd;
	cue_t cue;
	static double left_gain_shadow;
	static double right_gain_shadow;
	char rcmd;
	static char cmdbuf[MAXLEN];
	int rcv_count;
	static GSList * file_list = NULL;
#ifdef HAVE_JACK
	static int jack_popup_beenthere = 0;
#endif /* HAVE_JACK */
	metadata_t * meta;


	while (rb_read_space(rb_disk2gui)) {
		rb_read(rb_disk2gui, &recv_cmd, 1);
		switch (recv_cmd) {

		case CMD_FILEREQ:
			cmd = CMD_CUE;
			cue.filename = NULL;
			cue.voladj = 0.0f;

			if (!is_file_loaded)
				break; /* ignore leftover filereq message */

			decide_next_track(&cue);

			if (cue.filename != NULL) {
				rb_write(rb_gui2disk, &cmd, sizeof(char));
				rb_write(rb_gui2disk, (void *)&cue, sizeof(cue_t));
			} else {
				send_cmd = CMD_STOPWOFL;
				rb_write(rb_gui2disk, &send_cmd, sizeof(char));
			}

			try_waking_disk_thread();
			break;

		case CMD_FILEINFO:
			while (rb_read_space(rb_disk2gui) < sizeof(fileinfo_t))
				;
			rb_read(rb_disk2gui, (char *)&fileinfo, sizeof(fileinfo_t));

			sample_pos = 0;
			total_samples = fileinfo.total_samples;
			status.samples_left = fileinfo.total_samples;
			status.sample_pos = 0;
			status.sample_offset = 0;
			fresh_new_file = fresh_new_file_prev = 0;
			break;

		case CMD_STATUS:
			while (rb_read_space(rb_disk2gui) < sizeof(status_t))
				;
			rb_read(rb_disk2gui, (char *)&status, sizeof(status_t));

			sample_pos = total_samples - status.samples_left;
#ifdef HAVE_LOOP
			if (options.repeat_on &&
			    (sample_pos < total_samples * options.loop_range_start ||
			     sample_pos > total_samples * options.loop_range_end)) {

				seek_t seek;

				send_cmd = CMD_SEEKTO;
				seek.seek_to_pos = options.loop_range_start * total_samples;
				rb_write(rb_gui2disk, &send_cmd, 1);
				rb_write(rb_gui2disk, (char *)&seek, sizeof(seek_t));
				try_waking_disk_thread();
				refresh_scale_suppress = 2;
			}
#endif /* HAVE_LOOP */

			if ((is_file_loaded) && (status.samples_left < 2*status.sample_offset)) {
				allow_seeks = 0;
			} else {
				allow_seeks = 1;
			}

			/* treat files with unknown length */
			if (total_samples == 0) {
				allow_seeks = 1;
				sample_pos = status.sample_pos - status.sample_offset;
			}

			if ((!fresh_new_file) && (sample_pos > status.sample_offset)) {
				fresh_new_file = 1;
			}

			if (fresh_new_file && !fresh_new_file_prev) {
				disp_info = fileinfo;
				disp_samples = total_samples;
				if (sample_pos > status.sample_offset) {
					disp_pos = sample_pos - status.sample_offset;
				} else {
					disp_pos = 0;
				}
				refresh_displays();
			} else {
				if (sample_pos > status.sample_offset) {
					disp_pos = sample_pos - status.sample_offset;
				} else {
					disp_pos = 0;
				}

				if (disp_info.sample_rate != status.sample_rate) {
					disp_info.sample_rate = status.sample_rate;
					refresh_displays();
				}

				if (is_file_loaded) {
					refresh_time_displays();
				}
			}

			fresh_new_file_prev = fresh_new_file;

			if (refresh_scale && !refresh_scale_suppress) {
				if (total_samples == 0) {
					gtk_adjustment_set_value(GTK_ADJUSTMENT(adj_pos), 0.0f);
				} else {
					gtk_adjustment_set_value(GTK_ADJUSTMENT(adj_pos),
								 100.0 * sample_pos / total_samples);
				}
			}

			if (refresh_scale_suppress > 0) {
				--refresh_scale_suppress;
			}

                        show_scale_pos(!total_samples ? FALSE : TRUE);
			break;

		case CMD_METABLOCK:
			while (rb_read_space(rb_disk2gui) < sizeof(metadata_t *))
				;
			rb_read(rb_disk2gui, (char *)&meta, sizeof(metadata_t *));

			process_metablock(meta);
			break;

		default:
			fprintf(stderr, "gui: unexpected command %d recv'd from disk\n", recv_cmd);
			break;
		}
	}
        /* update volume & balance if necessary */
	if ((options.vol != vol_prev) || (options.bal != bal_prev)) {
		vol_prev = options.vol;
		vol_lin = (options.vol < -40.5f) ? 0 : db2lin(options.vol);
		bal_prev = options.bal;
		if (options.bal >= 0.0f) {
			left_gain_shadow = vol_lin * db2lin(-0.4f * options.bal);
			right_gain_shadow = vol_lin;
		} else {
			left_gain_shadow = vol_lin;
			right_gain_shadow = vol_lin * db2lin(0.4f * options.bal);
		}
			left_gain = left_gain_shadow;
			right_gain = right_gain_shadow;
	}

	/* receive and execute remote commands, if any */
	rcv_count = 0;
	while (((rcmd = receive_message(aqualung_socket_fd, cmdbuf)) != 0) && (rcv_count < MAX_RCV_COUNT)) {
		switch (rcmd) {
		case RCMD_BACK:
			prev_event(NULL, NULL, NULL);
			break;
		case RCMD_PAUSE:
			if (!options.combine_play_pause) {
				gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pause_button),
				        !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pause_button)));
				break;
			}
		case RCMD_PLAY:
			gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(play_button),
			     !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(play_button)));
			break;
		case RCMD_STOP:
			stop_event(NULL, NULL, NULL);
			break;
		case RCMD_FWD:
			next_event(NULL, NULL, NULL);
			break;
		case RCMD_ADD_FILE:
			file_list = g_slist_append(file_list, strdup(cmdbuf));
			break;
		case RCMD_ADD_COMMIT:
			process_filenames(file_list, cmdbuf[0], cmdbuf[1], cmdbuf[2], cmdbuf+3);
			file_list = NULL;
			break;
		case RCMD_CUSTOM:
			run_custom_remote_command(cmdbuf);
			break;
		case RCMD_VOLADJ:
			adjust_remote_volume(cmdbuf);
			break;
		case RCMD_QUIT:
			main_window_closing();
			break;
		default:
			fprintf(stderr, "Unrecognized remote command received: %i\n", (int)rcmd);
		}
		++rcv_count;
	}

	/* check for JACK shutdown condition */
#ifdef HAVE_JACK
	if (output == JACK_DRIVER) {
		if (jack_is_shutdown) {
			if (is_file_loaded) {
				stop_event(NULL, NULL, NULL);
			}
			if (!jack_popup_beenthere) {
				jack_shutdown_window();
				if (ports_window) {
					ports_clicked_close(NULL, NULL);
				}
				jack_client_close(jack_client);
				gtk_widget_set_sensitive(conf__jack, FALSE);
				jack_popup_beenthere = 1;
			}
		}
	}
#endif /* HAVE_JACK */

	return TRUE;
}


void
run_gui(void) {

	gtk_main();
	gdk_threads_leave();

	return;
}


void
set_buttons_relief(void) {

	GtkWidget *rbuttons_table[] = {
		prev_button, stop_button, next_button, play_button,
		pause_button, repeat_button, repeat_all_button, shuffle_button,
		playlist_toggle, musicstore_toggle, plugin_toggle
	};

	gint i, n;
        i = sizeof(rbuttons_table)/sizeof(GtkWidget*);

        for (n = 0; n < i; n++) {
		if ((rbuttons_table[n] != pause_button) || !options.combine_play_pause) {
			gtk_button_set_relief(GTK_BUTTON(rbuttons_table[n]),
				(options.disable_buttons_relief) ? GTK_RELIEF_NONE : GTK_RELIEF_NORMAL);
		}
	}
}

// vim: shiftwidth=8:tabstop=8:softtabstop=8 :

