/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 *  Authors: Iain Holmes <iain@ximian.com>
 *
 *  Copyright 2001 Iain Holmes
 *
 *  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., 59 Temple Street #330, Boston, MA 02111-1307, USA.
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <glib/gi18n.h>
#include <glib/gerror.h>
#include <gtk/gtkmain.h>
#include <gtk/gtkrange.h>
#include <gtk/gtkbutton.h>
#include <gtk/gtkoptionmenu.h>
#include <gtk/gtkmessagedialog.h>
#include <gdk/gdkkeysyms.h>
#include <libgnome/gnome-help.h>
#include <gtk/gtk.h>
#include <gconf/gconf-client.h>

#include <bonobo/bonobo-exception.h>
#include <bonobo/bonobo-control.h>
#include <bonobo/bonobo-widget.h>

#include <bonobo-activation/bonobo-activation.h>

#include "cddb.h"
#include "cdrom.h"
#include "display.h"
#include "gnome-cd.h"

#include "GNOME_Media_CDDBSlave2.h"

extern void destroy_cache_hashTable (void);

static GNOME_Media_CDDBTrackEditor track_editor = CORBA_OBJECT_NIL;

static gboolean position_auto_update=TRUE;
static gboolean stopped_state=FALSE;
static gboolean position_update_ignore_event=FALSE;
static gboolean remaining_time_mode=FALSE;
static gchar* discid_prev;

static void
maybe_close_tray (GnomeCD *gcd)
{
	GnomeCDRomStatus *status;
	GError *error = NULL;

	if (gnome_cdrom_get_status (gcd->cdrom, &status, &error) == FALSE) {
		gcd_warning ("%s", error);
		g_error_free (error);
		g_free (status);
		return;
	}

	if (status->cd == GNOME_CDROM_STATUS_TRAY_OPEN) {
		if (gnome_cdrom_close_tray (gcd->cdrom, &error) == FALSE) {
			gcd_warning ("%s", error);
			g_error_free (error);
		}
	}

	g_free (status);
	return;
}

void
eject_cb (GtkButton *button,
	  GnomeCD *gcd)
{
	GError *error = NULL;

	if (gnome_cdrom_eject (gcd->cdrom, &error) == FALSE) {
		gcd_error ("%s", error);
		g_error_free (error);
	}

	if (gcd->current_image == gcd->pause_image) {
		AtkObject *aob;

		aob = gtk_widget_get_accessible (GTK_WIDGET (gcd->play_b));
		atk_object_set_name (aob, _("Play"));

		gtk_container_remove (GTK_CONTAINER (gcd->play_b), gcd->current_image);
		gtk_container_add (GTK_CONTAINER (gcd->play_b), gcd->play_image);
		gcd->current_image = gcd->play_image;
	}
}

void
play_cb (GtkButton *button,
	 GnomeCD *gcd)
{
	GError *error = NULL;
	GnomeCDRomStatus *status;
	GnomeCDRomMSF msf;
	AtkObject *aob;
	int end_track;
	GnomeCDRomMSF *endmsf;

	if (gnome_cdrom_get_status (gcd->cdrom, &status, &error) == FALSE) {
		gcd_error ("%s", error);
		g_error_free (error);
		g_free (status);
		return;

	}

	switch (status->cd) {
	case GNOME_CDROM_STATUS_TRAY_OPEN:
		if (gnome_cdrom_close_tray (gcd->cdrom, &error) == FALSE) {
			gcd_error ("Cannot close tray: %s", error);
			g_error_free (error);

			g_free (status);
			return;
		}

		/* Tray is closed, now play */
		msf.minute = 0;
		msf.second = 0;
		msf.frame = 0;
		if (gcd->cdrom->playmode == GNOME_CDROM_WHOLE_CD) {
			end_track = -1;
			endmsf = NULL;
		} else {
			end_track = 2;
			endmsf = &msf;
		}
		
		if (gnome_cdrom_play (gcd->cdrom, 1, &msf, end_track, endmsf, &error) == FALSE) {
			g_warning ("%s: %s", G_GNUC_FUNCTION, error->message);
			g_error_free (error);

			g_free (status);
			return;
		}
		break;

	case GNOME_CDROM_STATUS_DRIVE_NOT_READY:
		gcd_error ("Drive not ready: %s", NULL);

		g_free (status);
		return;

	case GNOME_CDROM_STATUS_OK:
	default:
		break;
	}

	switch (status->audio) {
	case GNOME_CDROM_AUDIO_PLAY:
		if (gcd->current_image == gcd->play_image) {
			aob = gtk_widget_get_accessible (GTK_WIDGET (gcd->play_b));
			atk_object_set_name (aob, _("Pause"));
			gtk_container_remove (GTK_CONTAINER (gcd->play_b), gcd->current_image);
			gtk_container_add (GTK_CONTAINER (gcd->play_b), gcd->pause_image);
			gcd->current_image = gcd->pause_image;
		} else { 
			if (gnome_cdrom_pause (gcd->cdrom, &error) == FALSE) {
				gcd_error ("%s", error);
				g_error_free (error);

				g_free (status);
				return;
			}

			if (gcd->current_image == gcd->pause_image) {
				aob = gtk_widget_get_accessible (GTK_WIDGET (gcd->play_b));
				atk_object_set_name (aob, _("Play"));
				gtk_container_remove (GTK_CONTAINER (gcd->play_b), gcd->current_image);
				gtk_container_add (GTK_CONTAINER (gcd->play_b),
						   gcd->play_image);
				gcd->current_image = gcd->play_image;
			}
		}
		break;

	case GNOME_CDROM_AUDIO_PAUSE:
		if (gcd->cdrom->playmode == GNOME_CDROM_WHOLE_CD) {
			end_track = -1;
			endmsf = NULL;
		} else {
			if (gcd->disc_info && status->track >= gcd->disc_info->ntracks) {
				end_track = -1;
				endmsf = NULL;	
			} else {	
				end_track = status->track + 1;
				msf.minute = 0;
				msf.second = 0;
				msf.frame = 0;
				endmsf = &msf;
			}
		}
		if (gnome_cdrom_play (gcd->cdrom, status->track,
				      &status->relative, end_track, endmsf, &error) == FALSE) {
			gcd_error ("%s", error);
			g_error_free (error);
			
			g_free (status);
			return;
		}

		if (gcd->current_image == gcd->play_image) {
			aob = gtk_widget_get_accessible (GTK_WIDGET (gcd->play_b));
			atk_object_set_name (aob, _("Pause"));
			gtk_container_remove (GTK_CONTAINER (gcd->play_b), gcd->current_image);
			gtk_container_add (GTK_CONTAINER (gcd->play_b), gcd->pause_image);
			gcd->current_image = gcd->pause_image;
		}
		break;

	case GNOME_CDROM_AUDIO_COMPLETE:
		stopped_state = FALSE;
	case GNOME_CDROM_AUDIO_STOP:
		msf.minute = 0;
		msf.second = 0;
		msf.frame = 0;

		if (gcd->cdrom->playmode == GNOME_CDROM_WHOLE_CD) {
			end_track = -1;
			endmsf = NULL;
		} else {
			end_track = 2;
			endmsf = &msf;
		}
		if (gnome_cdrom_play (gcd->cdrom, 1, &msf, end_track, endmsf, &error) == FALSE) {
			gcd_error ("%s", error);
			g_error_free (error);

			g_free (status);
			return;
		}

		if (gcd->current_image == gcd->play_image) {
			aob = gtk_widget_get_accessible (GTK_WIDGET (gcd->play_b));
			atk_object_set_name (aob, _("Pause"));
			gtk_container_remove (GTK_CONTAINER (gcd->play_b), gcd->current_image);
			gtk_container_add (GTK_CONTAINER (gcd->play_b), gcd->pause_image);
			gcd->current_image = gcd->pause_image;
		}
		break;

	case GNOME_CDROM_AUDIO_ERROR:
		gcd_error ("Error playing CD: %s", NULL);
		g_free (status);
		return;

	case GNOME_CDROM_AUDIO_NOTHING:
	default:
		break;
	}

	g_free (status);
}

