/*
 * * Copyright (C) 2009 Ali <aliov@xfce.org>
 *
 * Licensed under the GNU General Public License Version 2
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef HAVE_XF86_KEYSYM
#include <X11/XF86keysym.h>
#endif

#include <gdk/gdkx.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>

#include <libxfce4util/libxfce4util.h>
#include <libxfcegui4/libxfcegui4.h>

#include <dbus/dbus-glib.h>

#include <parole/parole-file.h>

#include "parole-builder.h"
#include "parole-about.h"

#include "parole-player.h"
#include "parole-gst.h"
#include "parole-dbus.h"
#include "parole-mediachooser.h"
#include "parole-filters.h"
#include "parole-disc.h"
#include "parole-disc-menu.h"
#include "parole-statusbar.h"
#include "parole-screensaver.h"
#include "parole-conf-dialog.h"
#include "parole-conf.h"
#include "parole-rc-utils.h"
#include "parole-utils.h"
#include "parole-session.h"
#include "parole-debug.h"
#include "parole-button.h"
#include "enum-gtypes.h"
#include "parole-debug.h"

#include "gst/gst-enum-types.h"

#include "common/parole-common.h"

/*
 * DBus Glib init
 */
static void parole_player_dbus_class_init  (ParolePlayerClass *klass);
static void parole_player_dbus_init        (ParolePlayer *player);

static void parole_player_disc_selected_cb (ParoleDisc *disc, 
					    const gchar *uri, 
					    const gchar *device, 
					    ParolePlayer *player);

/*
 * GtkBuilder Callbacks
 */
gboolean        parole_player_configure_event_cb        (GtkWidget *widget, 
							 GdkEventConfigure *ev, 
							 ParolePlayer *player);
							 
gboolean	parole_player_range_button_press 	(GtkWidget *widget, 
							 GdkEventButton *ev, 
							 ParolePlayer *player);

gboolean	parole_player_range_button_release	(GtkWidget *widget,
							 GdkEventButton *ev,
							 ParolePlayer *player);

void            parole_player_range_value_changed       (GtkRange *range, 
							 ParolePlayer *player);

void            parole_player_play_pause_clicked        (GtkButton *button, 
							 ParolePlayer *player);

void            parole_player_stop_clicked              (GtkButton *button, 
							 ParolePlayer *player);

void            parole_player_seekf_cb                  (GtkButton *button, 
							 ParolePlayer *player);
							 
void            parole_player_seekb_cb                  (GtkButton *button, 
							 ParolePlayer *player);

gboolean        parole_player_scroll_event_cb		(GtkWidget *widget,
							 GdkEventScroll *ev,
							 ParolePlayer *player);

void		parole_player_leave_fs_cb		(GtkButton *button,
							 ParolePlayer *player);

void            parole_player_destroy_cb                (GtkObject *window, 
							 ParolePlayer *player);

gboolean	parole_player_delete_event_cb		(GtkWidget *widget, 
							 GdkEvent *ev,
							 ParolePlayer *player);

void		parole_player_show_hide_playlist	(GtkButton *button,
							 ParolePlayer *player);

/*Menu items callbacks*/
void            parole_player_menu_open_location_cb     (GtkWidget *widget, 
							 ParolePlayer *player);

void            parole_player_menu_add_cb               (GtkWidget *widget, 
							 ParolePlayer *player);

void            parole_player_menu_exit_cb              (GtkWidget *widget,
							 ParolePlayer *player);

void		dvd_iso_mi_activated_cb			(GtkWidget *widget,
							 ParolePlayer *player);

void		cd_iso_mi_activated_cb			(GtkWidget *widget,
							 ParolePlayer *player);

void            parole_player_volume_up 		(GtkWidget *widget, 
							 ParolePlayer *player);

void            parole_player_volume_down 		(GtkWidget *widget, 
							 ParolePlayer *player);

void            parole_player_volume_mute 		(GtkWidget *widget, 
							 ParolePlayer *player);

void		parole_player_open_preferences_cb	(GtkWidget *widget,
							 ParolePlayer *player);

void            parole_player_volume_value_changed_cb   (GtkRange *range, 
							 ParolePlayer *player);

gboolean        parole_player_volume_scroll_event_cb	(GtkWidget *widget,
							 GdkEventScroll *ev,
							 ParolePlayer *player);

void		parole_player_full_screen_activated_cb  (GtkWidget *widget,
							 ParolePlayer *player);

void		parole_player_shuffle_toggled_cb	(GtkWidget *widget,
							 ParolePlayer *player);

void		parole_player_repeat_toggled_cb		(GtkWidget *widget,
							 ParolePlayer *player);

/*
 * Aspect ratio
 */
void		ratio_none_toggled_cb			(GtkWidget *widget,
							 ParolePlayer *player);

void		ratio_auto_toggled_cb			(GtkWidget *widget,
							 ParolePlayer *player);

void		ratio_square_toggled_cb			(GtkWidget *widget,
							 ParolePlayer *player);

void		ratio_4_3_toggled_cb			(GtkWidget *widget,
							 ParolePlayer *player);

void		ratio_16_9_toggled_cb			(GtkWidget *widget,
							 ParolePlayer *player);

void		ratio_20_9_toggled_cb			(GtkWidget *widget,
							 ParolePlayer *player);

void	        parole_show_about			(GtkWidget *widget);

gboolean	parole_player_key_press 		(GtkWidget *widget, 
							 GdkEventKey *ev, 
							 ParolePlayer *player);

static GtkTargetEntry target_entry[] =
{
    { "STRING",        0, 0 },
    { "text/uri-list", 0, 1 },
};

/*
 * End of GtkBuilder Callbacks
 */
#define PAROLE_PLAYER_GET_PRIVATE(o) \
(G_TYPE_INSTANCE_GET_PRIVATE ((o), PAROLE_TYPE_PLAYER, ParolePlayerPrivate))

struct ParolePlayerPrivate
{
    DBusGConnection     *bus;
    ParoleMediaList	*list;
    ParoleStatusbar     *status;
    ParoleDisc          *disc;
    ParoleScreenSaver   *screen_saver;
    ParoleConf          *conf;
    ParoleDiscMenu      *disc_menu;
    ParoleSession       *session;
#ifdef HAVE_XF86_KEYSYM
    ParoleButton        *button;
#endif
    
    GtkFileFilter       *video_filter;
    GtkRecentManager    *recent;

    GtkWidget 		*gst;

    GtkWidget 		*window;
    GtkWidget		*playlist_nt;
    GtkWidget		*main_nt;	/*Main notebook*/
    GtkWidget		*show_hide_playlist;
    GtkWidget		*play_pause;
    GtkWidget		*stop;
    GtkWidget		*seekf;
    GtkWidget		*seekb;
    GtkWidget		*range;
    
    GtkWidget		*fs_window; /* Window for packing control widgets 
				     * when in full screen mode
				     */
    GtkWidget		*control; /* contains all play button*/
    GtkWidget		*leave_fs;
    
    GtkWidget		*main_box;
    
    GtkWidget		*volume;
    GtkWidget		*volume_image;
    GtkWidget		*menu_bar;
    GtkWidget		*play_box;
     
    gboolean             exit;
    
    gboolean		 full_screen;
    
    ParoleMediaState     state;
    gboolean		 user_seeking;
    gboolean             internal_range_change;
    gboolean		 buffering;
    
    GtkTreeRowReference *row;
        
};

G_DEFINE_TYPE (ParolePlayer, parole_player, G_TYPE_OBJECT)

void parole_show_about	(GtkWidget *widget)
{
    parole_about ();
}

void ratio_none_toggled_cb (GtkWidget *widget, ParolePlayer *player)
{
    g_object_set (G_OBJECT (player->priv->conf),
		  "aspect-ratio", PAROLE_ASPECT_RATIO_NONE,
		  NULL);
}

void ratio_auto_toggled_cb (GtkWidget *widget, ParolePlayer *player)
{
    g_object_set (G_OBJECT (player->priv->conf),
		  "aspect-ratio", PAROLE_ASPECT_RATIO_AUTO,
		  NULL);
}

void ratio_square_toggled_cb (GtkWidget *widget, ParolePlayer *player)
{
     g_object_set (G_OBJECT (player->priv->conf),
		  "aspect-ratio", PAROLE_ASPECT_RATIO_SQUARE,
		  NULL);
}

void ratio_4_3_toggled_cb (GtkWidget *widget, ParolePlayer *player)
{
    g_object_set (G_OBJECT (player->priv->conf),
		  "aspect-ratio", PAROLE_ASPECT_RATIO_4_3,
		  NULL);
}

void ratio_16_9_toggled_cb (GtkWidget *widget, ParolePlayer *player)
{
    g_object_set (G_OBJECT (player->priv->conf),
		  "aspect-ratio", PAROLE_ASPECT_RATIO_16_9,
		  NULL);
}