void
stop_cb (GtkButton *button,
	 GnomeCD *gcd)
{
	GError *error = NULL;

	/* Close the tray if needed */
	maybe_close_tray (gcd);

	if (gnome_cdrom_stop (gcd->cdrom, &error) == FALSE) {
		gcd_error ("%s", error);
		g_error_free (error);
		return;
	}

	stopped_state = TRUE;

	if (gcd->current_image == gcd->pause_image) {
		AtkObject *aob;

		aob = gtk_widget_get_accessible (GTK_WIDGET (gcd->play_b));
		atk_object_set_name (aob, _("Play"));

		gtk_container_remove (GTK_CONTAINER (gcd->play_b), gcd->current_image);
		gtk_container_add (GTK_CONTAINER (gcd->play_b), gcd->play_image);
		gcd->current_image = gcd->play_image;
	}
}

static gboolean
ffwd_timeout_cb (gpointer data)
{
	GError *error = NULL;
	GnomeCD *gcd = data;

	if (gnome_cdrom_fast_forward (gcd->cdrom, &error) == FALSE) {
		gcd_warning ("%s", error);
		g_error_free (error);
		return FALSE;
	}

	return TRUE;
}

int
ffwd_press_cb (GtkButton *button,
	       GdkEvent *ev,
	       GnomeCD *gcd)
{
	if (gcd->timeout > 0) {
		return FALSE;
	}
	
	maybe_close_tray (gcd);

	ffwd_timeout_cb (gcd);
	
	gcd->timeout = gtk_timeout_add (140, ffwd_timeout_cb, gcd);

	if (gcd->current_image == gcd->play_image) {
		AtkObject *aob;

		aob = gtk_widget_get_accessible (GTK_WIDGET (gcd->play_b));
		atk_object_set_name (aob, _("Pause"));

		gtk_container_remove (GTK_CONTAINER (gcd->play_b), gcd->current_image);
		gtk_container_add (GTK_CONTAINER (gcd->play_b), gcd->pause_image);
		gcd->current_image = gcd->pause_image;
	}

	return FALSE;
}

int
ffwd_release_cb (GtkButton *button,
		 GdkEvent *ev,
		 GnomeCD *gcd)
{
	if (gcd->timeout > 0) {
		gtk_timeout_remove (gcd->timeout);
		gcd->timeout = 0;
	}

	return FALSE;
}

void
next_cb (GtkButton *button,
	 GnomeCD *gcd)
{
	GError *error = NULL;

	maybe_close_tray (gcd);

	if (gnome_cdrom_next (gcd->cdrom, &error) == FALSE) {
		gcd_error ("%s", error);
		g_error_free (error);
	}

	if (gcd->current_image == gcd->play_image) {
		AtkObject *aob;

		aob = gtk_widget_get_accessible (GTK_WIDGET (gcd->play_b));
		atk_object_set_name (aob, _("Pause"));
		gtk_container_remove (GTK_CONTAINER (gcd->play_b), gcd->current_image);
		gtk_container_add (GTK_CONTAINER (gcd->play_b), gcd->pause_image);
		gcd->current_image = gcd->pause_image;
	}
}

void
back_cb (GtkButton *button,
	 GnomeCD *gcd)
{
	GError *error = NULL;

	maybe_close_tray (gcd);

	if (gnome_cdrom_back (gcd->cdrom, &error) == FALSE) {
		gcd_error ("%s", error);
		g_error_free (error);
	}

	if (gcd->current_image == gcd->play_image) {
		AtkObject *aob;

		aob = gtk_widget_get_accessible (GTK_WIDGET (gcd->play_b));
		atk_object_set_name (aob, _("Pause"));
		gtk_container_remove (GTK_CONTAINER (gcd->play_b), gcd->current_image);
		gtk_container_add (GTK_CONTAINER (gcd->play_b), gcd->pause_image);
		gcd->current_image = gcd->pause_image;
	}
}

static gboolean
rewind_timeout_cb (gpointer data)
{
	GError *error = NULL;
	GnomeCD *gcd = data;
	
	if (gnome_cdrom_rewind (gcd->cdrom, &error) == FALSE) {
		gcd_warning ("%s", error);
		g_error_free (error);
		return FALSE;
	}
	
	return TRUE;
}

int
rewind_press_cb (GtkButton *button,
		 GdkEvent *ev,
		 GnomeCD *gcd)
{
	if (gcd->timeout > 0) {
		return FALSE;
	}
	
	maybe_close_tray (gcd);

	/* Call it so a click will activate it */
	rewind_timeout_cb (gcd);
	gcd->timeout = gtk_timeout_add (140, rewind_timeout_cb, gcd);

	if (gcd->current_image == gcd->play_image) {
		AtkObject *aob;

		aob = gtk_widget_get_accessible (GTK_WIDGET (gcd->play_b));
		atk_object_set_name (aob, _("Pause"));
		gtk_container_remove (GTK_CONTAINER (gcd->play_b), gcd->current_image);
		gtk_container_add (GTK_CONTAINER (gcd->play_b), gcd->pause_image);
		gcd->current_image = gcd->pause_image;
	}

	return FALSE;
}

int
rewind_release_cb (GtkButton *button,
		   GdkEvent *ev,
		   GnomeCD *gcd)
{
	if (gcd->timeout > 0) {
		gtk_timeout_remove (gcd->timeout);
		gcd->timeout = 0;
	}
	
	return FALSE;
}

static void
set_track_option_menu (GtkOptionMenu *menu,
		       int track)
{
	g_signal_handlers_block_matched (G_OBJECT (menu),
					 G_SIGNAL_MATCH_FUNC,
					 0, 0, NULL,
					 G_CALLBACK (skip_to_track), NULL);
	gtk_option_menu_set_history (menu, track - 1);
	g_signal_handlers_unblock_matched (G_OBJECT (menu),
					   G_SIGNAL_MATCH_FUNC,
					   0, 0, NULL,
					   G_CALLBACK (skip_to_track), NULL);
}

/* set the window title
 *   with the current artist (through gcd)
 *   and the track title (through status' track index)
 * FIXME: only called during PAUSE/PLAY state changes, while this
 *        would make sense to be used during track changes too.
 *        What is being done instead there ?
 */
static void
set_window_track_title (GnomeCD *gcd,
			GnomeCDRomStatus *status)
{
	int idx = status->track - 1;
	const char *artist = NULL;
	const char *track_name = NULL;

	if (gcd->disc_info) {
		if (idx >= 0 && idx < gcd->disc_info->ntracks &&
		    gcd->disc_info->track_info)
			track_name = gcd->disc_info->track_info [idx]->name;

		artist = gcd->disc_info->artist;
	}

	gcd_debug ("track_title: setting artist %s, track_name %s", artist, track_name);
	gnome_cd_set_window_title (gcd, artist, track_name);
}