void ratio_20_9_toggled_cb (GtkWidget *widget, ParolePlayer *player)
{
    g_object_set (G_OBJECT (player->priv->conf),
		  "aspect-ratio", PAROLE_ASPECT_RATIO_DVB,
		  NULL);
}

void parole_player_show_hide_playlist (GtkButton *button, ParolePlayer *player)
{
    GtkWidget *img;
    gboolean   visible;
    
    g_object_get (G_OBJECT (player->priv->show_hide_playlist),
		  "image", &img,
		  NULL);

    visible = GTK_WIDGET_VISIBLE (player->priv->playlist_nt);

    if ( !visible )
    {
	g_object_set (G_OBJECT (img),
		      "stock", GTK_STOCK_GO_FORWARD,
		      NULL);
		      
	gtk_widget_show_all (player->priv->playlist_nt);
	gtk_widget_set_tooltip_text (GTK_WIDGET (player->priv->show_hide_playlist), _("Hide playlist"));
    }
    else
    {
	g_object_set (G_OBJECT (img),
		      "stock", GTK_STOCK_GO_BACK,
		      NULL);
		      
	gtk_widget_hide_all (player->priv->playlist_nt);
	gtk_widget_set_tooltip_text (GTK_WIDGET (player->priv->show_hide_playlist), _("Show playlist"));
    }
    g_object_unref (img);
}

typedef enum
{
    PAROLE_ISO_IMAGE_DVD,
    PAROLE_ISO_IMAGE_CD
} ParoleIsoImage;

static void
iso_files_folder_changed_cb (GtkFileChooser *widget, gpointer data)
{
    gchar *folder;
    folder = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (widget));
    
    if ( folder )
    {
	parole_rc_write_entry_string ("iso-image-folder", PAROLE_RC_GROUP_GENERAL, folder);
	g_free (folder);
    }
}

static void
parole_player_open_iso_image (ParolePlayer *player, ParoleIsoImage image)
{
    GtkWidget *chooser;
    GtkFileFilter *filter;
    gchar *file = NULL;
    const gchar *folder;
    gint response;
    
    chooser = gtk_file_chooser_dialog_new (_("Open ISO image"), GTK_WINDOW (player->priv->window),
					   GTK_FILE_CHOOSER_ACTION_OPEN,
					   GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
					   GTK_STOCK_OPEN, GTK_RESPONSE_OK,
					   NULL);
				
    gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (chooser), FALSE);
    
    folder = parole_rc_read_entry_string ("iso-image-folder", PAROLE_RC_GROUP_GENERAL, NULL);
    
    if ( folder )
	gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (chooser), folder);
    
    g_signal_connect (chooser, "current-folder-changed",
		      G_CALLBACK (iso_files_folder_changed_cb), NULL);
    
    filter = gtk_file_filter_new ();
    gtk_file_filter_set_name (filter, image == PAROLE_ISO_IMAGE_CD ? _("CD image") : _("DVD image"));
    gtk_file_filter_add_mime_type (filter, "application/x-cd-image");
    gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (chooser), filter);

    gtk_window_set_default_size (GTK_WINDOW (chooser), 680, 480);
    response = gtk_dialog_run (GTK_DIALOG (chooser));
    
    if ( response == GTK_RESPONSE_OK )
    {
	file = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (chooser));
    }
    
    gtk_widget_destroy (chooser);
    
    if ( file )
    {
	gchar *uri;
	//FIXME: vcd will word for svcd?
	uri = g_strdup_printf ("%s%s", PAROLE_ISO_IMAGE_CD ? "dvd://" : ("vcd://"), file);
	TRACE ("Playing ISO image %s", uri);
	parole_player_disc_selected_cb (NULL, uri, NULL, player);
	g_free (file);
	g_free (uri);
    }
}

void dvd_iso_mi_activated_cb (GtkWidget *widget, ParolePlayer *player)
{
    parole_player_open_iso_image (player, PAROLE_ISO_IMAGE_DVD);
}

void cd_iso_mi_activated_cb (GtkWidget *widget,	 ParolePlayer *player)
{
    parole_player_open_iso_image (player, PAROLE_ISO_IMAGE_CD);
}

static void
parole_player_change_range_value (ParolePlayer *player, gdouble value)
{
    if ( !player->priv->user_seeking )
    {
	player->priv->internal_range_change = TRUE;
    
	gtk_range_set_value (GTK_RANGE (player->priv->range), value);

	player->priv->internal_range_change = FALSE;
    }
}

static void
parole_player_reset (ParolePlayer *player)
{
    parole_player_change_range_value (player, 0);

    if ( player->priv->row )
    {
	parole_media_list_set_row_pixbuf (player->priv->list, player->priv->row, NULL);
	gtk_tree_row_reference_free (player->priv->row);
	player->priv->row = NULL;
    }
}

static void
parole_player_media_activated_cb (ParoleMediaList *list, GtkTreeRowReference *row, ParolePlayer *player)
{
    ParoleFile *file;
    GtkTreeIter iter;
    GtkTreeModel *model;

    parole_player_reset (player);
    
    player->priv->row = gtk_tree_row_reference_copy (row);
    
    model = gtk_tree_row_reference_get_model (row);
    
    if ( gtk_tree_model_get_iter (model, &iter, gtk_tree_row_reference_get_path (row)) )
    {
	gtk_tree_model_get (model, &iter, DATA_COL, &file, -1);
	
	if ( file )
	{
	    gchar *sub = NULL;
	    const gchar *uri;
	    
	    uri = parole_file_get_uri (file);
	    
	    if ( g_str_has_prefix (uri, "file:/") )
	    {
		if ( parole_file_filter (player->priv->video_filter, file) )
		{
		    sub = parole_get_subtitle_path (uri);
		}
	    }
	    TRACE ("Trying to play media file %s", uri);
	    TRACE ("File content type %s", parole_file_get_content_type (file));
	    
	    gtk_widget_set_sensitive (player->priv->stop, TRUE);
	    
	    parole_gst_play_uri (PAROLE_GST (player->priv->gst), 
				 parole_file_get_uri (file),
				 sub);
	    g_free (sub);
	    g_object_unref (file);
	}
    }
}

static void
parole_player_disc_selected_cb (ParoleDisc *disc, const gchar *uri, const gchar *device, ParolePlayer *player)
{
    parole_player_reset (player);
    gtk_widget_set_sensitive (player->priv->stop, TRUE);
    
    parole_gst_play_device_uri (PAROLE_GST (player->priv->gst), uri, device);
}

static void
parole_player_uri_opened_cb (ParoleMediaList *list, const gchar *uri, ParolePlayer *player)
{
    parole_player_reset (player);
    gtk_widget_set_sensitive (player->priv->stop, TRUE);
    parole_gst_play_uri (PAROLE_GST (player->priv->gst), uri, NULL);
}

static void
parole_player_media_cursor_changed_cb (ParoleMediaList *list, gboolean media_selected, ParolePlayer *player)
{
    if (player->priv->state < PAROLE_MEDIA_STATE_PAUSED)
    {
	gtk_widget_set_sensitive (player->priv->play_pause, 
				  media_selected || !parole_media_list_is_empty (player->priv->list));
    }
}

static void
parole_player_media_progressed_cb (ParoleGst *gst, const ParoleStream *stream, gint64 value, ParolePlayer *player)
{
#ifdef DEBUG
    g_return_if_fail (value > 0);
#endif
    
    if ( !player->priv->user_seeking && player->priv->state == PAROLE_MEDIA_STATE_PLAYING )
    {
	parole_player_change_range_value (player, value);
    }
}

static void
parole_player_set_playpause_button_image (GtkWidget *widget, const gchar *stock_id)
{
    GtkWidget *img;
    
    g_object_get (G_OBJECT (widget),
		  "image", &img,
		  NULL);
		  
    g_object_set (G_OBJECT (img),
		  "stock", stock_id,
		  NULL);

    g_object_unref (img);
}

static void
parole_player_save_uri (ParolePlayer *player, const ParoleStream *stream)
{
    ParoleMediaType media_type;
    gchar *uri;
    gboolean save = TRUE;
    gchar **lines = NULL;
    guint i;

    g_object_get (G_OBJECT (stream),
		  "uri", &uri,
		  NULL);
    
    g_object_get (G_OBJECT (stream),
		  "media-type", &media_type,
		  NULL);
		  
    if ( media_type == PAROLE_MEDIA_TYPE_LOCAL_FILE )
    {
	gtk_recent_manager_add_item (player->priv->recent, uri);
	goto out;
    }
	
    lines = parole_get_history ();
    
    if (lines )
    {
	for ( i = 0; lines[i]; i++)
	{
	    if ( !g_strcmp0 (lines[i], uri) )
	    {
		save = FALSE;
		break;
	    }   
	}
    }
    
    if ( save )
    {
	parole_insert_line_history (uri);
    }
    
    g_strfreev (lines);
out:
    g_free (uri);
}