/* Do all the stuff for when the status is ok */
static void
status_ok (GnomeCD *gcd,
	   GnomeCDRomStatus *status)
{
	AtkObject *aob;
	int track;
	char *text;
	char *current_time;

	gboolean remaining_time_temp = gconf_client_get_bool (gcd->client, "/apps/gnome-cd/show-remaining-time",NULL);
	
	/* Allow the track editor to work */
	gtk_widget_set_sensitive (gcd->trackeditor_b, TRUE);

	/* Set the track menu on. */
	gtk_widget_set_sensitive (gcd->tracks, TRUE);
	
	/* All buttons can be used when the state is ok */
	gtk_widget_set_sensitive (gcd->rewind_b, TRUE);
	gtk_widget_set_sensitive (gcd->play_b, TRUE);
	gtk_widget_set_sensitive (gcd->stop_b, TRUE);
	gtk_widget_set_sensitive (gcd->ffwd_b, TRUE);
	gtk_widget_set_sensitive (gcd->eject_b, TRUE);

	switch (status->audio) {
	case GNOME_CDROM_AUDIO_NOTHING:
		break;

	case GNOME_CDROM_AUDIO_PLAY:
		/* Change the play button to pause */
		if (gcd->current_image != gcd->pause_image) {
			aob = gtk_widget_get_accessible (GTK_WIDGET (gcd->play_b));
			atk_object_set_name (aob, _("Pause"));
			gtk_container_remove (GTK_CONTAINER (gcd->play_b),
					      gcd->play_image);
			gtk_container_add (GTK_CONTAINER (gcd->play_b),
					   gcd->pause_image);
			gcd->current_image = gcd->pause_image;
		}
		/* Find out if the track has changed */
                /* FIXME: this would be a good place to call
                   set_window_track_title instead.
                   Also, gtk_option_menu_get_history is now deprecated. */
		track = gtk_option_menu_get_history (GTK_OPTION_MENU (gcd->tracks)) + 1;
		if (track != status->track) {
			set_track_option_menu (GTK_OPTION_MENU (gcd->tracks),
					       status->track);
		}
		
/*  		cd_display_clear (CD_DISPLAY (gcd->display)); */
		if (remaining_time_temp) {
			gint min = status->length.minute - status->relative.minute;
			gint sec = status->length.second - status->relative.second;
			if(sec < 0) {
				min--;
				sec = 60 - (-1 * sec);
			}
			text = g_strdup_printf ("- %d:%02d", min, sec);
			current_time = g_strdup_printf ("Remaining time: %s", text);
        } else {
           	text = g_strdup_printf ("%d:%02d / %d:%02d",
					status->relative.minute, status->relative.second,
					status->length.minute, status->length.second);
	 	      		current_time = g_strdup_printf ("Current time: %s", text);
        }

		/* Displaying tooltip */
		gtk_tooltips_set_tip (gcd->tooltips, GTK_WIDGET(gcd->display), current_time, NULL);
		
		/* update position slider */
		if(position_auto_update && (status->length.minute!=0 ||
					    status->length.second!=0)){
			gboolean update_ignore_save = position_update_ignore_event;
			
			gint pos = status->relative.minute*60+status->relative.second;
			gint length = status->length.minute*60+status->length.second;
			
			if(pos>length) length = pos;
			if(pos<0) pos = 0;
			
			position_update_ignore_event = TRUE;
			gtk_adjustment_set_value (GTK_ADJUSTMENT(gcd->position_adj),
						  100.0*(double)pos/(double)length);
			position_update_ignore_event = update_ignore_save;
		}
		
		cd_display_set_line (CD_DISPLAY (gcd->display),
				     CD_DISPLAY_LINE_TIME, text);
		g_free (text);
		set_window_track_title (gcd, status);
			
		/* Update the tray icon tooltip */
		if (gcd->disc_info != NULL) {
			text = g_strdup_printf (_("Playing %s - %s\n%s"),
						gcd->disc_info->artist ? gcd->disc_info->artist : _("Unknown Artist"),
						gcd->disc_info->title ? gcd->disc_info->title : _("Unknown Album"),
						current_time);
		} else {
			text = g_strdup_printf (_("Playing\n: %s"), current_time);
		}
		if (gcd->tray)	
			gtk_status_icon_set_tooltip (gcd->tray, text);
		g_free (text);
		
		break;

	case GNOME_CDROM_AUDIO_PAUSE:
		/* Change the play button to pause */
		if (gcd->current_image != gcd->play_image) {
			aob = gtk_widget_get_accessible (GTK_WIDGET (gcd->play_b));
			atk_object_set_name (aob, _("Play"));
			gtk_container_remove (GTK_CONTAINER (gcd->play_b),
					      gcd->pause_image);
			gtk_container_add (GTK_CONTAINER (gcd->play_b),
					   gcd->play_image);
			gcd->current_image = gcd->play_image;
		}

		set_window_track_title (gcd, status);

		/* Update the tray icon tooltip */
		if (gcd->tray)	
			gtk_status_icon_set_tooltip (gcd->tray, _("Paused"));
		break;
		
	case GNOME_CDROM_AUDIO_COMPLETE:
		if (gcd->cdrom->loopmode == GNOME_CDROM_LOOP && stopped_state == FALSE) {
			if (gcd->cdrom->playmode == GNOME_CDROM_WHOLE_CD) {
				/* Around we go */
				play_cb (NULL, gcd);
			} else {
				GnomeCDRomMSF msf, *endmsf;
				int start_track, end_track;
				GError *error = NULL;
				
				/* CD is still in the same track */
				msf.minute = 0;
				msf.second = 0;
				msf.frame = 0;
				
				start_track = status->track;
				if (gcd->disc_info && status->track >= gcd->disc_info->ntracks) {
					end_track = -1;
					endmsf = NULL;
				} else {
					end_track = status->track + 1;
					endmsf = &msf;
				}
				
				if (gnome_cdrom_play (gcd->cdrom, start_track, &msf,
						      end_track, endmsf, &error) == FALSE) {
					gcd_warning ("%s", error);
					g_error_free (error);
					
					g_free (status);
					return;
				}
			}
		} else {
			/* We've stopped playing anything
			   reset play button, and track option menu */
			if (gcd->current_image != gcd->play_image) {
				gtk_container_remove (GTK_CONTAINER (gcd->play_b),
						      gcd->pause_image);
				gtk_container_add (GTK_CONTAINER (gcd->play_b),
						   gcd->play_image);
				gcd->current_image = gcd->play_image;
			}

			set_track_option_menu (GTK_OPTION_MENU (gcd->tracks), 1);

			/* Update tray icon tooltip */
			if (gcd->tray)	
				gtk_status_icon_set_tooltip (gcd->tray, _("CD Player"));
			if (gcd->tooltips)
				gtk_tooltips_set_tip (gcd->tooltips, GTK_WIDGET(gcd->display), _("CD Player"), NULL);

			/* Initialise the display time to 0:00 in stopped state*/
			cd_display_set_line (CD_DISPLAY (gcd->display),
					     CD_DISPLAY_LINE_TIME, "0:00");
			/* Update the position of the slider */
			gtk_adjustment_set_value (GTK_ADJUSTMENT(gcd->position_adj), 0);
		}		
		break;
		
	case GNOME_CDROM_AUDIO_STOP:
		cd_display_set_line (CD_DISPLAY (gcd->display),
				     CD_DISPLAY_LINE_TIME, "");
		if (gcd->disc_info != NULL) {
                        gcd_debug ("AUDIO_STOP: artist %s, title %s", gcd->disc_info->artist, gcd->disc_info->title);
			gnome_cd_set_window_title (gcd,
						   gcd->disc_info->artist,
						   gcd->disc_info->title);
		} else {
			gnome_cd_set_window_title (gcd, NULL, NULL);
		}

		/* Update the tray icon tooltip */
		if (gcd->tray)	
			gtk_status_icon_set_tooltip (gcd->tray, _("Stopped"));
		break;
		
	case GNOME_CDROM_AUDIO_ERROR:
		cd_display_clear (CD_DISPLAY (gcd->display));
		cd_display_set_line (CD_DISPLAY (gcd->display),
				     CD_DISPLAY_LINE_TIME, _("Disc error"));
		if (gcd->disc_info != NULL) {
                        gcd_debug ("AUDIO_ERROR: artist %s, title %s", gcd->disc_info->artist, gcd->disc_info->title);
			gnome_cd_set_window_title (gcd, gcd->disc_info->artist,
						   gcd->disc_info->title);
		} else {
			gnome_cd_set_window_title (gcd, NULL, NULL);
		}

		/* Update the tray icon tooltip */
		if (gcd->tray)	
			gtk_status_icon_set_tooltip (gcd->tray, _("No disc"));
		break;
		
	default:
		if (gcd->disc_info != NULL) {
                        gcd_debug ("default: artist %s, title %s", gcd->disc_info->artist, gcd->disc_info->title);
			gnome_cd_set_window_title (gcd,
						   gcd->disc_info->artist,
						   gcd->disc_info->title);
		} else {
			gnome_cd_set_window_title (gcd, NULL, NULL);
		}
		
		break;
	}
	
	if (gcd->last_status == NULL ||
	    gcd->last_status->cd != GNOME_CDROM_STATUS_OK) {
		cddb_get_query (gcd);
	}
		
	if (gcd->disc_info == NULL) {
		cd_display_set_line (CD_DISPLAY (gcd->display),
				     CD_DISPLAY_LINE_ARTIST,
				     _("Unknown Artist"));
		cd_display_set_line (CD_DISPLAY (gcd->display),
				     CD_DISPLAY_LINE_ALBUM,
				     _("Unknown Album"));
		gnome_cd_set_window_title (gcd, NULL, NULL);
	} else {
		GnomeCDDiscInfo *info = gcd->disc_info;
		
		cd_display_set_line (CD_DISPLAY (gcd->display),
				     CD_DISPLAY_LINE_ARTIST,
				     info->artist ? info->artist :
				     _("Unknown Artist"));
		cd_display_set_line (CD_DISPLAY (gcd->display),
				     CD_DISPLAY_LINE_ALBUM,
				     info->title ? info->title :
				     _("Unknown Album"));
	}
}