static void
parole_player_playing (ParolePlayer *player, const ParoleStream *stream)
{
    GdkPixbuf *pix = NULL;
    
    gint64 duration;
    gboolean seekable;
    gboolean live;
    
    player->priv->state = PAROLE_MEDIA_STATE_PLAYING;
    
    pix = parole_icon_load ("player_play", 16);
    
    if ( !pix )
	pix = parole_icon_load ("gtk-media-play-ltr", 16);
    
    parole_media_list_set_row_pixbuf (player->priv->list, player->priv->row, pix);
    
    g_object_get (G_OBJECT (stream),
		  "seekable", &seekable,
		  "duration", &duration,
		  "live", &live,
		  NULL);
		  
    gtk_widget_set_sensitive (player->priv->play_pause, TRUE);
    gtk_widget_set_sensitive (player->priv->stop, TRUE);
    
    parole_player_set_playpause_button_image (player->priv->play_pause, GTK_STOCK_MEDIA_PAUSE);
    
    gtk_widget_set_sensitive (player->priv->range, seekable);
    
    player->priv->internal_range_change = TRUE;
    if ( live || duration == 0)
	parole_player_change_range_value (player, 0);
    else 
	gtk_range_set_range (GTK_RANGE (player->priv->range), 0, duration);
	
    player->priv->internal_range_change = FALSE;
    
    gtk_widget_set_sensitive (player->priv->seekf, seekable);
    gtk_widget_set_sensitive (player->priv->seekb, seekable);
    gtk_widget_set_tooltip_text (GTK_WIDGET (player->priv->range), seekable ? NULL : _("Media stream is not seekable"));

    if ( pix )
	g_object_unref (pix);
	
    parole_player_save_uri (player, stream);
    
    gtk_widget_grab_focus (player->priv->gst);
}

static void
parole_player_paused (ParolePlayer *player)
{
    GdkPixbuf *pix = NULL;
    
    player->priv->state = PAROLE_MEDIA_STATE_PAUSED;
    
    TRACE ("Player paused");
    
    pix = parole_icon_load (GTK_STOCK_MEDIA_PAUSE, 16);
    parole_media_list_set_row_pixbuf (player->priv->list, player->priv->row, pix);
    
    gtk_widget_set_sensitive (player->priv->play_pause, TRUE);
    gtk_widget_set_sensitive (player->priv->stop, TRUE);
    
    if ( player->priv->user_seeking == FALSE)
	parole_player_set_playpause_button_image (player->priv->play_pause, GTK_STOCK_MEDIA_PLAY);
    
    if ( pix )
	g_object_unref (pix);
	
}

static void
parole_player_quit (ParolePlayer *player)
{
    parole_media_list_save_list (player->priv->list);
    parole_gst_shutdown (PAROLE_GST (player->priv->gst));
    gtk_widget_destroy (player->priv->window);
    g_object_unref (player);
    gtk_main_quit ();
}

static void
parole_player_stopped (ParolePlayer *player)
{
    TRACE ("Player stopped");
    
    player->priv->state = PAROLE_MEDIA_STATE_STOPPED;
    
    gtk_widget_set_sensitive (player->priv->play_pause, 
			      parole_media_list_is_selected_row (player->priv->list) || 
			      !parole_media_list_is_empty (player->priv->list));
    
    /* 
     * Set the stop widget insensitive only if we are not going to got to playing
     * state, this give the possibility to press on it if the media get stuck
     * for some reason.
     */
    if ( parole_gst_get_gst_target_state (PAROLE_GST (player->priv->gst)) != GST_STATE_PLAYING)
    {
	gtk_widget_set_sensitive (player->priv->stop, FALSE);
    }

    parole_player_change_range_value (player, 0);
    gtk_widget_set_sensitive (player->priv->range, FALSE);
    
    gtk_widget_set_sensitive (player->priv->seekf, FALSE);
    gtk_widget_set_sensitive (player->priv->seekb, FALSE);

    parole_player_set_playpause_button_image (player->priv->play_pause, GTK_STOCK_MEDIA_PLAY);
    
    parole_media_list_set_row_pixbuf (player->priv->list, player->priv->row, NULL);
    
    if ( player->priv->exit )
    {
	parole_player_quit (player);
    }
}

static void
parole_player_play_selected_row (ParolePlayer *player)
{
    GtkTreeRowReference *row;
    
    row = parole_media_list_get_selected_row (player->priv->list);
    
    if ( row == NULL )
	row = parole_media_list_get_first_row (player->priv->list);
    
    if ( row )
	parole_player_media_activated_cb (player->priv->list, row, player);
}

static void
parole_player_play_next (ParolePlayer *player, gboolean allow_shuffle)
{
    gboolean repeat, shuffle;
    
    GtkTreeRowReference *row;
    
    g_object_get (G_OBJECT (player->priv->conf),
		  "shuffle", &shuffle,
		  "repeat", &repeat,
		  NULL);
    
    if ( player->priv->row )
    {
	parole_media_list_set_row_pixbuf (player->priv->list, player->priv->row, NULL);
	
	if ( shuffle && allow_shuffle )
	    row = parole_media_list_get_row_random (player->priv->list);
	else
	    row = parole_media_list_get_next_row (player->priv->list, player->priv->row, repeat);
	
	if ( row )
	{
	    parole_player_media_activated_cb (player->priv->list, row, player);
	    return;
	}
	else
	{
	    TRACE ("No remaining media in the list");
	    gtk_tree_row_reference_free (player->priv->row);
	    player->priv->row = NULL;
	}
    }

    parole_gst_stop (PAROLE_GST (player->priv->gst));
}

static void
parole_player_play_prev (ParolePlayer *player)
{
    GtkTreeRowReference *row;
    
    if ( player->priv->row )
    {
	parole_media_list_set_row_pixbuf (player->priv->list, player->priv->row, NULL);
	
	row = parole_media_list_get_prev_row (player->priv->list, player->priv->row);
	
	if ( row )
	{
	    parole_player_media_activated_cb (player->priv->list, row, player);
	    return;
	}
	else
	{
	    TRACE ("No remaining media in the list");
	    gtk_tree_row_reference_free (player->priv->row);
	    player->priv->row = NULL;
	}
    }

    parole_gst_stop (PAROLE_GST (player->priv->gst));
}

static void
parole_player_reset_saver_changed (ParolePlayer *player, const ParoleStream *stream)
{
    gboolean reset_saver;
    
    TRACE ("Start");
    
    g_object_get (G_OBJECT (player->priv->conf),
		  "reset-saver", &reset_saver,
		  NULL);
		  
    if ( !reset_saver )
	parole_screen_saver_uninhibit (player->priv->screen_saver);
    else if ( player->priv->state ==  PAROLE_MEDIA_STATE_PLAYING )
    {
	gboolean has_video;
	
	g_object_get (G_OBJECT (stream),
		      "has-video", &has_video,
		      NULL);
		      
	if ( has_video )
	    parole_screen_saver_inhibit (player->priv->screen_saver);
    }
    else
	parole_screen_saver_uninhibit (player->priv->screen_saver);
}

static void
parole_player_media_state_cb (ParoleGst *gst, const ParoleStream *stream, ParoleMediaState state, ParolePlayer *player)
{
    PAROLE_DEBUG_ENUM ("State callback", state, GST_ENUM_TYPE_MEDIA_STATE);

    parole_player_reset_saver_changed (player, stream);
    
    if ( state == PAROLE_MEDIA_STATE_PLAYING )
    {
	parole_player_playing (player, stream);
    }
    else if ( state == PAROLE_MEDIA_STATE_PAUSED )
    {
	parole_player_paused (player);
    }
    else if ( state == PAROLE_MEDIA_STATE_STOPPED )
    {
	parole_player_stopped (player);
    }
    else if ( state == PAROLE_MEDIA_STATE_FINISHED )
    {
	TRACE ("***Playback finished***");
	parole_player_play_next (player, TRUE);
    }
}

void
parole_player_play_pause_clicked (GtkButton *button, ParolePlayer *player)
{
    if ( player->priv->state == PAROLE_MEDIA_STATE_PLAYING )
	parole_gst_pause (PAROLE_GST (player->priv->gst));
    else if ( player->priv->state == PAROLE_MEDIA_STATE_PAUSED )
	parole_gst_resume (PAROLE_GST (player->priv->gst));
    else
	parole_player_play_selected_row (player);
}

void
parole_player_stop_clicked (GtkButton *button, ParolePlayer *player)
{
    parole_gst_stop (PAROLE_GST (player->priv->gst));
}