/* GnomeCDRom status-changed signal handler */
void
cd_status_changed_cb (GnomeCDRom *cdrom,
		      GnomeCDRomStatus *status,
		      GnomeCD *gcd)
{
	if (gcd->not_ready == TRUE) {
		return;
	}

	switch (status->cd) {
	case GNOME_CDROM_STATUS_OK:
                /* does everything when the state is ok */
		status_ok (gcd, status);
                /* next is sensitive when this is not the last track */
		/* but gcd->disc_info might be NULL when
                 * this gets called on startup but we haven't looked at the
                 * disc yet */
		if (gcd->disc_info && status->track >= gcd->disc_info->ntracks)
                       gtk_widget_set_sensitive (gcd->next_b, FALSE);
		else
                       gtk_widget_set_sensitive (gcd->next_b, TRUE);
                /* back is sensitive when it's not the first track and
                   we're playing */
               if (status->track <= 1 && (status->audio == GNOME_CDROM_AUDIO_STOP || status->audio == GNOME_CDROM_AUDIO_COMPLETE))
                       gtk_widget_set_sensitive (gcd->back_b, FALSE);
		else
                       gtk_widget_set_sensitive (gcd->back_b, TRUE);
		break;

        /* everything below are states where we're not playing */
	case GNOME_CDROM_STATUS_NO_DISC:
	case GNOME_CDROM_STATUS_EMPTY_DISC:
		if (gcd->disc_info != NULL) {
			cddb_free_disc_info (gcd->disc_info);
			gcd->disc_info = NULL;
			/* we need to destroy the hashtable(cddb_cache) */
			destroy_cache_hashTable();
		}
		
		gtk_widget_set_sensitive (gcd->trackeditor_b, FALSE);

		/* Set the other widgets off */
		gtk_widget_set_sensitive (gcd->tracks, FALSE);
		gtk_widget_set_sensitive (gcd->back_b, FALSE);
		gtk_widget_set_sensitive (gcd->rewind_b, FALSE);
		gtk_widget_set_sensitive (gcd->play_b, FALSE);
		gtk_widget_set_sensitive (gcd->stop_b, FALSE);
		gtk_widget_set_sensitive (gcd->ffwd_b, FALSE);
		gtk_widget_set_sensitive (gcd->next_b, FALSE);

		/* We can still use the eject button when there's no disc. */
		gtk_widget_set_sensitive (gcd->eject_b, TRUE);

		cd_display_clear (CD_DISPLAY (gcd->display));
		if (status->cd == GNOME_CDROM_STATUS_EMPTY_DISC)
			cd_display_set_line (CD_DISPLAY (gcd->display), CD_DISPLAY_LINE_TIME, _("Empty disc"));
		else
			cd_display_set_line (CD_DISPLAY (gcd->display), CD_DISPLAY_LINE_TIME, _("No disc")); 
		gnome_cd_set_window_title (gcd, NULL, NULL);
		break;

	case GNOME_CDROM_STATUS_TRAY_OPEN:
		if (gcd->disc_info != NULL) {
			cddb_free_disc_info (gcd->disc_info);
			gcd->disc_info = NULL;
			/* we need to destroy the hashtable(cddb_cache) */
			destroy_cache_hashTable();
		}
		
		gtk_widget_set_sensitive (gcd->trackeditor_b, FALSE);

		/* Set the track menu off. */
		gtk_widget_set_sensitive (gcd->tracks, FALSE);

		/* All buttons can be used when the tray is open,
		   they should shut the tray. */
		gtk_widget_set_sensitive (gcd->back_b, TRUE);
		gtk_widget_set_sensitive (gcd->rewind_b, TRUE);
		gtk_widget_set_sensitive (gcd->play_b, TRUE);
		gtk_widget_set_sensitive (gcd->stop_b, TRUE);
		gtk_widget_set_sensitive (gcd->ffwd_b, TRUE);
		gtk_widget_set_sensitive (gcd->next_b, TRUE);
		gtk_widget_set_sensitive (gcd->eject_b, TRUE);

		cd_display_clear (CD_DISPLAY (gcd->display));
		cd_display_set_line (CD_DISPLAY (gcd->display), CD_DISPLAY_LINE_TIME, _("Drive open"));
		gnome_cd_set_window_title (gcd, NULL, NULL);
		break;

	case GNOME_CDROM_STATUS_DATA_CD:
		if (gcd->disc_info != NULL) {
				cddb_free_disc_info (gcd->disc_info);
				gcd->disc_info = NULL;
				/* we need to destroy the hashtable(cddb_cache) */
				destroy_cache_hashTable();
		}	
		gtk_widget_set_sensitive (gcd->trackeditor_b, FALSE);

                /* Set the other widgets off */
                gtk_widget_set_sensitive (gcd->tracks, FALSE);
                gtk_widget_set_sensitive (gcd->back_b, FALSE);
                gtk_widget_set_sensitive (gcd->rewind_b, FALSE);
                gtk_widget_set_sensitive (gcd->play_b, FALSE);
                gtk_widget_set_sensitive (gcd->stop_b, FALSE);
                gtk_widget_set_sensitive (gcd->ffwd_b, FALSE);
                gtk_widget_set_sensitive (gcd->next_b, FALSE);

		/* Eject button can be used even to eject a data cd */
		gtk_widget_set_sensitive (gcd->eject_b, TRUE);
	
		cd_display_clear (CD_DISPLAY (gcd->display));
                cd_display_set_line (CD_DISPLAY (gcd->display), CD_DISPLAY_LINE_TIME, _("Data CD"));
                gnome_cd_set_window_title (gcd, NULL, NULL);
                break;

	case GNOME_CDROM_STATUS_NO_CDROM:
		if (gcd->disc_info != NULL) {
				cddb_free_disc_info (gcd->disc_info);
				gcd->disc_info = NULL;
				/* we need to destroy the hashtable(cddb_cache) */
				destroy_cache_hashTable();
		}	
		gtk_widget_set_sensitive (gcd->trackeditor_b, FALSE);

                /* Set the other widgets off */
                gtk_widget_set_sensitive (gcd->tracks, FALSE);
                gtk_widget_set_sensitive (gcd->back_b, FALSE);
                gtk_widget_set_sensitive (gcd->rewind_b, FALSE);
                gtk_widget_set_sensitive (gcd->play_b, FALSE);
                gtk_widget_set_sensitive (gcd->stop_b, FALSE);
                gtk_widget_set_sensitive (gcd->ffwd_b, FALSE);
                gtk_widget_set_sensitive (gcd->next_b, FALSE);
		gtk_widget_set_sensitive (gcd->eject_b, TRUE);
	
		cd_display_clear (CD_DISPLAY (gcd->display));
                cd_display_set_line (CD_DISPLAY (gcd->display), CD_DISPLAY_LINE_TIME, _("No Cdrom"));
                gnome_cd_set_window_title (gcd, NULL, NULL);

		/* Updated the tray icon tooltip */
		if (gcd->tray)	
			gtk_status_icon_set_tooltip (gcd->tray, _("No Cdrom"));
                break;

	default:
		if (gcd->disc_info != NULL) {
			cddb_free_disc_info (gcd->disc_info);
			gcd->disc_info = NULL;
			/* we need to destroy the hashtable(cddb_cache) */
			destroy_cache_hashTable();
		}

		gtk_widget_set_sensitive (gcd->trackeditor_b, FALSE);
		cd_display_clear (CD_DISPLAY (gcd->display));
		cd_display_set_line (CD_DISPLAY (gcd->display), CD_DISPLAY_LINE_TIME, _("Drive Error"));
		gnome_cd_set_window_title (gcd, NULL, NULL);
		break;
	}

	if (gcd->last_status != NULL) {
		g_free (gcd->last_status);
	}

	gcd->last_status = gnome_cdrom_copy_status (status);
}