/*
 * Seek 5%
 */
static gint64
parole_player_get_seek_value (ParolePlayer *player)
{
    gint64 val;
    gint64 dur;
    
    dur = parole_gst_get_stream_duration (PAROLE_GST (player->priv->gst));
    
    val = dur * 5 /100;
    
    return val;
}

void parole_player_seekf_cb (GtkButton *button, ParolePlayer *player)
{
    gdouble seek;
    
    seek =  parole_gst_get_stream_position (PAROLE_GST (player->priv->gst) )
	    +
	    parole_player_get_seek_value (player);
	    
    parole_gst_seek (PAROLE_GST (player->priv->gst), seek);
}
							 
void parole_player_seekb_cb (GtkButton *button, ParolePlayer *player)
{
    gdouble seek;
    
    seek =  parole_gst_get_stream_position (PAROLE_GST (player->priv->gst) )
	    -
	    parole_player_get_seek_value (player);
	    
    parole_gst_seek (PAROLE_GST (player->priv->gst), seek);
}

gboolean parole_player_scroll_event_cb (GtkWidget *widget, GdkEventScroll *ev, ParolePlayer *player)
{
    gboolean ret_val = FALSE;
    
    if ( ev->direction == GDK_SCROLL_UP )
    {
	parole_player_seekf_cb (NULL, player);
        ret_val = TRUE;
    }
    else if ( ev->direction == GDK_SCROLL_DOWN )
    {
	parole_player_seekb_cb (NULL, player);
        ret_val = TRUE;
    }

    return ret_val;
}

gboolean
parole_player_range_button_release (GtkWidget *widget, GdkEventButton *ev, ParolePlayer *player)
{
    ev->button = 2;
    
    player->priv->user_seeking = FALSE;
    
    return FALSE;
}

gboolean
parole_player_range_button_press (GtkWidget *widget, GdkEventButton *ev, ParolePlayer *player)
{
    ev->button = 2;
    
    player->priv->user_seeking = TRUE;
    
    return FALSE;
}

void
parole_player_range_value_changed (GtkRange *range, ParolePlayer *player)
{
    gdouble value;
    
    if ( !player->priv->internal_range_change )
    {
	value = gtk_range_get_value (GTK_RANGE (range));
	player->priv->user_seeking = TRUE;
	TRACE ("Sending a seek request with value :%e", value);
	parole_gst_seek (PAROLE_GST (player->priv->gst), value);
	player->priv->user_seeking = FALSE;
    }
}

static void
parole_player_error_cb (ParoleGst *gst, const gchar *error, ParolePlayer *player)
{
    xfce_err ("%s", error);
    parole_screen_saver_uninhibit (player->priv->screen_saver);
    parole_player_stopped (player);
}

static void
parole_player_media_tag_cb (ParoleGst *gst, const ParoleStream *stream, ParolePlayer *player)
{
    gchar *title;
    
    if ( player->priv->row )
    {
	g_object_get (G_OBJECT (stream),
		      "title", &title,
		      NULL);
	if ( title )
	{
	    parole_media_list_set_row_name (player->priv->list, player->priv->row, title);
	    g_free (title);
	}
    }
}

static void
parole_player_buffering_cb (ParoleGst *gst, const ParoleStream *stream, gint percentage, ParolePlayer *player)
{
    if ( percentage == 100 )
    {
	player->priv->buffering = FALSE;
	parole_gst_resume (PAROLE_GST (player->priv->gst));
    }
    else
    {
	player->priv->buffering = TRUE;
	
	if ( player->priv->state == PAROLE_MEDIA_STATE_PLAYING )
	    parole_gst_pause (PAROLE_GST (player->priv->gst));
    }
}

gboolean parole_player_delete_event_cb (GtkWidget *widget, GdkEvent *ev, ParolePlayer *player)
{
    parole_window_busy_cursor (GTK_WIDGET (player->priv->window)->window);
    
    player->priv->exit = TRUE;
    parole_gst_terminate (PAROLE_GST (player->priv->gst));
    
    return TRUE;
}

void
parole_player_destroy_cb (GtkObject *window, ParolePlayer *player)
{
}

static void
parole_player_play_menu_item_activate (ParolePlayer *player)
{
    gtk_widget_activate (player->priv->play_pause);
}

static void
parole_player_stop_menu_item_activate (ParolePlayer *player)
{
    gtk_widget_activate (player->priv->stop);
}

static void
parole_player_next_menu_item_activate (ParolePlayer *player)
{
    parole_disc_menu_seek_next (player->priv->disc_menu);
}

static void
parole_player_previous_menu_item_activate (ParolePlayer *player)
{
    parole_disc_menu_seek_prev (player->priv->disc_menu);
}

static void
parole_player_move_fs_window (ParolePlayer *player)
{
    GdkScreen *screen;
    GdkRectangle rect;
    
    screen = gtk_window_get_screen (GTK_WINDOW (player->priv->fs_window));
    
    gdk_screen_get_monitor_geometry (screen,
				     gdk_screen_get_monitor_at_window (screen, player->priv->window->window),
				     &rect);
    
    gtk_window_resize (GTK_WINDOW (player->priv->fs_window), 
		       rect.width, 
		       player->priv->play_box->allocation.width);
    
    gtk_window_move (GTK_WINDOW (player->priv->fs_window),
		     0, 
		     rect.height + rect.y - player->priv->play_box->allocation.height);
}

static void
parole_player_full_screen (ParolePlayer *player, gboolean fullscreen)
{
    gint npages;
    static gint current_page = 0;
    
    if ( player->priv->full_screen == fullscreen )
	return;
    
    if ( player->priv->full_screen )
    {
	npages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (player->priv->main_nt));
	gtk_widget_reparent (player->priv->play_box, player->priv->control);
	gtk_widget_hide (player->priv->fs_window);
	parole_statusbar_set_visible (player->priv->status, TRUE);
	parole_disc_menu_set_fullscreen (player->priv->disc_menu, FALSE);
	gtk_widget_show (player->priv->play_box);
	gtk_widget_show (player->priv->menu_bar);
	gtk_widget_show (player->priv->playlist_nt);
	gtk_widget_show (player->priv->show_hide_playlist);
	gtk_widget_hide (player->priv->leave_fs);
	
	gtk_notebook_set_show_tabs (GTK_NOTEBOOK (player->priv->main_nt), npages > 1);
	
	gtk_window_unfullscreen (GTK_WINDOW (player->priv->window));
	gtk_notebook_set_current_page (GTK_NOTEBOOK (player->priv->playlist_nt), current_page);
	player->priv->full_screen = FALSE;
    }
    else
    {
	parole_disc_menu_set_fullscreen (player->priv->disc_menu, TRUE);
	parole_player_move_fs_window (player);
	gtk_widget_reparent (player->priv->play_box, player->priv->fs_window);
	
	parole_statusbar_set_visible (player->priv->status, FALSE);
	
	gtk_widget_hide (player->priv->play_box);
	gtk_widget_hide (player->priv->menu_bar);
	gtk_widget_hide (player->priv->playlist_nt);
	gtk_widget_hide (player->priv->show_hide_playlist);
	gtk_widget_show (player->priv->leave_fs);
	
	current_page = gtk_notebook_get_current_page (GTK_NOTEBOOK (player->priv->playlist_nt));
	gtk_notebook_set_show_tabs (GTK_NOTEBOOK (player->priv->main_nt), FALSE);
	
	gtk_window_fullscreen (GTK_WINDOW (player->priv->window));
	player->priv->full_screen = TRUE;
    }
}

static void
parole_player_full_screen_menu_item_activate (ParolePlayer *player)
{
    parole_player_full_screen (player, !player->priv->full_screen);
}

void parole_player_full_screen_activated_cb (GtkWidget *widget, ParolePlayer *player)
{
    parole_player_full_screen_menu_item_activate (player);
}

void parole_player_leave_fs_cb (GtkButton *button, ParolePlayer *player)
{
    parole_player_full_screen_menu_item_activate (player);
}