void
about_cb (GtkWidget *widget,
	  gpointer data)
{
	const char *authors[2] = {"Iain Holmes <iain@prettypeople.org>", NULL};

  	gtk_show_about_dialog (NULL,
			       "name", _("CD Player"),
			       "version", VERSION,
			       "copyright", "Copyright \xc2\xa9 2001-2002 Iain Holmes",
			       "comments", _("A CD player for GNOME"),
			       "authors", authors,
			       "logo-icon-name", "gnome-cd",
		               NULL);

}

void
help_cb (GtkWidget *widget,
	 gpointer data)
{
	GError *error = NULL;

	gnome_help_display ("gnome-cd", NULL, &error); 
	if (error) {
		GtkWidget *msgbox;
		msgbox = gtk_message_dialog_new (NULL,
						 GTK_DIALOG_MODAL,
						 GTK_MESSAGE_ERROR,
						 GTK_BUTTONS_CLOSE,
						 ("There was an error displaying help: \n%s"),
						 error->message);
		g_signal_connect (G_OBJECT(msgbox), "response",
				  G_CALLBACK (gtk_widget_destroy),
				  NULL);
		gtk_widget_show (msgbox);
		g_error_free (error);
	} 
}
 
void
loopmode_changed_cb (GtkWidget *display,
		     GnomeCDRomMode mode,
		     GnomeCD *gcd)
{
	gcd->cdrom->loopmode = mode;
}