static void
parole_player_show_menu (ParolePlayer *player, guint button, guint activate_time)
{
    GtkWidget *menu, *mi, *img;
    gboolean sensitive;
    gboolean dvd_menu;
    ParoleMediaType media_type;
    
    dvd_menu = parole_disc_menu_visible (player->priv->disc_menu);
    
    media_type = parole_gst_get_current_stream_type (PAROLE_GST (player->priv->gst));
    
    menu = gtk_menu_new ();
    
    /*Play menu item
     */
    mi = gtk_image_menu_item_new_from_stock (player->priv->state == PAROLE_MEDIA_STATE_PLAYING 
					     ? GTK_STOCK_MEDIA_PAUSE : GTK_STOCK_MEDIA_PLAY, 
					     NULL);
					     
    g_object_get (G_OBJECT (player->priv->play_pause),
		  "sensitive", &sensitive,
		  NULL);
		  
    gtk_widget_set_sensitive (mi, sensitive);
    gtk_widget_show (mi);
    g_signal_connect_swapped (mi, "activate",
			      G_CALLBACK (parole_player_play_menu_item_activate), player);
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
    
    /*
     * Seek Forward.
     */
    mi = gtk_image_menu_item_new_from_stock (GTK_STOCK_MEDIA_FORWARD, NULL);
					     
    gtk_widget_set_sensitive (mi, (player->priv->state >= PAROLE_MEDIA_STATE_PAUSED));
    gtk_widget_show (mi);
    g_signal_connect (mi, "activate",
		      G_CALLBACK (parole_player_seekf_cb), player);
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
    
    /*
     * Seek backward.
     */
    mi = gtk_image_menu_item_new_from_stock (GTK_STOCK_MEDIA_REWIND, NULL);
					     
    gtk_widget_set_sensitive (mi, (player->priv->state >= PAROLE_MEDIA_STATE_PAUSED));
    gtk_widget_show (mi);
    g_signal_connect (mi, "activate",
		      G_CALLBACK (parole_player_seekb_cb), player);
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
    
    /*
     * Stop menu item
     */
    mi = gtk_image_menu_item_new_from_stock (GTK_STOCK_MEDIA_STOP, NULL);
					     
    gtk_widget_set_sensitive (mi, player->priv->state == PAROLE_MEDIA_STATE_PLAYING);
    gtk_widget_show (mi);
    g_signal_connect_swapped (mi, "activate",
			      G_CALLBACK (parole_player_stop_menu_item_activate), player);
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
    
    /*
     * Next chapter menu item
     */
    mi = gtk_image_menu_item_new_with_label (media_type == PAROLE_MEDIA_TYPE_CDDA ? _("Next Track") : _("Next Chapter"));
    img = gtk_image_new_from_stock (GTK_STOCK_MEDIA_NEXT, GTK_ICON_SIZE_MENU);
    gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mi),img);
    gtk_widget_set_sensitive (mi, (player->priv->state == PAROLE_MEDIA_STATE_PLAYING) && dvd_menu);
    gtk_widget_show (mi);
    g_signal_connect_swapped (mi, "activate",
			      G_CALLBACK (parole_player_next_menu_item_activate), player);
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
    
    /*
     * Previous chapter menu item
     */
    mi = gtk_image_menu_item_new_with_label (media_type == PAROLE_MEDIA_TYPE_CDDA ? _("Previous Track") : _("Previous Chapter"));
    img = gtk_image_new_from_stock (GTK_STOCK_MEDIA_PREVIOUS, GTK_ICON_SIZE_MENU);
    gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mi), img);
					     
    gtk_widget_set_sensitive (mi, (player->priv->state == PAROLE_MEDIA_STATE_PLAYING) && dvd_menu);
    gtk_widget_show (mi);
    g_signal_connect_swapped (mi, "activate",
			      G_CALLBACK (parole_player_previous_menu_item_activate), player);
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
    
    mi = gtk_separator_menu_item_new ();
    gtk_widget_show (mi);
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
    
    /*
     * Un/Full screen
     */
    mi = gtk_image_menu_item_new_from_stock (player->priv->full_screen ? GTK_STOCK_LEAVE_FULLSCREEN:
					     GTK_STOCK_FULLSCREEN, NULL);
    gtk_widget_show (mi);
    g_signal_connect_swapped (mi, "activate",
			      G_CALLBACK (parole_player_full_screen_menu_item_activate), player);
    gtk_menu_shell_append (GTK_MENU_SHELL (menu), mi);
    

    g_signal_connect_swapped (menu, "selection-done",
			      G_CALLBACK (gtk_widget_destroy), menu);
    
    gtk_menu_popup (GTK_MENU (menu), 
		    NULL, NULL,
		    NULL, NULL,
		    button, activate_time);
}

static gboolean
parole_player_gst_widget_button_press (GtkWidget *widget, GdkEventButton *ev, ParolePlayer *player)
{
    gboolean ret_val = FALSE;

    if ( ev->type == GDK_2BUTTON_PRESS )
    {
	parole_player_full_screen_menu_item_activate (player);
	ret_val = TRUE;
    }

    return ret_val;
}

static gboolean
parole_player_gst_widget_button_release (GtkWidget *widget, GdkEventButton *ev, ParolePlayer *player)
{
    gboolean ret_val = FALSE;
    
    if ( ev->button == 3 )
    {
	parole_player_show_menu (player, ev->button, ev->time);
	gtk_widget_grab_focus (widget);
	ret_val = TRUE;
    }
    else if ( ev->button == 1 )
    {
	gtk_widget_grab_focus (widget);
	ret_val = TRUE;
    }
    
    return ret_val;
}

static gboolean parole_player_hide_fs_window (gpointer data)
{
    ParolePlayer *player;
    gint x, y, w, h;
    
    player = PAROLE_PLAYER (data);
    
    if ( GTK_WIDGET_VISIBLE (player->priv->fs_window) )
    {
	/* Don't hide the popup if the pointer is above it*/
	w = player->priv->fs_window->allocation.width;
	h = player->priv->fs_window->allocation.height;
	
	gtk_widget_get_pointer (player->priv->fs_window, &x, &y);
	
	if ((x >= 0) && (x <= w) && (y >= 0) && (y <= h))
	    return TRUE;

	gtk_widget_hide (player->priv->fs_window);
    }

    return FALSE;
}

static gboolean
parole_player_gst_widget_motion_notify_event (GtkWidget *widget, GdkEventMotion *ev, ParolePlayer *player)
{
    static gulong hide_timeout = 0;
    
    if ( player->priv->full_screen )
    {
	gtk_widget_show_all (player->priv->fs_window);
	if ( hide_timeout != 0 )
	{
	    g_source_remove (hide_timeout);
	    hide_timeout = 0;
	}
	    
	hide_timeout = g_timeout_add_seconds (4, (GSourceFunc) parole_player_hide_fs_window, player);
    }
    else if ( hide_timeout != 0)
    {
	g_source_remove (hide_timeout);
	hide_timeout = 0;
    }
    
    return FALSE;
}

void
parole_player_menu_open_location_cb (GtkWidget *widget, ParolePlayer *player)
{
    parole_media_list_open_location (player->priv->list);
}

void
parole_player_menu_add_cb (GtkWidget *widget, ParolePlayer *player)
{
    parole_media_list_open (player->priv->list);
}

void parole_player_open_preferences_cb	(GtkWidget *widget, ParolePlayer *player)
{
    ParoleConfDialog *dialog;
    
    dialog = parole_conf_dialog_new ();
    
    parole_conf_dialog_open (dialog, player->priv->window);
}

void
parole_player_menu_exit_cb (GtkWidget *widget, ParolePlayer *player)
{
    parole_player_delete_event_cb (NULL, NULL, player);
}


void parole_player_shuffle_toggled_cb (GtkWidget *widget, ParolePlayer *player)
{
    gboolean toggled;
    
    toggled = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget));
    
    g_object_set (G_OBJECT (player->priv->conf),
		  "shuffle", toggled,
		  NULL);
}

void parole_player_repeat_toggled_cb (GtkWidget *widget, ParolePlayer *player)
{
    gboolean toggled;
    
    toggled = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget));
    
    g_object_set (G_OBJECT (player->priv->conf),
		  "repeat", toggled,
		  NULL);
}

static const gchar *
parole_player_get_volume_icon_name (gdouble value)
{
    if ( value == 1. )
	return "audio-volume-high";
    else if ( value > 0.5 )
	return "audio-volume-medium";
    else if ( value > 0 )
	return "audio-volume-low";
    
    return "audio-volume-muted";
}

static void
parole_player_set_volume_image (ParolePlayer *player, gdouble value)
{
    GdkPixbuf *icon;

    icon = parole_icon_load (parole_player_get_volume_icon_name (value), 
			     player->priv->volume_image->allocation.width);
    if ( icon )
    {
	g_object_set (G_OBJECT (player->priv->volume_image),
		      "pixbuf", icon,
		      NULL);
	gdk_pixbuf_unref (icon);
    }
}

static void
parole_player_change_volume (ParolePlayer *player, gdouble value)
{
    parole_gst_set_volume (PAROLE_GST (player->priv->gst), value);
    parole_player_set_volume_image (player, value);
}

gboolean
parole_player_volume_scroll_event_cb (GtkWidget *widget, GdkEventScroll *ev, ParolePlayer *player)
{
    gboolean ret_val = FALSE;

    if ( ev->direction == GDK_SCROLL_UP )
    {
	parole_player_volume_up (NULL, player);
	ret_val = TRUE;
    }
    else if ( ev->direction == GDK_SCROLL_DOWN )
    {
	parole_player_volume_down (NULL, player);
	ret_val = TRUE;
    }

    return ret_val;
}