void
remainingtime_mode_changed_cb (GtkWidget *display,
                    GnomeCDRomMode mode,
                    GnomeCD *gcd)
{
	remaining_time_mode = !remaining_time_mode;
	gconf_client_set_bool (gcd->client, "/apps/gnome-cd/show-remaining-time", remaining_time_mode, NULL);
}

void
playmode_changed_cb (GtkWidget *display,
		     GnomeCDRomMode mode,
		     GnomeCD *gcd)
{
	gcd->cdrom->playmode = mode;
}

/* Popup Menu callback */
gboolean
button_press_event_cb (GtkWidget *widget,
                GdkEventButton *event,
                GnomeCD *gcd)
{
    if ((event->button == 3) && !(event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK))) {
        make_popup_menu (gcd, event, FALSE);
        return TRUE;
    }
    return FALSE;
}

/* Popup Menu callback */
gboolean
popup_menu_cb (GtkWidget *widget, GnomeCD *gcd)
{
    make_popup_menu (gcd, NULL, FALSE);
    return TRUE;
}

void
tray_popup_menu_cb (GtkStatusIcon *icon,
		    guint          button,
                    guint          activate_time,
		    GnomeCD       *gcd)
{
    make_popup_menu (gcd, NULL, FALSE);
}

void
tray_icon_activated (GtkStatusIcon *icon, GnomeCD *gcd)
{
	if (GTK_WIDGET_VISIBLE (gcd->window)) {
		gtk_widget_hide (gcd->window);
	} else {
		gtk_widget_show (gcd->window);
	}
}

gboolean
tray_icon_destroyed (GtkWidget *widget, GnomeCD *gcd)
{
	if (!GTK_WIDGET_VISIBLE (gcd->window)) {
		gtk_widget_show (gcd->window);
	}
	g_object_unref (gcd->tray);
	gcd->tray = NULL;

	tray_icon_create (gcd);

	return TRUE;
}

void
open_preferences (GtkWidget *widget,
		  GnomeCD *gcd)
{
	static GtkWidget *dialog = NULL;

	if (dialog == NULL) {
		dialog = preferences_dialog_show (gcd);
		g_signal_connect (G_OBJECT (dialog), "destroy",
				  G_CALLBACK (gtk_widget_destroyed), &dialog);
	} else {
		gdk_window_show (dialog->window);
		gdk_window_raise (dialog->window);
	}
}

#define CDDBSLAVE_TRACK_EDITOR_IID "OAFIID:GNOME_Media_CDDBSlave2_TrackEditor"

static void
restart_track_editor (gpointer data)
{
	CORBA_Environment ev;
	
	CORBA_exception_init (&ev);
	track_editor = bonobo_activation_activate_from_id (CDDBSLAVE_TRACK_EDITOR_IID, 0, NULL, &ev);
	if (BONOBO_EX (&ev)) {
		g_warning ("Could not reactivate track editor.\n%s", CORBA_exception_id (&ev));
		track_editor = CORBA_OBJECT_NIL;
		CORBA_exception_free (&ev);

		return;
	}

	ORBit_small_listen_for_broken (track_editor, G_CALLBACK (restart_track_editor), &ev);
	CORBA_exception_free (&ev);
}

void
destroy_track_editor (void)
{
	if (track_editor != CORBA_OBJECT_NIL) {
		ORBit_small_unlisten_for_broken (track_editor, G_CALLBACK (restart_track_editor));
		bonobo_object_release_unref (track_editor, NULL);
	}
}

void
open_track_editor (GtkWidget *widget,
		   GnomeCD *gcd)
{
	GError *error = NULL;
	GnomeCDRomCDDBData *data;
	CORBA_Environment ev;
	char *discid;
	gint result = GTK_RESPONSE_YES;

	if (gnome_cdrom_get_cddb_data (gcd->cdrom, &data, &error) == FALSE) {
		gcd_warning ("gnome_cdrom_get_cddb_data returned FALSE: %s", error);
		g_error_free (error);
		return;
	}
	
	discid = g_strdup_printf ("%08lx", (gulong) data->discid);

	if (discid_prev != NULL) {
		gboolean same_cd;
		same_cd = g_str_equal(discid,discid_prev);
		if (!same_cd) {
			GtkWidget *dialog;
			gchar *text = g_strdup_printf ("<b>%s</b>\n\n%s\n%s",
            	   				    _("Contents in the CDDB have changed"),
		                                    _("Choosing 'Yes' will delete the previous contents in CDDB."),
                		                    _("Do you wish to continue?"));

			dialog = gtk_message_dialog_new (NULL, 0,
                		                         GTK_MESSAGE_WARNING,
                					 GTK_BUTTONS_YES_NO,
							 text);
			gtk_label_set_use_markup (GTK_LABEL (GTK_MESSAGE_DIALOG (dialog)->label), TRUE);
			result = gtk_dialog_run (GTK_DIALOG (dialog));
			gtk_widget_destroy(dialog);
   		}
	}
	
	CORBA_exception_init (&ev);
	if (track_editor == CORBA_OBJECT_NIL) {
		track_editor = bonobo_activation_activate_from_id (CDDBSLAVE_TRACK_EDITOR_IID, 0, NULL, &ev);
		if (BONOBO_EX (&ev)) {
			/* FIXME: Should be an error dialog */
			g_warning ("Could not activate track editor.\n%s",
				   CORBA_exception_id (&ev));
			CORBA_exception_free (&ev);
			
			return;
		}
		
		if (track_editor == CORBA_OBJECT_NIL) {
			/* FIXME: Should be an error dialog */
			g_warning ("Could not start track editor.");
			
			return;
		}

		/* Listen for the trackeditor dying on us,
		   then restart it */
		ORBit_small_listen_for_broken (track_editor, G_CALLBACK (restart_track_editor), &ev);
	}

	if (data != NULL) {
		discid = g_strdup_printf ("%08lx", (gulong) data->discid);
		if (result == GTK_RESPONSE_NO) 
		GNOME_Media_CDDBTrackEditor_setDiscID (track_editor, discid_prev, &ev);
               if (result == GTK_RESPONSE_YES){
               GNOME_Media_CDDBTrackEditor_setDiscID (track_editor, discid, &ev);
		discid_prev = g_strdup (discid);}
		g_free (discid);
	}
	
	GNOME_Media_CDDBTrackEditor_showWindow (track_editor, &ev);
	if (BONOBO_EX (&ev)) {
		/* FIXME: Should be an error dialog */
		CORBA_exception_free (&ev);
		bonobo_object_release_unref (track_editor, NULL);
		return;
	}
	CORBA_exception_free (&ev);
}

void
volume_changed (GtkRange *range,
		GnomeCD *gcd)
{
	double volume;
	GError *error = NULL;
	gchar *volume_level;
	gint scaled_volume;

	volume = gtk_range_get_value (range);
	if (gnome_cdrom_set_volume (gcd->cdrom, (int) volume, &error) == FALSE) {
		gcd_warning ("Error setting volume: %s", error);
		g_error_free (error);
	}
	/* Set the Tooltip */
	scaled_volume = (volume/255)*100;
	volume_level = g_strdup_printf (_("Volume %d%%"), scaled_volume);
	gtk_tooltips_set_tip (gcd->tooltips, GTK_WIDGET (gcd->slider), volume_level, NULL);
	gconf_client_set_int (gcd->client, "/apps/gnome-cd/volume", scaled_volume, NULL);
	g_free (volume_level);
}

void
position_changed (GtkRange *range,
		  GnomeCD *gcd)
{
	double position;
	int end_track;
	GnomeCDRomStatus *status = NULL;
	GnomeCDRomMSF msf, msf2, *endmsf;
	GError *error = NULL;
	gint length, pos;
	GtkAdjustment *adj;
	
	if(position_update_ignore_event) return;
	
	position = gtk_range_get_value (range);
		
	if (gnome_cdrom_get_status (GNOME_CDROM (gcd->cdrom), &status, NULL) == FALSE) 
		goto out;
	
	/* slider should have no effect unless the CD is playing or 
	 * at lease pausing 
	 */
	if(status->cd!=GNOME_CDROM_STATUS_OK) goto out;
	if(status->audio!=GNOME_CDROM_AUDIO_PLAY &&
	   status->audio!=GNOME_CDROM_AUDIO_PAUSE) goto out;
	
	adj=gtk_range_get_adjustment (range);
	length = status->length.minute*60 + status->length.second;
	pos = length * position / (adj->upper - adj->lower);
	
	msf.minute = pos / 60;
	msf.second = pos % 60;
	msf.frame = 0;
	
	msf2.minute = 0;
	msf2.second = 0;
	msf2.frame = 0;
	
	if (gcd->cdrom->playmode == GNOME_CDROM_SINGLE_TRACK) {
		if (gcd->disc_info && status->track >= gcd->disc_info->ntracks) {
			end_track = -1;
			endmsf = NULL;
		} else {	
			end_track = status->track + 1;
			endmsf = &msf2;
		}
	} else {
		end_track = -1;
		endmsf = NULL;
	}
	
	if (gnome_cdrom_play (GNOME_CDROM (gcd->cdrom), status->track, 
			      &msf, end_track, endmsf, &error) == FALSE) {
		gcd_warning ("Error skipping %s", error);
		g_error_free (error);
	}
	if (status->audio==GNOME_CDROM_AUDIO_PAUSE){
		if (gnome_cdrom_pause (gcd->cdrom, &error) == FALSE) {
			gcd_warning ("%s", error);
			g_error_free (error);
		}
	}
	
out:
	if(status) g_free(status);
	return;
}

void 
position_slider_enter (GtkRange *range, 
		       GdkEventCrossing *event,
		       GnomeCD *gcd){
	/* stop updating our position slider so the user has full control */
	position_auto_update = FALSE; 
}

void 
position_slider_leave (GtkRange *range, 
		       GdkEventCrossing *event,
		       GnomeCD *gcd){
	/* user has moved the mouse away, we may now update the slider again */
	position_auto_update = TRUE;
}