void
parole_player_volume_value_changed_cb (GtkRange *range, ParolePlayer *player)
{
    gdouble value;
    value = gtk_range_get_value (range);
    parole_player_change_volume (player, value);
    parole_rc_write_entry_int ("volume", PAROLE_RC_GROUP_GENERAL, (gint)(value * 100));
}

void
parole_player_volume_up (GtkWidget *widget, ParolePlayer *player)
{
    gdouble value;
    value = gtk_range_get_value (GTK_RANGE (player->priv->volume));
    gtk_range_set_value (GTK_RANGE (player->priv->volume), value + 0.1);
}

void
parole_player_volume_down (GtkWidget *widget, ParolePlayer *player)
{
    gdouble value;
    value = gtk_range_get_value (GTK_RANGE (player->priv->volume));
    gtk_range_set_value (GTK_RANGE (player->priv->volume), value - 0.1);
}

void parole_player_volume_mute (GtkWidget *widget, ParolePlayer *player)
{
    gtk_range_set_value (GTK_RANGE (player->priv->volume), 0);
}

static void
parole_player_screen_size_change_changed_cb (GdkScreen *screen, ParolePlayer *player)
{
    if ( player->priv->full_screen )
	parole_player_move_fs_window (player);
}

static void
parole_player_finalize (GObject *object)
{
    ParolePlayer *player;

    player = PAROLE_PLAYER (object);

    TRACE ("start");
    
    dbus_g_connection_unref (player->priv->bus);
    
    g_object_unref (player->priv->conf);
    g_object_unref (player->priv->video_filter);
    g_object_unref (player->priv->status);
    g_object_unref (player->priv->disc);
    g_object_unref (player->priv->disc_menu);
    g_object_unref (player->priv->screen_saver);
    
#ifdef HAVE_XF86_KEYSYM
    g_object_unref (player->priv->button);
#endif

    gtk_widget_destroy (player->priv->fs_window);

    G_OBJECT_CLASS (parole_player_parent_class)->finalize (object);
}

static void
parole_player_class_init (ParolePlayerClass *klass)
{
    GObjectClass *object_class = G_OBJECT_CLASS (klass);

    object_class->finalize = parole_player_finalize;

    g_type_class_add_private (klass, sizeof (ParolePlayerPrivate));
    
    parole_player_dbus_class_init (klass);
}

/**
 * Configuration changed regarding
 * whether to Reset the screen saver counter
 * while playing movies or not.
 * 
 */
static void
parole_player_reset_saver_changed_cb (ParolePlayer *player)
{
    const ParoleStream *stream;
    
    stream = parole_gst_get_stream (PAROLE_GST (player->priv->gst));
    TRACE ("Reset saver configuration changed");
    parole_player_reset_saver_changed (player, stream);
}

static gboolean
parole_player_handle_key_press (GdkEventKey *ev, ParolePlayer *player)
{
    GtkWidget *focused;
    
    gboolean ret_val = FALSE;
    
    focused = gtk_window_get_focus (GTK_WINDOW (player->priv->window));
    
    if ( focused )
    {
	if ( ( gtk_widget_is_ancestor (focused, player->priv->playlist_nt) ) ||
	     ( gtk_widget_is_ancestor (focused, player->priv->main_nt) && 
	       !gtk_widget_is_ancestor (focused, player->priv->main_box) ))
	{
	    return FALSE;
	}
    }
    
    switch (ev->keyval)
    {
	case GDK_F11:
	case GDK_f:
	case GDK_F:
	    parole_player_full_screen_menu_item_activate (player);
	    ret_val = TRUE;
	    break;
	case GDK_plus:
	    parole_player_volume_up (NULL, player);
	    ret_val = TRUE;
	    break;
	case GDK_space:
	case GDK_p:
	case GDK_P:
	    parole_player_play_pause_clicked (NULL, player);
	    ret_val = TRUE;
	    break;
	case GDK_minus:
	    parole_player_volume_down (NULL, player);
	    ret_val = TRUE;
	    break;
	case GDK_Right:
	    /* Media seekable ?*/
	    if ( GTK_WIDGET_SENSITIVE (player->priv->range) )
		parole_player_seekf_cb (NULL, player);
	    ret_val = TRUE;
	    break;
	case GDK_Left:
	    if ( GTK_WIDGET_SENSITIVE (player->priv->range) )
		parole_player_seekb_cb (NULL, player);
	    ret_val = TRUE;
	    break;
	case GDK_s:
	case GDK_S:
	    parole_player_stop_clicked (NULL, player);
	    ret_val = TRUE;
	    break;
	case GDK_Escape:
	    parole_player_full_screen (player, FALSE);
	    break;
#ifdef HAVE_XF86_KEYSYM
	case XF86XK_OpenURL:
	    parole_player_full_screen (player, FALSE);
	    parole_media_list_open_location (player->priv->list);
	    break;
#endif
	case GDK_O:
	case GDK_o:
	    if ( ev->state & GDK_CONTROL_MASK )
	    {
		parole_player_full_screen (player, FALSE);
		parole_media_list_open (player->priv->list);
	    }
	break;
	/* 
	 * Pass these to the media list and tell it to
	 * grab the focus
	 */
	case GDK_Up:
	case GDK_Down:
	    parole_media_list_grab_focus (player->priv->list);
	    break;
	default:
	    break;
    }
    
    return ret_val;
}

gboolean
parole_player_key_press (GtkWidget *widget, GdkEventKey *ev, ParolePlayer *player)
{
/*
    gchar *key;
    key = gdk_keyval_name (ev->keyval);
    g_print ("Key Press 0x%X:%s on widget=%s\n", ev->keyval, key, gtk_widget_get_name (widget));
*/

    switch (ev->keyval)
    {
	case GDK_F11:
	    parole_player_full_screen_menu_item_activate (player);
	    return TRUE;
#ifdef HAVE_XF86_KEYSYM
	case XF86XK_AudioPlay:
	    parole_player_play_pause_clicked (NULL, player);
	    return TRUE;
	case XF86XK_AudioStop:
	    parole_player_stop_clicked (NULL, player);
	    return TRUE;
	case XF86XK_AudioRaiseVolume:
	    parole_player_volume_up (NULL, player);
	    return TRUE;
	case XF86XK_AudioLowerVolume:
	    parole_player_volume_down (NULL, player);
	    return TRUE;
	case XF86XK_AudioMute:
	    parole_player_volume_mute (NULL, player);
	    return TRUE;
	case XF86XK_AudioPrev:
	    if ( !parole_disc_menu_seek_prev (player->priv->disc_menu))
		parole_player_play_prev (player);
	    return TRUE;
	case XF86XK_AudioNext:
	    if ( !parole_disc_menu_seek_next (player->priv->disc_menu))
		parole_player_play_next (player, FALSE);
	    return TRUE;
#endif /* HAVE_XF86_KEYSYM */
	default:
	    break;
    }
    
    return parole_player_handle_key_press (ev, player);
}

#ifdef HAVE_XF86_KEYSYM
static void
parole_player_button_pressed_cb (ParoleButton *button, ParoleButtonKey key, ParolePlayer *player)
{
    PAROLE_DEBUG_ENUM ("Button Press:", key, ENUM_GTYPE_BUTTON_KEY);
    
    switch (key)
    {
	case PAROLE_KEY_AUDIO_PLAY:
	    parole_player_play_pause_clicked (NULL, player);
	    break;
	case PAROLE_KEY_AUDIO_STOP:
	    parole_player_stop_clicked (NULL, player);
	    break;
	case PAROLE_KEY_AUDIO_PREV:
	    if ( !parole_disc_menu_seek_prev (player->priv->disc_menu))
		parole_player_play_prev (player);
	    break;
	case PAROLE_KEY_AUDIO_NEXT:
	    if ( !parole_disc_menu_seek_next (player->priv->disc_menu))
		parole_player_play_next (player, FALSE);
	    break;
	default:
	    break;
    }
}
#endif

static void
parole_player_session_die_cb (ParolePlayer *player)
{
    player->priv->exit = TRUE;
    parole_gst_terminate (PAROLE_GST (player->priv->gst));
}

static void
parole_gst_set_default_aspect_ratio (ParolePlayer *player, GtkBuilder *builder)
{
    ParoleAspectRatio ratio;
    const gchar *widget_name;
    
    g_object_get (G_OBJECT (player->priv->conf),
		  "aspect-ratio", &ratio,
		  NULL);
		  
    switch (ratio )
    {
	case PAROLE_ASPECT_RATIO_NONE:
	    widget_name = "ratio_none";
	    break;
	case PAROLE_ASPECT_RATIO_AUTO:
	    widget_name = "ratio_auto";
	    break;
	case PAROLE_ASPECT_RATIO_SQUARE:
	    widget_name = "ratio_square";
	    break;
	case PAROLE_ASPECT_RATIO_16_9:
	    widget_name = "ratio_16_9";
	    break;
	case PAROLE_ASPECT_RATIO_4_3:
	    widget_name = "ratio_4_3";
	    break;
	case PAROLE_ASPECT_RATIO_DVB:
	    widget_name = "ratio_20_9";
	    break;
	default:
	    g_warn_if_reached ();
	    return;
    }
	
    gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, widget_name)), 
				    TRUE);
}

static void
parole_player_set_sound_menu_items_image (GtkBuilder *builder)
{
    GtkWidget *img;
    
    img = gtk_image_new_from_icon_name ("audio-volume-muted", GTK_ICON_SIZE_MENU);
    gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (gtk_builder_get_object (builder, "volume-mute-menu")), img);
    
    img = gtk_image_new_from_icon_name ("audio-volume-high", GTK_ICON_SIZE_MENU);
    gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (gtk_builder_get_object (builder, "volume-up-menu")), img);
    
    img = gtk_image_new_from_icon_name ("audio-volume-low", GTK_ICON_SIZE_MENU);
    gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (gtk_builder_get_object (builder, "volume-down-menu")), img);
}

gboolean
parole_player_configure_event_cb (GtkWidget *widget, GdkEventConfigure *ev, ParolePlayer *player)
{
    gint w,h;
    
    if ( !player->priv->full_screen )
    {
	gtk_window_get_size (GTK_WINDOW (widget), &w, &h);
	g_object_set (G_OBJECT (player->priv->conf),
		      "window-width", w,
		      "window-height", h,
		      NULL);
    }
    
    return FALSE;
}

static void
parole_player_drag_data_received_cb (GtkWidget *widget,
				     GdkDragContext *drag_context,
				     gint x,
				     gint y,
				     GtkSelectionData *data,
				     guint info,
				     guint drag_time,
				     ParolePlayer *player)
{
    
    gchar **uri_list;
    guint added  = 0;
    guint i;
    
    parole_window_busy_cursor (widget->window);
    
    uri_list = g_uri_list_extract_uris ((const gchar *)data->data);
    for ( i = 0; uri_list[i] != NULL; i++)
    {
	gchar *path;
	path = g_filename_from_uri (uri_list[i], NULL, NULL);
	added += parole_media_list_add_by_path (player->priv->list, path, i == 0 ? TRUE : FALSE);

	g_free (path);
    }
    
    g_strfreev (uri_list);

    gdk_window_set_cursor (widget->window, NULL);
    gtk_drag_finish (drag_context, added == i ? TRUE : FALSE, FALSE, drag_time);
}

static void
parole_player_window_notify_is_active (ParolePlayer *player)
{
    if ( !player->priv->full_screen )
	return;
	
    if (!gtk_window_is_active (GTK_WINDOW (player->priv->window)) )
    {
	gtk_widget_hide (player->priv->fs_window);
	parole_gst_set_cursor_visible (PAROLE_GST (player->priv->gst), TRUE);
    } 
    else 
    {
	parole_gst_set_cursor_visible (PAROLE_GST (player->priv->gst), FALSE);
    }
} 

/**
 * 
 * Sets the _NET_WM_WINDOW_OPACITY_LOCKED wm hint 
 * so window manager keep us opaque.
 * 
 * Currently it is only supported by xfwm.
 * 
 * NOTE: The widget has to be realized first.
 **/
static void
parole_player_set_wm_opacity_hint (GtkWidget *widget)
{
    GdkScreen *gdkscreen;
    GdkDisplay *gdkdisplay;
    GdkWindow *gdkwindow;
    Display *xdisplay;
    Atom atom;
    char mode = 1;
    
    gdkscreen = gtk_widget_get_screen (widget);
    gdkdisplay = gdk_screen_get_display (gdkscreen);

    xdisplay = GDK_DISPLAY_XDISPLAY (gdkdisplay);
    
    atom = XInternAtom (xdisplay, "_NET_WM_WINDOW_OPACITY_LOCKED", TRUE);
    
    if ( atom == None )
	return;
    
    gdkwindow = gtk_widget_get_window (widget);
    
    XChangeProperty (xdisplay, GDK_WINDOW_XID (gdkwindow),
		     atom, XA_CARDINAL,
		     32, PropModeAppend,
		     (guchar *) &mode, 
		     1);
}

static void
parole_player_init (ParolePlayer *player)
{
    GtkWidget *output;
    GtkBuilder *builder;
    GdkScreen *screen;
    gint w, h;
    
    gboolean repeat, shuffle;
    
    player->priv = PAROLE_PLAYER_GET_PRIVATE (player);
    
    player->priv->bus = parole_g_session_bus_get ();
    
    player->priv->video_filter = parole_get_supported_video_filter ();
    g_object_ref_sink (player->priv->video_filter);
    
    builder = parole_builder_get_main_interface ();
    
    player->priv->conf = parole_conf_new ();
    
    g_signal_connect_swapped (player->priv->conf, "notify::reset-saver",
			      G_CALLBACK (parole_player_reset_saver_changed_cb), player);
    
    player->priv->session = parole_session_get ();
    
    g_signal_connect_swapped (player->priv->session, "die",
			      G_CALLBACK (parole_player_session_die_cb), player);
    
    player->priv->gst = parole_gst_new (FALSE, player->priv->conf);

    player->priv->status = parole_statusbar_new ();

    player->priv->disc = parole_disc_new ();
    g_signal_connect (player->priv->disc, "disc-selected",
		      G_CALLBACK (parole_player_disc_selected_cb), player);
		      
    player->priv->disc_menu = parole_disc_menu_new ();
    
    player->priv->screen_saver = parole_screen_saver_new ();
    player->priv->list = PAROLE_MEDIA_LIST (parole_media_list_get ());
    
    player->priv->state = PAROLE_MEDIA_STATE_STOPPED;
    player->priv->user_seeking = FALSE;
    player->priv->internal_range_change = FALSE;
    player->priv->exit = FALSE;
    player->priv->full_screen = FALSE;
    player->priv->buffering = FALSE;
    player->priv->row = NULL;
    
    player->priv->recent = gtk_recent_manager_get_default ();
    
    parole_player_set_sound_menu_items_image (builder);
    /*
     * Gst signals
     */
    g_signal_connect (G_OBJECT (player->priv->gst), "media-state",
		      G_CALLBACK (parole_player_media_state_cb), player);
	
    g_signal_connect (G_OBJECT (player->priv->gst), "media-progressed",
		      G_CALLBACK (parole_player_media_progressed_cb), player);
		      
    g_signal_connect (G_OBJECT (player->priv->gst), "media-tag",
		      G_CALLBACK (parole_player_media_tag_cb), player);
    
    g_signal_connect (G_OBJECT (player->priv->gst), "error",
		      G_CALLBACK (parole_player_error_cb), player);
   
    g_signal_connect (G_OBJECT (player->priv->gst), "buffering",
		      G_CALLBACK (parole_player_buffering_cb), player);
    
    g_signal_connect_after (G_OBJECT (player->priv->gst), "button-release-event",
			    G_CALLBACK (parole_player_gst_widget_button_release), player);
    
    g_signal_connect_after (G_OBJECT (player->priv->gst), "button-press-event",
			    G_CALLBACK (parole_player_gst_widget_button_press), player);
    
    g_signal_connect (G_OBJECT (player->priv->gst), "motion-notify-event",
		      G_CALLBACK (parole_player_gst_widget_motion_notify_event), player);
    
    output = GTK_WIDGET (gtk_builder_get_object (builder, "output"));
    
    gtk_drag_dest_set (output, GTK_DEST_DEFAULT_ALL, 
		       target_entry, G_N_ELEMENTS (target_entry),
                       GDK_ACTION_COPY | GDK_ACTION_MOVE);
		       
    g_signal_connect (output, "drag-data-received",
		      G_CALLBACK (parole_player_drag_data_received_cb), player);
    
    player->priv->window = GTK_WIDGET (gtk_builder_get_object (builder, "main-window"));
   
    player->priv->main_nt = GTK_WIDGET (gtk_builder_get_object (builder, "main-notebook"));
    
    player->priv->play_pause = GTK_WIDGET (gtk_builder_get_object (builder, "play-pause"));
    player->priv->stop = GTK_WIDGET (gtk_builder_get_object (builder, "stop"));
    player->priv->seekf = GTK_WIDGET (gtk_builder_get_object (builder, "forward"));
    player->priv->seekb = GTK_WIDGET (gtk_builder_get_object (builder, "back"));
     
    player->priv->range = GTK_WIDGET (gtk_builder_get_object (builder, "scale"));
    
    player->priv->volume = GTK_WIDGET (gtk_builder_get_object (builder, "volume"));
    player->priv->volume_image = GTK_WIDGET (gtk_builder_get_object (builder, "volume-image"));
    
    player->priv->menu_bar = GTK_WIDGET (gtk_builder_get_object (builder, "menubar"));
    player->priv->play_box = GTK_WIDGET (gtk_builder_get_object (builder, "play-box"));
    player->priv->playlist_nt = GTK_WIDGET (gtk_builder_get_object (builder, "notebook-playlist"));
    player->priv->show_hide_playlist = GTK_WIDGET (gtk_builder_get_object (builder, "show-hide-list"));
    player->priv->control = GTK_WIDGET (gtk_builder_get_object (builder, "control"));
    player->priv->leave_fs = GTK_WIDGET (gtk_builder_get_object (builder, "leave_fs"));
    player->priv->main_box = GTK_WIDGET (gtk_builder_get_object (builder, "main-box"));
    
    gtk_range_set_range (GTK_RANGE (player->priv->volume), 0, 1.0);
    
    gtk_range_set_value (GTK_RANGE (player->priv->volume), 
			 (gdouble) (parole_rc_read_entry_int ("volume", PAROLE_RC_GROUP_GENERAL, 100)/100.));
    
    /*
     * Pack the playlist.
     */
    gtk_notebook_append_page (GTK_NOTEBOOK (player->priv->playlist_nt), 
			      GTK_WIDGET (player->priv->list),
			      gtk_label_new (_("Playlist")));
			      
    g_object_get (G_OBJECT (player->priv->conf),
		  "window-width", &w,
		  "window-height", &h,
		  NULL);
    
    gtk_window_set_default_size (GTK_WINDOW (player->priv->window), w, h);
    
    gtk_widget_show_all (player->priv->window);
    
    parole_player_set_wm_opacity_hint (player->priv->window);
    
    gtk_box_pack_start (GTK_BOX (output), 
			player->priv->gst,
			TRUE, TRUE, 0);
    
    gtk_widget_realize (player->priv->gst);
    gtk_widget_show (player->priv->gst);

    parole_player_change_volume (player, 
				 (gdouble) (parole_rc_read_entry_int ("volume", PAROLE_RC_GROUP_GENERAL, 100)/100.));

    g_signal_connect (player->priv->list, "media_activated",
		      G_CALLBACK (parole_player_media_activated_cb), player);
		      
    g_signal_connect (player->priv->list, "media_cursor_changed",
		      G_CALLBACK (parole_player_media_cursor_changed_cb), player);
		      
    g_signal_connect (player->priv->list, "uri-opened",
		      G_CALLBACK (parole_player_uri_opened_cb), player);
    
    /*
     * Load auto saved media list.
     */
    parole_media_list_load (player->priv->list);
    
    g_object_get (G_OBJECT (player->priv->conf),
		  "repeat", &repeat,
		  "shuffle", &shuffle,
		  NULL);

    gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "repeat")),
				    repeat);

    gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (gtk_builder_get_object (builder, "shuffle")),
				    shuffle);
	
    player->priv->fs_window = gtk_window_new (GTK_WINDOW_POPUP);

    gtk_window_set_gravity (GTK_WINDOW (player->priv->fs_window), GDK_GRAVITY_SOUTH_WEST);
    gtk_window_set_position (GTK_WINDOW (player->priv->fs_window), GTK_WIN_POS_NONE);
  
    parole_gst_set_default_aspect_ratio (player, builder);
	
    gtk_builder_connect_signals (builder, player);
    
    screen = gtk_widget_get_screen (player->priv->window);
    g_signal_connect (G_OBJECT (screen), "size-changed",
		      G_CALLBACK (parole_player_screen_size_change_changed_cb), player);
    
    g_object_unref (builder);
    
#ifdef HAVE_XF86_KEYSYM
    player->priv->button = parole_button_new ();
    g_signal_connect (player->priv->button, "button-pressed",
		      G_CALLBACK (parole_player_button_pressed_cb), player);
#endif
    
    g_signal_connect_swapped (player->priv->window, "notify::is-active",
			      G_CALLBACK (parole_player_window_notify_is_active), player);
    
    parole_player_dbus_init (player);
}

ParolePlayer *
parole_player_new (void)
{
    ParolePlayer *player = NULL;
    player = g_object_new (PAROLE_TYPE_PLAYER, NULL);
    return player;
}

ParoleMediaList	*parole_player_get_media_list (ParolePlayer *player)
{
    return player->priv->list;
}

void parole_player_play_uri_disc (ParolePlayer *player, const gchar *uri, const gchar *device)
{
    if ( uri )
    {
	parole_player_disc_selected_cb (NULL, uri, device, player);
    }
    else if (device)
    {
	gchar *uri_local = parole_get_uri_from_unix_device (device);
	if ( uri_local )
	{
	    parole_player_disc_selected_cb (NULL, uri_local, device, player);
	    g_free (uri_local);
	}
    }
}

void parole_player_terminate (ParolePlayer *player)
{
    parole_player_delete_event_cb (NULL, NULL, player);
}


static gboolean	parole_player_dbus_play (ParolePlayer *player,
					 GError *error);

static gboolean	parole_player_dbus_stop (ParolePlayer *player,
					 GError *error);

static gboolean	parole_player_dbus_next_track (ParolePlayer *player,
					       GError *error);

static gboolean	parole_player_dbus_prev_track (ParolePlayer *player,
					       GError *error);

static gboolean	parole_player_dbus_seek_forward (ParolePlayer *player,
					         GError *error);

static gboolean	parole_player_dbus_seek_backward (ParolePlayer *player,
					          GError *error);

static gboolean	parole_player_dbus_raise_volume (ParolePlayer *player,
						 GError *error);

static gboolean	parole_player_dbus_lower_volume (ParolePlayer *player,
						 GError *error);
					 
static gboolean	parole_player_dbus_mute (ParolePlayer *player,
					 GError *error);

static gboolean parole_player_dbus_play_disc (ParolePlayer *player,
					      gchar *in_uri,
					      gchar *in_device,
					      GError **error);

#include "org.parole.media.player.h"

/*
 * DBus server implementation
 */
static void 
parole_player_dbus_class_init (ParolePlayerClass *klass)
{
    
    dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (klass),
				     &dbus_glib_parole_player_object_info);
				     
}

static void
parole_player_dbus_init (ParolePlayer *player)
{
    dbus_g_connection_register_g_object (player->priv->bus,
					 PAROLE_DBUS_PATH,
					 G_OBJECT (player));
}

static gboolean	parole_player_dbus_play (ParolePlayer *player,
					 GError *error)
{
    
    parole_player_play_pause_clicked (NULL, player);
    return TRUE;
}

static gboolean	parole_player_dbus_stop (ParolePlayer *player,
					 GError *error)
{
    parole_gst_stop (PAROLE_GST (player->priv->gst));
    return TRUE;
}

static gboolean	parole_player_dbus_next_track (ParolePlayer *player,
					       GError *error)
{
    if ( !parole_disc_menu_seek_next (player->priv->disc_menu))
	parole_player_play_next (player, FALSE);
    return TRUE;
}

static gboolean	parole_player_dbus_prev_track (ParolePlayer *player,
					       GError *error)
{
    if ( !parole_disc_menu_seek_prev (player->priv->disc_menu))
	parole_player_play_prev (player);
    return TRUE;
}

static gboolean	parole_player_dbus_seek_forward (ParolePlayer *player,
					         GError *error)
{
    parole_player_seekf_cb (NULL, player);
    return TRUE;
}

static gboolean	parole_player_dbus_seek_backward (ParolePlayer *player,
					          GError *error)
{
    parole_player_seekb_cb (NULL, player);
    return TRUE;
}
					 
static gboolean	parole_player_dbus_raise_volume (ParolePlayer *player,
						 GError *error)
{
    parole_player_volume_up (NULL, player);
    return TRUE;
}

static gboolean	parole_player_dbus_lower_volume (ParolePlayer *player,
						 GError *error)
{
    parole_player_volume_down (NULL, player);
    return TRUE;
}
					 
static gboolean	parole_player_dbus_mute (ParolePlayer *player,
					 GError *error)
{
    gtk_range_set_value (GTK_RANGE (player->priv->volume), 0);
    return TRUE;
}

static gboolean parole_player_dbus_play_disc (ParolePlayer *player,
					      gchar *in_uri,
					      gchar *in_device,
					      GError **error)
{
    TRACE ("uri : %s", in_uri);
    
    gtk_window_present (GTK_WINDOW (player->priv->window));
    
    if ( parole_is_uri_disc (in_uri) )
	parole_player_disc_selected_cb (NULL, in_uri, in_device, player);
	
    return TRUE;
}
