/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: mainapp.cpp,v 1.55.2.19 2004/11/23 00:24:25 rggammon Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

/* This file contains functions with several different prefixes.
 * They include:
 *   hmw - For (h)xplay (m)ain (w)indow, these functions are designed
 *         to be used as signal handlers that can be used by a glade
 *         window.
 *
 *   hxwindow - Each top-level window has a hxwindow object associated
 *              with it.
 *
 *   hpw - For (h)xplayer (p)layer (w)idget, these functions are
 *         designed to work as callbacks to the hx_player_widget.
 *
 *   no prefix - These functions are "private" supporting functions for
 *               one of the methods above, and are not intended for general
 *               use
 */

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

#include <X11/Xatom.h>

#ifdef USE_GNOME
#include <gnome.h>
#endif

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

#include "appver.h"
#include "hxplayer-i18n.h"

/* Gtk widgt includes */
#include "hxplayer.h"
#include "hxbin.h"
#include "hxplayer.h"
#include "hxstatisticsobserver.h"
#include "hxgvalue.h"
#include "hxgprefs.h"

/* Player includes */
#include "winutils.h"
#include "mainapp.h"
#include "commonapp.h"
#include "remote.h"
#include "hxstatustop.h"
#include "hxstatuspositionfield.h"
#include "hxstatuspositionslider.h"
#include "favorites.h"
#include "vidctrls.h"
#include "about.h"
#include "statistics.h"
#include "prefsdialog.h"
#include "clipdetails.h"
#include "plugins.h"
#include "open.h"
#include "contextmenu.h"
#include "switchboard.h"
#include "sysinfo.h"

/* Helix includes (currently for viewsrc) */
#include "hxcom.h"
#include "hxwintyp.h"
#include "hxcore.h"
#include "hxvsrc.h"


#ifdef HELIX_FEATURE_RP_SIGN_IN
#include "signin.h"
#endif

/* Constants and #defines
 * ======================
 */

#define REGISTERED_PREF    APP_PREFIX_LC "registered"

/* Define the size of the empty video widget if no logo is provided,
   and no content is playing */
#define DEFAULT_VIDEO_WIDGET_WIDTH  320
#define DEFAULT_VIDEO_WIDGET_HEIGHT 200

#define HIDE_CURSOR_TIMEOUT 2 /* Cursor hiding timeout in fullscreen (seconds) */

/* some defaults for handling recent urls */
#define DEF_NUM_RECENT_URLS  20
#define DEF_NUM_FILE_MENU_URLS 8

#define MAX_GROUP_TITLE_LENGTH 60

/* Data structures
 * ===============
 */

typedef enum {
    ON_TOP_NEVER,
    ON_TOP_WHILE_PLAYING,
    ON_TOP_ALWAYS
} HXMainWindowOnTop;

typedef struct _HXMainWindow
{
    GladeXML* xml;
    
    GtkWidget* player;
    GtkWidget* hxbin;

    GtkWidget* window;
    GtkWidget* vbox;
    
    GtkWidget* play_button;
    GtkWidget* stop_button;
    GtkWidget* pause_button;
    GtkWidget* previous_button;
    GtkWidget* next_button;
    GtkWidget* play_pause_button;

    GtkWidget* play_pause_image;

    GtkWidget* seek_scale;
    GtkWidget* volume_scale;
    GtkWidget* mute_button;
    GtkWidget* volume_image;
    GtkWidget* status_bar;
    GtkWidget* menu_bar;
    GtkWidget* controls_hbox;
    
    /* menu items of interest */
    GtkWidget* play_menu_item;
    GtkWidget* stop_menu_item;
    GtkWidget* pause_menu_item;
    GtkWidget* next_menu_item;
    GtkWidget* previous_menu_item;

    GtkWidget* on_top_never_menu_item;
    GtkWidget* on_top_while_playing_menu_item;
    GtkWidget* on_top_always_menu_item;

    GtkWidget* normal_size_menu_item;
    GtkWidget* double_size_menu_item;
    
    GdkPixbuf* play_pixbuf;
    GdkPixbuf* pause_pixbuf;
    GdkPixbuf* mute_pixbuf;
    GdkPixbuf* volume_off_pixbuf;
    GdkPixbuf* volume_low_pixbuf;
    GdkPixbuf* volume_mid_pixbuf;
    GdkPixbuf* volume_high_pixbuf;

    GtkDialog* video_controls_dialog;
    GtkDialog* about_dialog;
    GtkDialog* preferences_dialog;
    GtkDialog* manage_favorites_dialog;
    GtkDialog* statistics_dialog;
    GtkDialog* open_location_dialog;
    GtkDialog* open_file_dialog;
    GtkDialog* clip_details_dialog;
    GtkDialog* plugins_dialog;
    GtkDialog* import_favorites_dialog;
    GtkDialog* export_favorites_dialog;

    HXContextMenu* context_menu;
    
    HXMainWindowZoomSize zoom;
    gint custom_zoom_height, custom_zoom_width;    
    gboolean is_fullscreened;

    gboolean show_controls_on_unfullscreen;
    gboolean show_menu_and_caption_on_unfullscreen;
    gboolean show_status_bar_on_unfullscreen;
    HXMainWindowZoomSize zoom_on_unfullscreen;
    HXMainWindowOnTop on_top_on_unfullscreen;
    gint custom_zoom_height_on_unfullscreen;
    gint custom_zoom_width_on_unfullscreen;
    
    gboolean fullscreen_motion_notify_enabled;
    gint hxbin_fullscreen_motion_notify_handler;
    gint player_fullscreen_motion_notify_handler;
    
    gboolean fullscreen_hide_cursor_timer_enabled;
    guint    fullscreen_hide_cursor_timer_id;

    HXMainWindowOnTop on_top;

    gboolean on_top_while_playing_request_pending;
    
    gboolean has_content;
    
    /* cache the x and y window positions so that we
       can save them properly when the window is destroyed */
    gint x_pos, y_pos;

    /* command line switchs */
    gboolean quit_on_done;

    /* Player preferences */
    gboolean enable_url_hurling;
    gchar* media_files_path;
    gchar* web_browser_path;
    gchar* last_browsed_directory;

    /* Real.com declares that these are 7 char's max (+ '\0') */
    gchar distcode[8]; /* Code for player distribution method */
    gchar origcode[8]; /* The original distcode, before any updates/upgrades */
    
    /* Recently played urls */
    GList* recent_urls_list;
    gboolean enable_recent_urls;

    gboolean is_seeking;

} HXMainWindow;


typedef struct 
{
    /* Players */
    GList* windows_list;

    /* Favorites */
    GList* favorites_list;

    /* Sign in ("R1") preferences (type HXSignInPref) */
    GList* sign_in_preferences_list; 
} HXMainApp;


typedef enum {
    PLAY_MENU_TYPE_FAVORITE = 0,
    PLAY_MENU_TYPE_MRU
} HXPlayMenuItemType;

/* Our global data is stored in an app object.
   Data that is common between our window and the mozilla
   plugin embedded window can be found in g_hxcommon_app. */
static HXMainApp* g_hx_main_app = NULL;

/* Signal handler prototypes
 * =========================
 */
extern "C"
{
/* These can be used as signal callbacks with glade. The widget
   parameter is (usually) used to locate the toplevel widget via
   gtk_widget_get_toplevel. The top level widget has an "hxwindow"
   key set on it which points us at our per window data structure.
   This datastructure is then used in a call to the corresponding
   hxwindow_xxx function. */
    
void     hmw_open_location      (GtkWidget* widget);
void     hmw_open_file          (GtkWidget* widget);
void     hmw_quit               (GtkWidget* widget);
void     hmw_close              (GtkWidget* widget);
void     hmw_play               (GtkWidget* widget);
void     hmw_pause              (GtkWidget* widget);
void     hmw_stop               (GtkWidget* widget);
void     hmw_previous           (GtkWidget* widget);
void     hmw_next               (GtkWidget* widget);
void     hmw_play_pause         (GtkWidget* widget);
void     hmw_volume_changed     (GtkWidget* widget);
void     hmw_volume_up          (GtkWidget* widget);
void     hmw_volume_down        (GtkWidget* widget);
void     hmw_mute               (GtkWidget* widget);

/* These callbacks don't have hxwindow_xxx counterparts (yet) */
void     hmw_fullscreen         (GtkWidget* widget);
void     hmw_normal_size        (GtkWidget* widget);
void     hmw_double_size        (GtkWidget* widget);
void     hmw_clip_details       (GtkWidget* widget);
void     hmw_statistics         (GtkWidget* widget);
void     hmw_video_controls     (GtkWidget* widget);
void     hmw_show_menu_and_caption   (GtkWidget* widget);
void     hmw_show_controls      (GtkWidget* widget);
void     hmw_show_status_bar    (GtkWidget* widget);
void     hmw_plugins            (GtkWidget* widget);
void     hmw_manage_favorites   (GtkWidget* widget);
void     hmw_add_favorite       (GtkWidget* widget);
void     hmw_export_favorites   (GtkWidget* widget);
void     hmw_import_favorites   (GtkWidget* widget);
void     hmw_intro              (GtkWidget* widget);
void     hmw_release_notes      (GtkWidget* widget);    
void     hmw_faq                (GtkWidget* widget);
void     hmw_report_issue       (GtkWidget* widget);    
void     hmw_nightly_build      (GtkWidget* widget);
void     hmw_check_for_updates  (GtkWidget* widget);    
void     hmw_about              (GtkWidget* widget);
void     hmw_view_source        (GtkWidget* widget);
void     hmw_file_menu_update   (GtkWidget* widget);
void     hmw_favorites_menu_update(GtkWidget* widget);
void     hmw_player_reset       (GtkWidget* widget);    

#ifdef HELIX_FEATURE_RP_SIGN_IN
void     hmw_sign_in            (GtkWidget* widget);
#endif

void     hmw_on_top_never        (GtkWidget *widget);
void     hmw_on_top_while_playing(GtkWidget *widget);
void     hmw_on_top_always       (GtkWidget *widget);
void     hmw_play_from_menu_item (GtkWidget* widget,
                                 gchar* url);
void     hmw_play_group         (GtkWidget* widget,
                                 guint group);

/* see prefsdialog.cpp for hmw_preferences */
void     hmw_preferences        (GtkWidget* widget);

void     hmw_zoom_menu_refresh(GtkWidget* widget);
    
/* Event callbacks, connectable to button_down */
gboolean hmw_seek_start         (GtkWidget* widget);
gboolean hmw_seek_stop          (GtkWidget* widget);

gboolean  hmw_key_press_event   (GtkWidget *widget,
                                 GdkEventKey *event,
                                 gpointer user_data);    

GtkWidget* hmw_create_hxbin     (gchar *widget_name,
                                 gchar *string1,
                                 gchar *string2,
                                 gint int1,
                                 gint int2);

GtkWidget* hmw_create_status_area(gchar *widget_name,
                                  gchar *string1,
                                  gchar *string2,
                                  gint int1,
                                  gint int2);

GtkWidget* hmw_create_position_slider(gchar *widget_name,
                                      gchar *string1,
                                      gchar *string2,
                                      gint int1,
                                      gint int2);

void     hmw_realize (GtkWidget* widget,
                      gpointer);

gboolean hmw_configure_event(GtkWidget* widget,
                             GdkEventConfigure *event,
                             gpointer);

gboolean hmw_window_state_event(GtkWidget *widget,
                                GdkEventWindowState *event,
                                gpointer user_data);

gboolean hmw_property_notify_event(GtkWidget *widget,
                                   GdkEventProperty *event,
                                   gpointer user_data);

void hmw_drag_data_received(GtkWidget*        widget,
                            GdkDragContext*   context,
                            gint              x,
                            gint              y,
                            GtkSelectionData* selection_data,
                            guint             info,
                            guint             time);
    

static HXMainWindow*  hxwindow_get_from_widget (GtkWidget* widget);
static void           hxwindow_destroy         (HXMainWindow* window);

}; // extern "C"

/* Recent url management implementation
 * ====================================
 */
static void
recent_urls_menu_unpopulate(HXMainWindow* window)
{
    GtkWidget* separator;
    GtkWidget* file_menu;
    GtkWidget* file_menu_item;
    GtkWidget* child;
    GList* children_iter;

    separator = glade_xml_get_widget(window->xml, "hmw_mru_separator");
    g_return_if_fail(separator != NULL);

    file_menu_item = glade_xml_get_widget(window->xml, "hmw_file_menu");
    g_return_if_fail(file_menu_item != NULL);

    file_menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(file_menu_item));
    g_return_if_fail(file_menu != NULL);

    children_iter = GTK_MENU_SHELL(file_menu)->children;

    while(children_iter)
    {
        child = (GtkWidget*)children_iter->data;
        children_iter = g_list_next(children_iter);
        
        if(g_object_get_data(G_OBJECT(child), "is_mru"))
        {
            gtk_container_remove(GTK_CONTAINER(file_menu), child);
        }
    }
}

static void
recent_urls_menu_populate(HXMainWindow* window)
{
    GtkWidget *separator;
    GtkWidget *file_menu, *file_menu_item;
    gint pos;
    guint i;
    GList *children;
    GtkWidget *menu_item;

    separator = glade_xml_get_widget(window->xml, "hmw_mru_separator");
    g_return_if_fail(separator != NULL);

    file_menu_item = glade_xml_get_widget(window->xml, "hmw_file_menu");
    g_return_if_fail(file_menu_item != NULL);

    file_menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(file_menu_item));
    g_return_if_fail(file_menu != NULL);

    children = GTK_MENU_SHELL(file_menu)->children;
    
    pos = g_list_index(children, separator);
    g_return_if_fail(pos >= 0);

    pos++;
    i = 0;

    GList* iter = window->recent_urls_list;
    while(iter && i < DEF_NUM_FILE_MENU_URLS)
    {
        // XXXNH: we really need to fix how we store recent urls to
        // include the actual title of the clip. for now we'll just
        // shorten the url        
        gchar* escaped_filename;
        gchar* menu_item_text; 
        gchar* title;
        gchar* url;

        url = g_strdup((const char*)iter->data);
        title = hxcommon_get_title_from_url(url);

        escaped_filename = hxcommon_escape_underscores(title);
        g_free(title);
        
        menu_item_text = g_strdup_printf("_%d. %s", i+1, escaped_filename);
        g_free(escaped_filename);
                            
        menu_item = gtk_menu_item_new_with_mnemonic(menu_item_text);
        g_free(menu_item_text);
        g_object_set_data(G_OBJECT(menu_item), "is_mru", (gpointer)TRUE);
        gtk_widget_show(GTK_WIDGET(menu_item));
        gtk_menu_shell_insert(GTK_MENU_SHELL(file_menu), menu_item, pos);

        g_object_set_data(G_OBJECT(menu_item), "item_type", (gpointer) PLAY_MENU_TYPE_MRU);

        g_signal_connect(G_OBJECT(menu_item), "activate",
                         G_CALLBACK(hmw_play_from_menu_item), url);

        g_object_set_data(G_OBJECT(menu_item), "url", url);
        
        /* Free the g_strdup'd url from above on menu destruction */
        g_signal_connect_swapped(G_OBJECT(menu_item), "destroy",
                                 G_CALLBACK(g_free), url);        
        i++;
        pos++;
        iter = g_list_next(iter);
    }

    /* Only insert the extra separator if there are recent url's
       in the file menu */
    if(window->recent_urls_list)
    {
        menu_item = gtk_menu_item_new ();
        g_object_set_data(G_OBJECT(menu_item), "is_mru", (gpointer)TRUE);
        gtk_widget_show(GTK_WIDGET(menu_item));
        gtk_widget_set_sensitive (menu_item, FALSE);
        gtk_menu_shell_insert(GTK_MENU_SHELL(file_menu), menu_item, pos);
    }
}

static void
recent_urls_menu_refresh(HXMainWindow* window)
{
    recent_urls_menu_unpopulate(window);

    if(window->enable_recent_urls)
    {
        recent_urls_menu_populate(window);
    }
}

static void
recent_urls_add(HXMainWindow* window,
                const gchar* url,
                gboolean append)
{
    gint len;
    GList* last;
    GList* pos;
    GList* iter;

    /* Check for duplicates */
    iter = window->recent_urls_list;
    while(iter)
    {
        if(strcmp(url, (gchar*)iter->data) == 0)
        {
            /* Already in our list */
            return;
        }
        
        iter = g_list_next(iter);
    }
    
    len = g_list_length(window->recent_urls_list);
    last = g_list_last(window->recent_urls_list);
    
    while(len >= DEF_NUM_RECENT_URLS)
    {
        /* Remove old items from the end of the list until we have
           the right number of urls */

        pos = g_list_previous(last);
        g_free(last->data);
        window->recent_urls_list =
            g_list_delete_link(window->recent_urls_list, last);
        last = pos;
        len--;
    }

    if(append)
    {
        window->recent_urls_list =
            g_list_append(window->recent_urls_list,
                          g_strdup(url));    
    }
    else
    {
        window->recent_urls_list =
            g_list_prepend(window->recent_urls_list,
                           g_strdup(url));    
    }
    
    /* Refresh the menu */
    recent_urls_menu_refresh(window);
}

static void
populate_clip_list_menu(HXMainWindow* window)
{
    GtkWidget* clip_list_menu_item;
    GtkWidget* menu_item;
    GtkWidget* clip_list_menu;
    gchar* menu_item_text;
    guint i;
    guint group_count;

    clip_list_menu_item = glade_xml_get_widget(window->xml, "hmw_clip_list_menu_item");
    g_return_if_fail(clip_list_menu_item != NULL);

    /* get rid of the old sub-menu */
    gtk_menu_item_remove_submenu(GTK_MENU_ITEM(clip_list_menu_item));

    group_count = hx_player_get_group_count(HX_PLAYER(window->player));
    if(group_count == 0)
    {
        gtk_widget_set_sensitive(clip_list_menu_item, FALSE);
    }
    else
    {
        gtk_widget_set_sensitive(clip_list_menu_item, TRUE);

        clip_list_menu = gtk_menu_new();
        gtk_menu_item_set_submenu(GTK_MENU_ITEM(clip_list_menu_item), clip_list_menu);

        for(i = 0; i < group_count; i++)
        {
            const gchar* group_title_raw = hx_player_get_group_title(HX_PLAYER(window->player), i);
            gchar* group_title;

            if(!group_title_raw)
            {
                group_title_raw = _("Untitled");
            }
            
            if(group_title_raw)
            {
                group_title = g_strdup(group_title_raw);
                if(strlen(group_title_raw) > MAX_GROUP_TITLE_LENGTH)
                {
                    group_title[MAX_GROUP_TITLE_LENGTH] = '\0';
                }
            }

            gchar* escaped_group_title;
            escaped_group_title = hxcommon_escape_underscores(group_title);

            menu_item_text = g_strdup_printf("_%d. %s", i+1, escaped_group_title);
            g_free(escaped_group_title);
            
            menu_item = gtk_menu_item_new_with_mnemonic(menu_item_text);
            g_free(menu_item_text);
            gtk_widget_show(GTK_WIDGET(menu_item));
            gtk_menu_shell_append(GTK_MENU_SHELL(clip_list_menu), menu_item);
        
            g_signal_connect(G_OBJECT(menu_item), "activate",
                             G_CALLBACK(hmw_play_group), (gpointer)i);        
        }
    }
}

/* setup assistant helper functions */
gboolean check_registered()
{
    gboolean bRegistered = FALSE;

    HXEntry* entry = hx_prefs_get_entry(REGISTERED_PREF);
    if (entry)
    {
	HXValue* value = hx_entry_get_value(entry);
	if (atoi(hx_value_get_string(value)) != 0)
	{
	    bRegistered = TRUE;
	}
    }
    
    return bRegistered;
}


gint
show_setup_assistant(HXMainWindow* window)
{
    gint nResult = 0;
    gchar* filename;
    GladeXML* xml;
    
    filename = hxcommon_locate_file("setup.glade");
    xml = glade_xml_new (filename, NULL, NULL);
    g_free(filename);

    // XXXRGG: This will only work for the generated source glade build.
    // Setup assistant has to be fixed to work with libglade.
    GtkWidget* pSetupAssistant = GTK_WIDGET(xml);
    g_return_val_if_fail(pSetupAssistant != NULL, -1);
        
    if (pSetupAssistant)
    {
	nResult = 1;

	gtk_widget_show(pSetupAssistant);
	while (gtk_events_pending() || GTK_WIDGET_VISIBLE(pSetupAssistant))
	    gtk_main_iteration();

#ifdef HELIX_FEATURE_REAL_BRANDING // XXXNH: no EULA for helix player
	if (!g_object_get_data(G_OBJECT(pSetupAssistant), "eula-accepted"))
	{
	    nResult = -1;
	}
#endif

	if (nResult >= 0)
	{
	    HXValue* value = hx_value_new(HX_VALUE_STRING);
	    hx_value_set_string(value, "1");
	    hx_prefs_set_entry(REGISTERED_PREF, value);
            hxcommon_save_preferences(window);
        }
    }
    return nResult;
}

void
hxwindow_recent_urls_remove_all(HXMainWindow* window)
{
    g_list_foreach(window->recent_urls_list, (GFunc)g_free, NULL);
    g_list_free(window->recent_urls_list);
    window->recent_urls_list = NULL;
    
    recent_urls_menu_unpopulate(window);
}

void
hxwindow_recent_urls_hide(HXMainWindow* window)
{
    recent_urls_menu_unpopulate(window);
}


void
hxwindow_recent_urls_show(HXMainWindow* window)
{    
    recent_urls_menu_unpopulate(window);
    recent_urls_menu_populate(window);
}

void
hxwindow_recent_urls_prepend(HXMainWindow* window,
                             const gchar* url)
{
    recent_urls_add(window, url, FALSE);
}

void
hxwindow_recent_urls_append(HXMainWindow* window,
                            const gchar* url)
{
    recent_urls_add(window, url, TRUE);    
}

/* Favorites management implementation
 * ===================================
 */
static void
favorites_menu_unpopulate(HXMainWindow* window)
{
    GtkWidget* separator;
    GtkWidget* favorites_menu;
    GtkWidget* child;
    GtkWidget* menu_shell;
    GList* children_iter;
    gboolean remove_items = FALSE;

    separator = glade_xml_get_widget(window->xml, "hmw_favorites_separator");
    g_return_if_fail(separator != NULL);

    favorites_menu = glade_xml_get_widget(window->xml, "hmw_favorites_menu");
    g_return_if_fail(favorites_menu != NULL);

    menu_shell = gtk_menu_item_get_submenu(GTK_MENU_ITEM(favorites_menu));
    g_return_if_fail(menu_shell != NULL);

    children_iter = GTK_MENU_SHELL(menu_shell)->children;

    while(children_iter)
    {
        child = (GtkWidget*)children_iter->data;
        children_iter = g_list_next(children_iter);
        
        if(remove_items)
        {
            gtk_container_remove(GTK_CONTAINER(menu_shell), child);
        }

        if(child == separator)
        {
            remove_items = TRUE;
        }
    }
}

static void
favorites_menu_populate(HXMainWindow* window)
{
    GtkWidget* separator;
    GtkWidget* favorites_menu;
    gint pos;
    GList* children;
    GtkWidget* menu_item;
    GtkWidget* menu_shell;

    separator = glade_xml_get_widget(window->xml, "hmw_favorites_separator");
    g_return_if_fail(separator != NULL);

    favorites_menu = glade_xml_get_widget(window->xml, "hmw_favorites_menu");
    g_return_if_fail(favorites_menu != NULL);

    menu_shell = gtk_menu_item_get_submenu(GTK_MENU_ITEM(favorites_menu));
    g_return_if_fail(menu_shell != NULL);

    children = GTK_MENU_SHELL(menu_shell)->children;
    
    pos = g_list_index(children, separator);
    g_return_if_fail(pos >= 0);

    pos++;

    gint i = 0;
    GList* iter = g_hx_main_app->favorites_list;
    while(iter)
    {
        gchar* escaped_title;
        gchar* menu_item_text;
        gchar* url;
        HXFavorite* favorite = (HXFavorite*)iter->data;
        
        escaped_title = hxcommon_escape_underscores(favorite->title);

        menu_item_text = g_strdup_printf("_%d. %s", i+1, escaped_title);
        g_free(escaped_title);
                            
        menu_item = gtk_menu_item_new_with_mnemonic(menu_item_text);
        g_free(menu_item_text);
        gtk_widget_show(GTK_WIDGET(menu_item));
        gtk_menu_shell_insert(GTK_MENU_SHELL(menu_shell), menu_item, pos);

        g_object_set_data(G_OBJECT(menu_item), "item_type", (gpointer) PLAY_MENU_TYPE_FAVORITE);
        
        url = g_strdup(favorite->url);
        g_signal_connect(G_OBJECT(menu_item), "activate",
                         G_CALLBACK(hmw_play_from_menu_item), url);
        
        /* Free the g_strdup'd url from above on menu destruction */
        g_signal_connect_swapped(G_OBJECT(menu_item), "destroy",
                                 G_CALLBACK(g_free), url);        
        i++;
        pos++;
        iter = g_list_next(iter);
    }
}

static void
favorites_menu_refresh(HXMainWindow* window)
{
    favorites_menu_unpopulate(window);
    favorites_menu_populate(window);
}

void
hxwindow_favorite_add(HXMainWindow* window,
                      const gchar*  favorite_title,
                      const gchar*  favorite_url)
{
    HXFavorite* favorite = hx_favorite_new();

    favorite->title = g_strdup(favorite_title);
    favorite->url = g_strdup(favorite_url);

    g_hx_main_app->favorites_list =
        g_list_append(g_hx_main_app->favorites_list, favorite);


    favorites_menu_refresh(window);
}


/* hxwindow_* functions 
 * ==============================
 * functions to manipulate a HXMainWindow object
 */

GtkWidget*
hxwindow_get_player(HXMainWindow* window)
{
    return window->player;
}

GtkWidget*
hxwindow_get_window(HXMainWindow* window)
{
    return window->window;
}

gboolean
hxwindow_is_fullscreened (HXMainWindow* window)
{
    return window->is_fullscreened;
}

HXMainWindowZoomSize
hxwindow_get_zoom (HXMainWindow* window)
{
    return window->zoom;
}

GladeXML*
hxwindow_get_glade_xml (HXMainWindow* window)
{
    return window->xml;
}


HXContentStateType
hxwindow_get_content_state(HXMainWindow* window)
{
    return hx_player_get_content_state (HX_PLAYER(window->player));
}

G_CONST_RETURN gchar*
hxwindow_get_distcode (HXMainWindow* window)
{
    return window->distcode;
}

G_CONST_RETURN gchar*
hxwindow_get_origcode (HXMainWindow* window)
{
    return window->origcode;
}

void
hxwindow_set_quit_on_done(HXMainWindow* window,
                          gboolean quit)
{
    window->quit_on_done = quit;
}

void
hxwindow_change_layout(HXMainWindow* window)
{
    gint ideal_player_width,       ideal_player_height;
    gint desired_window_height,    desired_window_width;
    gboolean has_visual_content;

    GList* children_iter;
    
    HXContentStateType state;
    HXMainWindowZoomSize zoom;

    if(!window->player)
    {
        return;
    }

    has_visual_content = hx_player_has_visual_content(HX_PLAYER(window->player));
    
    if(!has_visual_content && 
       !GTK_WIDGET_VISIBLE(window->menu_bar))                     
    {
        /* If the menu has been hidden, the video area must be visible so the
           player doesn't disapper. This should only be an issue when playing
           a playlist that contains both audio and video clips when all controls
           are hidden. */
        
        gtk_widget_show(window->hxbin);
        hx_player_get_logo_size(HX_PLAYER(window->player),
                                &ideal_player_width,
                                &ideal_player_height);
    }
    else if(hx_player_get_url(HX_PLAYER(window->player)) == NULL)
    {
        /* No content is set -- player proabably just started, so make us
           the default size */
        ideal_player_width = DEFAULT_VIDEO_WIDGET_WIDTH;
        ideal_player_height = DEFAULT_VIDEO_WIDGET_HEIGHT;
    }
    else
    {
        hx_player_get_ideal_size(HX_PLAYER(window->player),
                                 &ideal_player_width, &ideal_player_height);
    }
    
    state = hx_player_get_content_state(HX_PLAYER(window->player));
    if(state == HX_CONTENT_STATE_NOT_LOADED || !has_visual_content)
    {
        zoom = NORMAL_SIZE;
    }
    else
    {        
        zoom = window->zoom;
    }
               
    /* Set the menu items based on the zoom setting to be used while
       playing */
    switch(zoom)
    {
        case NORMAL_SIZE:
            desired_window_width = ideal_player_width;
            desired_window_height = ideal_player_height;
            break;
            
        case DOUBLE_SIZE:
            desired_window_width = 2 * ideal_player_width;
            desired_window_height = 2 * ideal_player_height;
            break;
            
        case CUSTOM_SIZE:
        default:
            desired_window_height = window->custom_zoom_height;
            desired_window_width = window->custom_zoom_width;
            break;
    }
        
    /* Apply vbox's borders & spacings */
    guint border_width, spacing, visible_child_count = 0;
    border_width = gtk_container_get_border_width(GTK_CONTAINER(window->vbox));
    spacing = gtk_box_get_spacing(GTK_BOX(window->vbox));

    desired_window_height += 2 * border_width;
    desired_window_width  += 2 * border_width;

    /* Add children's allocations */
    children_iter = gtk_container_get_children(GTK_CONTAINER(window->vbox));
    while(children_iter)
    {
        GtkWidget* widget = GTK_WIDGET(children_iter->data);
        if(GTK_WIDGET_VISIBLE(widget))
        {
            if(widget != window->hxbin)
            {
                GtkRequisition child_requisition;
                guint padding;
                gboolean expand, fill;
                
                expand = FALSE;
                fill = FALSE;
                padding = 0;
                gtk_box_query_child_packing(GTK_BOX(window->vbox),
                                            widget,
                                            &expand,
                                            &fill,
                                            &padding,
                                            NULL);

                g_assert(!fill);
                g_assert(!expand);
                g_assert(padding == 0);
            
                gtk_widget_size_request(widget, &child_requisition);
                desired_window_height += child_requisition.height + padding * 2;
            }
            visible_child_count++;
        }
        children_iter = g_list_next(children_iter);
    }

    if(visible_child_count > 0)
    {
        desired_window_height += (visible_child_count - 1) * spacing;
    }
    
    /* Windows have to be at least 1x1 */
    if(desired_window_width <= 0)
    {
        desired_window_width = 1;
    }
    if(desired_window_height <= 0)
    {
        desired_window_height = 1;
    }

    gtk_window_resize(GTK_WINDOW(window->window),
                      desired_window_width,
                      desired_window_height);
}


void
hxwindow_update_ui(HXMainWindow* window)
{
    guint current_group, group_count;

    populate_clip_list_menu(window);
    
    group_count = hx_player_get_group_count(HX_PLAYER(window->player));
    current_group = hx_player_get_current_group(HX_PLAYER(window->player));

    if(current_group == 0)
    {
        gtk_widget_set_sensitive(GTK_WIDGET(window->previous_button), FALSE);
        gtk_widget_set_sensitive(GTK_WIDGET(window->previous_menu_item), FALSE);
    }
    else
    {
        gtk_widget_set_sensitive(GTK_WIDGET(window->previous_button), TRUE);
        gtk_widget_set_sensitive(GTK_WIDGET(window->previous_menu_item), TRUE);
    }

    if((current_group + 1) < group_count)
    {
        gtk_widget_set_sensitive(GTK_WIDGET(window->next_button), TRUE);
        gtk_widget_set_sensitive(GTK_WIDGET(window->next_menu_item), TRUE);
    }
    else
    {
        gtk_widget_set_sensitive(GTK_WIDGET(window->next_button), FALSE);
        gtk_widget_set_sensitive(GTK_WIDGET(window->next_menu_item), FALSE);
    }
}

void
hxwindow_set_property(HXMainWindow* window, const gchar* key, GValue* value)
{
    guint uint_val;
    gboolean bool_val;
    
    if(strcmp("XPos", key) == 0)
    {
        if(G_VALUE_HOLDS_STRING(value))
        {
            uint_val = atoi(g_value_get_string(value));
        }
        else
        {
            uint_val = g_value_get_uint(value);
        }
           
        window->x_pos = uint_val;
    }
    else if(strcmp("YPos", key) == 0)
    {
        if(G_VALUE_HOLDS_STRING(value))
        {
            uint_val = atoi(g_value_get_string(value));
        }
        else
        {
            uint_val = g_value_get_uint(value);
        }

        window->y_pos = uint_val;

        /* XXXRGG: Hack. fixme */
        if(window->x_pos && window->y_pos)
        {
            gtk_window_move(GTK_WINDOW(window->window), window->x_pos, window->y_pos);
        }
    }
    else if(strcmp("EnableRecentUrlsList", key) == 0)
    {
        if(G_VALUE_HOLDS_STRING(value))
        {
            bool_val = atoi(g_value_get_string(value));
        }
        else
        {
            bool_val = g_value_get_boolean(value);
        }

        window->enable_recent_urls = bool_val;
    }
    else if(strcmp("MediaFilesPath", key) == 0)
    {
        g_free(window->media_files_path);
        window->media_files_path = g_strdup(g_value_get_string(value));
    }
    else if(strcmp("WebBrowserPath", key) == 0)
    {
        g_free(window->web_browser_path);
        window->web_browser_path = g_strdup(g_value_get_string(value));
    }
    else if(strcmp("EnableUrlHurling", key) == 0)
    {
        if(G_VALUE_HOLDS_STRING(value))
        {
            bool_val = atoi(g_value_get_string(value));
        }
        else
        {
            bool_val = g_value_get_boolean(value);
        }

        window->enable_url_hurling = bool_val;
    }
    else if(strcmp("LastBrowsedDirectory", key) == 0)
    {
        g_free(window->last_browsed_directory);
        window->last_browsed_directory = g_strdup(g_value_get_string(value));
    }    
    else if(strcmp("DistCode", key) == 0)
    {
        const gchar* distcode = g_value_get_string(value);
        snprintf(window->distcode, sizeof(window->distcode), "%s", distcode);
    }    
    else if(strcmp("OrigCode", key) == 0)
    {
        const gchar* origcode = g_value_get_string(value);
        snprintf(window->origcode, sizeof(window->origcode), "%s", origcode);
    }    
    else
    {
        g_warning("Unknown property %s in config file", key);
    }
}

gboolean
hxwindow_get_property(HXMainWindow* window,
                      const gchar*  key,
                      GValue*       value)
{
    gboolean ret = TRUE;
    
    if(strcmp("XPos", key) == 0)
    {
        g_value_init(value, G_TYPE_UINT);
        g_value_set_uint(value, window->x_pos);
    }
    else if(strcmp("YPos", key) == 0)
    {
        g_value_init(value, G_TYPE_UINT);
        g_value_set_uint(value, window->y_pos);        
    }
    else if(strcmp("EnableRecentUrlsList", key) == 0)
    {
        g_value_init(value, G_TYPE_BOOLEAN);
        g_value_set_boolean(value, window->enable_recent_urls);
    }
    else if(strcmp("MediaFilesPath", key) == 0)
    {
        g_value_init(value, G_TYPE_STRING);
        g_value_set_string(value, window->media_files_path);
    }
    else if(strcmp("WebBrowserPath", key) == 0)
    {
        g_value_init(value, G_TYPE_STRING);
        g_value_set_string(value, window->web_browser_path);
    }
    else if(strcmp("EnableUrlHurling", key) == 0)
    {
        g_value_init(value, G_TYPE_BOOLEAN);
        g_value_set_boolean(value, window->enable_url_hurling);
    }
    else if(strcmp("LastBrowsedDirectory", key) == 0)
    {
        g_value_init(value, G_TYPE_STRING);
        g_value_set_string(value, window->last_browsed_directory);
    }
    else if(strcmp("DistCode", key) == 0)
    {
        g_value_init(value, G_TYPE_STRING);
        g_value_set_string(value, window->distcode);
    }
    else if(strcmp("OrigCode", key) == 0)
    {
        g_value_init(value, G_TYPE_STRING);
        g_value_set_string(value, window->origcode);
    }
    else
    {
        g_warning("Unknown property %s in config file", key);
        ret = FALSE;
    }

    return ret;
}

#ifdef HELIX_FEATURE_RP_SIGN_IN
void
hxwindow_set_sign_in_property(HXMainWindow* /* window */,
                              const gchar*  key,
                              const gchar*  value)
{
    GList* pref_iter = g_hx_main_app->sign_in_preferences_list;
    HXSignInPref* pref = NULL;

    while(pref_iter)
    {
        HXSignInPref* cur_pref = (HXSignInPref*)pref_iter->data;

        if(strcmp(key, cur_pref->key) == 0)
        {
            pref = cur_pref;
            break;
        }        
        pref_iter = g_list_next(pref_iter);
    }

    if(!pref)
    {
        pref = g_new0(HXSignInPref, 1);
        pref->key = g_strdup(key);
        g_hx_main_app->sign_in_preferences_list = g_list_append(g_hx_main_app->sign_in_preferences_list, pref);
    }
    
    g_free(pref->value);
    pref->value = g_strdup(value);

}

G_CONST_RETURN gchar*
hxwindow_get_sign_in_property(HXMainWindow* /* window */,
                              const gchar*  key)
{
    GList* pref_iter = g_hx_main_app->sign_in_preferences_list;

    while(pref_iter)
    {
        HXSignInPref* pref = (HXSignInPref*)pref_iter->data;

        if(strcmp(key, pref->key) == 0)
        {
            return pref->value;
        }        
        pref_iter = g_list_next(pref_iter);
    }

    return NULL;
}
#endif // HELIX_FEATURE_RP_SIGN_IN


void
hxwindow_save_preferences(HXMainWindow* window, GIOChannel* chan)
{
    gchar* line;
    gboolean result;
    guint i;
    GValue val;

    /* Write out window preferences */

    gchar* uint_props[] =
    {
        /* XXXRGG: See GnomeClient for proper "session management"
           tracking of the last window positions. */
        
        "XPos",
        "YPos"
    };

    gchar* bool_props[] =
    {
        "EnableRecentUrlsList",
        "EnableUrlHurling",
    };
    
    gchar* str_props[] =
    {
        "MediaFilesPath",
        "WebBrowserPath",
        "LastBrowsedDirectory",
        "DistCode",
        "OrigCode"
    };
    
    memset(&val, 0, sizeof(val));
    
    hxcommon_channel_write(chan, "\n[player]\n");

    for(i = 0; i < sizeof(uint_props) / sizeof(*uint_props); i++)
    {
        if(hxwindow_get_property(window, uint_props[i], &val))
        {
            line = g_strdup_printf("%s=%d\n",
                                   uint_props[i],
                                   g_value_get_uint(&val));
            result = hxcommon_channel_write(chan, line);
            g_free(line);    
            g_value_unset(&val);
            g_return_if_fail(result);
        }
    }

    for(i = 0; i < sizeof(bool_props) / sizeof(*bool_props); i++)
    {
        if(hxwindow_get_property(window, bool_props[i], &val))
        {
            line = g_strdup_printf("%s=%d\n",
                                   bool_props[i],
                                   g_value_get_boolean(&val)? 1: 0);
            result = hxcommon_channel_write(chan, line);
            g_free(line);    
            g_value_unset(&val);
            g_return_if_fail(result);
        }
    }

    for(i = 0; i < sizeof(str_props) / sizeof(*str_props); i++)
    {
        if(hxwindow_get_property(window, str_props[i], &val))
        {
            const gchar* str = g_value_get_string(&val);

            if(!str)
            {
                str = "";
            }
            
            line = g_strdup_printf("%s=%s\n",
                                   str_props[i],
                                   str);
            result = hxcommon_channel_write(chan, line);
            g_free(line);    
            g_value_unset(&val);
            g_return_if_fail(result);
        }
    }
    
    /* Step 3: Write out recently used url's */
    GList* recent_iter;

    hxcommon_channel_write(chan, "\n[recent_urls]\n");
    i = 0;
    recent_iter = window->recent_urls_list;
    while(recent_iter)
    {        
        line = g_strdup_printf("url%d=%s\n", i, (gchar*)recent_iter->data);
        
        result = hxcommon_channel_write(chan, line);        
        g_free(line);

        g_return_if_fail(result);
        
        i++;
        recent_iter = g_list_next(recent_iter);        
    }

    /* Step 4: Write out favorites */
    GList* favorites_iter;

    hxcommon_channel_write(chan, "\n[favorites]\n");
    i = 0;
    favorites_iter = g_hx_main_app->favorites_list;
    while(favorites_iter)
    {
        HXFavorite* favorite = (HXFavorite*)favorites_iter->data;

        line = g_strdup_printf("favorite_title%d=%s\n", i, favorite->title);
        result = hxcommon_channel_write(chan, line);        
        g_free(line);

        line = g_strdup_printf("favorite_url%d=%s\n", i, favorite->url);
        result = hxcommon_channel_write(chan, line);        
        g_free(line);

        g_return_if_fail(result);

        i++;
        favorites_iter = g_list_next(favorites_iter);
    }    

#ifdef HELIX_FEATURE_RP_SIGN_IN

    /* Step 5: Write out sign-in preferences */
    GList* iter;
    hxcommon_channel_write(chan, "\n[signin]\n");
    iter = g_hx_main_app->sign_in_preferences_list;
    while(iter)
    {
        HXSignInPref* pref = (HXSignInPref*)iter->data;

        line = g_strdup_printf("%s=%s\n", pref->key, pref->value);
        result = hxcommon_channel_write(chan, line);        
        g_free(line);

        g_return_if_fail(result);

        iter = g_list_next(iter);
    }    
#endif // HELIX_FEATURE_RP_SIGN_IN
}

static gboolean
hide_cursor(gpointer data)
{
    char invisible_cursor_bits[] = { 0x0 };
    GdkCursor* cursor;
    HXMainWindow* window;
    GdkBitmap *empty_bitmap;
    GdkColor color = { 0, 0, 0, 0 };
    
    window = (HXMainWindow*) data;
    
    empty_bitmap = gdk_bitmap_create_from_data (window->window->window,
                                                invisible_cursor_bits,
                                                1, 1);    

    cursor = gdk_cursor_new_from_pixmap (empty_bitmap,
                                         empty_bitmap,
                                         &color, &color, 0, 0);

    gdk_window_set_cursor(window->window->window, cursor);
    gdk_window_set_cursor(window->hxbin->window, cursor);

    gdk_cursor_unref(cursor);
    g_object_unref(empty_bitmap);

    window->fullscreen_hide_cursor_timer_enabled = FALSE;

    return FALSE; // remove this timer callback
}

static void
show_cursor(HXMainWindow*  window)
{
    /* Show the cursor and reset the hide cursor timeout */
    gdk_window_set_cursor(window->window->window, NULL);
    gdk_window_set_cursor(window->hxbin->window, NULL);

    if(window->fullscreen_hide_cursor_timer_enabled)
    {
        gtk_timeout_remove(window->fullscreen_hide_cursor_timer_id);
        window->fullscreen_hide_cursor_timer_enabled = FALSE;
    }

}

static gboolean
handle_motion_in_fullscreened_window(GtkWidget* widget,
                                     GdkEventMotion* /* event */,
                                     HXMainWindow*  window)
{
    show_cursor(window);
    
    /* Re-add hide cursor timeout */
    window->fullscreen_hide_cursor_timer_enabled = TRUE;
    window->fullscreen_hide_cursor_timer_id =
        gtk_timeout_add(HIDE_CURSOR_TIMEOUT * 1000,
                        hide_cursor,
                        window);
        
    return FALSE; // Propagate
}

static void
hxwindow_on_top(HXMainWindow* window, gboolean on_top)
{
    gboolean used_net_wm = FALSE;

    winutils_window_on_top(GTK_WINDOW(window->window), on_top, &used_net_wm);

    if(!used_net_wm)
    {
        /* XXXRGG: tbd - will we get a _WIN_LAYER property back? */
    }
}

void
hxwindow_set_zoom(HXMainWindow* window, HXMainWindowZoomSize zoom)
{
    if(window->zoom != zoom)
    {
        if(window->is_fullscreened &&
           (zoom == NORMAL_SIZE ||
            zoom == DOUBLE_SIZE))
        {
            window->zoom_on_unfullscreen = zoom;
            hxwindow_unfullscreen(window);
        }
        else
        {
            window->zoom = zoom;
            hxwindow_change_layout(window);

            switch(window->zoom)
            {
                case NORMAL_SIZE:
                    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(window->normal_size_menu_item), TRUE);
                    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(window->double_size_menu_item), FALSE);
                    break;

                case DOUBLE_SIZE:
                    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(window->normal_size_menu_item), FALSE);
                    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(window->double_size_menu_item), TRUE);
                    break;

                case CUSTOM_SIZE:
                default:
                    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(window->normal_size_menu_item), FALSE);
                    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(window->double_size_menu_item), FALSE);
                    break;
            }
        }
    }
}

static void
hxwindow_update_fullscreen(HXMainWindow* window)
{
    /* Update full screen checked menuitem, if necessary */
    GtkWidget* fullscreen_menu_item;
    fullscreen_menu_item = glade_xml_get_widget(window->xml, "hmw_fullscreen_menu_item");
    g_assert(fullscreen_menu_item != NULL);
    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(fullscreen_menu_item),
                                   window->is_fullscreened);        
        
    if(window->is_fullscreened)
    {
        window->show_controls_on_unfullscreen = GTK_WIDGET_VISIBLE(window->controls_hbox);
        window->show_menu_and_caption_on_unfullscreen = GTK_WIDGET_VISIBLE(window->menu_bar);
        window->show_status_bar_on_unfullscreen = GTK_WIDGET_VISIBLE(window->status_bar);

        /* Hide the controls */
        gtk_widget_hide(GTK_WIDGET(window->menu_bar));
        gtk_widget_hide(GTK_WIDGET(window->controls_hbox));
        gtk_widget_hide(GTK_WIDGET(window->seek_scale));
        gtk_widget_hide(GTK_WIDGET(window->status_bar));

        window->zoom_on_unfullscreen = window->zoom;
        window->custom_zoom_height_on_unfullscreen = window->custom_zoom_height;
        window->custom_zoom_width_on_unfullscreen = window->custom_zoom_width;
        window->on_top_on_unfullscreen = window->on_top;
        window->on_top = ON_TOP_ALWAYS;
        winutils_screensaver_disable();	
        hxwindow_on_top(window, TRUE);
                
        g_assert(window->fullscreen_hide_cursor_timer_enabled == FALSE);
        window->fullscreen_hide_cursor_timer_enabled = TRUE;
        window->fullscreen_hide_cursor_timer_id =
            gtk_timeout_add(HIDE_CURSOR_TIMEOUT * 1000,
                            hide_cursor,
                            window);

        window->player_fullscreen_motion_notify_handler =
            g_signal_connect(G_OBJECT(window->window),
                             "motion-notify-event",
                             G_CALLBACK(handle_motion_in_fullscreened_window),
                             window);
        
        window->hxbin_fullscreen_motion_notify_handler =
            g_signal_connect(G_OBJECT(window->hxbin),
                             "motion-notify-event",
                             G_CALLBACK(handle_motion_in_fullscreened_window),
                             window);

        window->fullscreen_motion_notify_enabled = TRUE;
    }
    else /* Unfullscreened */
    {
        /* re-enable the screensaver */
        winutils_screensaver_enable();

        /* come out of full screen */
        switch(window->on_top_on_unfullscreen)
        {
            case ON_TOP_WHILE_PLAYING:
                hxwindow_on_top_while_playing(window);
                break;

            case ON_TOP_ALWAYS:
                hxwindow_on_top_always(window);
                break;

            case ON_TOP_NEVER:
            default:
                hxwindow_on_top_never(window);
                break;
        }

        /* Disable the hide-the-cursor-on-fullscreen logic */
        if(window->fullscreen_motion_notify_enabled)
        {
            g_signal_handler_disconnect(G_OBJECT(window->window),
                                        window->player_fullscreen_motion_notify_handler);
            
            g_signal_handler_disconnect(G_OBJECT(window->hxbin),
                                        window->hxbin_fullscreen_motion_notify_handler);
            
            window->fullscreen_motion_notify_enabled = FALSE;
        }

        show_cursor(window);

        /* If the window manager is fd.o compliant, our decorations will
           be preserved. The decorations showing/hiding code is only of
           interest for legacy wm's where winutils_window_on_top hides
           decorations. */
        if(window->show_menu_and_caption_on_unfullscreen)
        {            
            gtk_window_set_decorated(GTK_WINDOW(window->window), TRUE);
            gtk_widget_show(GTK_WIDGET(window->menu_bar));
        }
        else
        {
            gtk_window_set_decorated(GTK_WINDOW(window->window), FALSE);
        }  

        if(window->show_controls_on_unfullscreen)
        {
            gtk_widget_show(GTK_WIDGET(window->controls_hbox));
            gtk_widget_show(GTK_WIDGET(window->seek_scale));
        }
            
        if(window->show_status_bar_on_unfullscreen)
        {
            gtk_widget_show(GTK_WIDGET(window->status_bar));
        }

        hxwindow_set_zoom(window, window->zoom_on_unfullscreen);
        window->custom_zoom_height = window->custom_zoom_height_on_unfullscreen;
        window->custom_zoom_width = window->custom_zoom_width_on_unfullscreen;
    }
}

void
hxwindow_fullscreen(HXMainWindow* window)
{
    gboolean used_net_wm = FALSE; 

    if(!window->is_fullscreened)
    {
        winutils_window_fullscreen(GTK_WINDOW(window->window), &used_net_wm);

        if(!used_net_wm)
        {
            /* We won't get a window_state reply from the wm. */
            window->is_fullscreened = TRUE;
            hxwindow_update_fullscreen(window);
        }
    }
}

void
hxwindow_unfullscreen(HXMainWindow* window)
{
    gboolean used_net_wm = FALSE;

    if(window->is_fullscreened)
    {
        winutils_window_unfullscreen(GTK_WINDOW(window->window), &used_net_wm);

        if(!used_net_wm)
        {
            /* We won't get a window_state reply from the wm. */
            window->is_fullscreened = FALSE;
            hxwindow_update_fullscreen(window);
        }
    }
}

gint
hxwindow_handle_remote_command(HXMainWindow* window,
                               const gchar* remote_command)
{
    GError* error = NULL;
    gint argc;
    gchar** argv;
    gboolean parse_result;
    gint result = -1;
    
    parse_result = g_shell_parse_argv(remote_command, &argc, &argv, &error);
    if(error)
    {
        g_warning(error->message);
        g_free(error);
        return -1;
    }

    g_return_val_if_fail(parse_result, -1);

    if(argc > 0)
    {
        if(strcasecmp(argv[0], "OpenURL") == 0 && argc == 2)
        {
            hxwindow_open(window, argv[1]);
            result = 0;
        }        
    }

    g_strfreev(argv);

    return result;
}


/* Signal handler implementations (hmw_*, houd_*)
 * Remaining HXMainWindow function implementations (hxwindow_*)
 * ========================================================
 * Often, a hwm_* function will lookup the hxwindow object
 * associated with the window, and call the corresponding
 * hxwindow function.
 *
 * XXXRGG: hmw's and hxwindow's could be combined if we
 * could set the user_data argument with glade in an easy way,
 * and tell glade to connect signals swapped.
 */

/* Creation function for a custom HXBin widget */
GtkWidget*
hmw_create_hxbin(gchar *, gchar *, gchar *, gint, gint)
{
    GtkWidget* hxbin;
    hxbin = hx_bin_new(); 

    gtk_widget_show(hxbin);
    
    return hxbin;
}

GtkWidget*
hmw_create_status_area(gchar *, gchar *, gchar *, gint, gint)
{
    GtkWidget* status_display;
    status_display = hxstatus_display_top_new();

    gtk_widget_show(status_display);
    
    return status_display;
}

GtkWidget*
hmw_create_position_slider(gchar *, gchar *, gchar *, gint, gint)
{
    GtkWidget* slider;
    slider = hxstatus_display_position_slider_new();

    gtk_widget_show(slider);
    
    return slider;
}

void
hmw_realize (GtkWidget* widget, gpointer)
{
    /* Add properties for remote operation */
    GdkAtom hxplay_remote_version;
    GdkAtom xa_string;
            
    hxplay_remote_version  = gdk_atom_intern(HXPLAY_REMOTE_VERSION_PROP, FALSE);
    xa_string              = gdk_x11_xatom_to_atom(XA_STRING);

    gdk_property_change (widget->window,
                         hxplay_remote_version,
                         xa_string,
                         8,
                         GDK_PROP_MODE_REPLACE,
                         (const guchar*) HXPLAY_REMOTE_VERSION,
                         sizeof(HXPLAY_REMOTE_VERSION));
}

gboolean
hmw_configure_event(GtkWidget* widget, GdkEventConfigure *event, gpointer)
{
    HXMainWindow* window = hxwindow_get_from_widget(widget);

    window->x_pos = event->x;
    window->y_pos = event->y;

    return FALSE; // propagate event
}

gboolean
hmw_window_state_event(GtkWidget *widget,
                       GdkEventWindowState *event,
                       gpointer user_data)
{
    /* XXXRGG: This function is driven from hmw_property_notify_event
       instead of gtk-2.0, as gtk-2.0.x does not synthesize fullscreen
       and above events. When our gtk baseline goes up, property_notify
       will go away, and this function will be hooked up the the
       window-state-event signal directly.
    */
    
    HXMainWindow* window = hxwindow_get_from_widget(widget);

    g_return_val_if_fail(event != NULL, FALSE);

    /* Other states of interest:
       GDK_WINDOW_STATE_WITHDRAWN
       GDK_WINDOW_STATE_ICONIFIED
       GDK_WINDOW_STATE_MAXIMIZED
       GDK_WINDOW_STATE_STICKY
       GDK_WINDOW_STATE_BELOW
    */

    if(event->changed_mask & GDK_WINDOW_STATE_FULLSCREEN)
    {    
        if(event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN)
        {
            window->is_fullscreened = TRUE;
        }
        else
        {
            window->is_fullscreened = FALSE;        
        }

        hxwindow_update_fullscreen(window);
    }

    if(event->changed_mask & GDK_WINDOW_STATE_ABOVE)
    {

        /* Update the menu */
        if(window->on_top_always_menu_item &&
           window->on_top_while_playing_menu_item &&
           window->on_top_always_menu_item)
        {            
            gboolean is_on_top = event->new_window_state & GDK_WINDOW_STATE_ABOVE;

            switch(window->on_top)
            {
            case ON_TOP_WHILE_PLAYING:
                /* If we were expecting an on top event, great. If we weren't,
                   the user is changing on top, probably via the window manager.
                   If this is the case, revert to always/never on top. */

                if(window->on_top_while_playing_request_pending)
                {
                    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(window->on_top_while_playing_menu_item),
                                                   TRUE);
                    
                    window->on_top_while_playing_request_pending = FALSE;
                    break;
                }

                /* Not our request -- fall through and flip to either
                   always on top or never on top as appropriate. */

            case ON_TOP_ALWAYS:
            case ON_TOP_NEVER:
            default:
            
                if(is_on_top)
                {
                    window->on_top = ON_TOP_ALWAYS;
                    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(window->on_top_always_menu_item),
                                                   TRUE);        
                }
                else
                {
                    window->on_top = ON_TOP_NEVER;
                    gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(window->on_top_never_menu_item),
                                                   TRUE);        
                }
                break;                
            }
        }
    }
    
    return FALSE;
}


gboolean
hmw_property_notify_event(GtkWidget *widget,
                          GdkEventProperty *event,
                          gpointer)
{
    /* XXXRGG: The _NET_WM_STATE portion of this function should be
       removed when our gtk baseline goes up. */
    GdkAtom net_wm_state;
    GdkAtom hxplay_remote_command;
    GdkAtom hxplay_remote_response;
    GdkAtom xa_string;

    GdkAtom type;
    gint format;
    gint length;
    GdkAtom* atoms = NULL;
    gint i;
    gboolean result;

    static gboolean last_is_above = FALSE;
    static gboolean last_is_fullscreen = FALSE;
    
    net_wm_state = gdk_atom_intern("_NET_WM_STATE", FALSE);
    hxplay_remote_command  = gdk_atom_intern(HXPLAY_REMOTE_COMMAND_PROP,  FALSE);
    hxplay_remote_response = gdk_atom_intern(HXPLAY_REMOTE_RESPONSE_PROP, FALSE);            
    xa_string              = gdk_x11_xatom_to_atom(XA_STRING);
    
    if(event->atom == net_wm_state)
    {
        GdkAtom fullscreen_atom, above_atom;
        gboolean is_above = FALSE;
        gboolean is_fullscreen = FALSE;
        
        fullscreen_atom = gdk_atom_intern("_NET_WM_STATE_FULLSCREEN", FALSE);
        above_atom = gdk_atom_intern("_NET_WM_STATE_ABOVE", FALSE);

        result = gdk_property_get(widget->window,
                                  net_wm_state,
                                  0,
                                  0,
                                  G_MAXLONG - 3,
                                  FALSE,
                                  &type,
                                  &format,
                                  &length,
                                  (guchar**)&atoms);

        if(result)
        {
            for(i = 0; i < length; i++)
            {
                if(atoms[i] == fullscreen_atom)
                {
                    is_fullscreen = TRUE;
                }
                else if(atoms[i] == above_atom)
                {
                    is_above = TRUE;
                }
            }
            g_free(atoms);
        
            GdkEventWindowState temp_event;
            temp_event.window = widget->window;
            temp_event.type = GDK_WINDOW_STATE;
            temp_event.send_event = FALSE;
            temp_event.new_window_state = (GdkWindowState)0;
            temp_event.changed_mask = (GdkWindowState)0;

            if(is_fullscreen)
            {
                temp_event.new_window_state = (GdkWindowState)
                    (temp_event.new_window_state |
                     GDK_WINDOW_STATE_FULLSCREEN);
            }

            if(is_above)
            {
                temp_event.new_window_state = (GdkWindowState)
                    (temp_event.new_window_state |
                     GDK_WINDOW_STATE_ABOVE);
            }

            if(is_fullscreen != last_is_fullscreen)
            {
                temp_event.changed_mask = (GdkWindowState)
                    (temp_event.changed_mask |
                     GDK_WINDOW_STATE_FULLSCREEN);
            }

            if(is_above != last_is_above)
            {
                temp_event.changed_mask = (GdkWindowState)
                    (temp_event.changed_mask |
                     GDK_WINDOW_STATE_ABOVE);
            }
        
            hmw_window_state_event(widget, &temp_event, NULL);

            last_is_above = is_above;
            last_is_fullscreen = is_fullscreen;
        }
    }
    else if(event->atom == hxplay_remote_command &&
            event->state == GDK_PROPERTY_NEW_VALUE)
    {
        gchar* remote_command = NULL;
        gint response = -1;
        gchar* remote_response = NULL;
        
        /* Process the command */        
        result = gdk_property_get(widget->window,
                                  hxplay_remote_command,
                                  xa_string,
                                  0,
                                  G_MAXLONG - 3,
                                  TRUE,
                                  &type,
                                  &format,
                                  &length,
                                  (guchar**)&remote_command);
        
        if(result && 
           format == 8 &&
           length > 0 &&
           type == xa_string &&
           remote_command)
        {
            HXMainWindow* window = hxwindow_get_from_widget(widget);

            response = hxwindow_handle_remote_command(window, remote_command);
        }

        remote_response = g_strdup_printf("%d", response);

        /* Set the response property */
        gdk_property_change (widget->window,
                             hxplay_remote_response,
                             xa_string,
                             8,
                             GDK_PROP_MODE_REPLACE,
                             (const guchar*) remote_response,
                             strlen(remote_response) + 1);

        gdk_flush();
        
        g_free(remote_command);            
        g_free(remote_response);            
    }

    return FALSE; // propagate
}


/* This function gets a HXMainWindow object associated with a given
   window, given a widget somewhere in that window. We use this
   instead of setting the data on gtk signals to make signal hookup
   using glade_xml_signal_autoconnect work easily. */
HXMainWindow*
hxwindow_get_from_widget(GtkWidget* widget)
{
    GtkWidget* toplevel;
    HXMainWindow* window;
           
    toplevel = hxcommon_get_toplevel_widget_no_menus(widget);
    window = (HXMainWindow*) g_object_get_data(G_OBJECT(toplevel), "hxwindow");
    g_assert(window != NULL);

    return window;
}

/* Main menu callbacks & supporting functions */

/* Helper function to feed data from stdin to a datastream */
static gboolean
write_stream_data(GIOChannel *channel,
                  GIOCondition condition,
                  gpointer data)
{
    gchar buf[1024];
    gsize bytes_read;
    GError* error;
    
    GIOStatus status;
    HXPlayer* window = HX_PLAYER(data);
    HXDataStream* stream = (HXDataStream*)g_object_get_data(G_OBJECT(window), "stream");

    g_return_val_if_fail(window != NULL, TRUE);
    g_return_val_if_fail(stream != NULL, TRUE);    
    
    if(condition == G_IO_IN)
    {
        do
        {
            error = NULL;
            status = g_io_channel_read_chars(channel,
                                             buf,
                                             sizeof(buf),
                                             &bytes_read,
                                             &error);
            if(error)
            {
                g_log(G_LOG_DOMAIN,
                      G_LOG_LEVEL_WARNING,
                      "g_io_channel_read_chars: %s",
                      error->message);
                
                g_free(error);
            }
            
            switch(status)
            {
                case G_IO_STATUS_ERROR:                
                case G_IO_STATUS_EOF:
                    hx_player_close_data_stream(window, stream);

                    /* Remove input source */
                    return FALSE;
                    
                case G_IO_STATUS_AGAIN:
                    continue;
                case G_IO_STATUS_NORMAL:
                    hx_player_write_data_stream(window, stream, buf, bytes_read);
                    break;
                default:
                    g_assert(FALSE); /* not reached */
            }
        } while(status == G_IO_STATUS_NORMAL);
    }

    return TRUE; // don't remove the event source
}


void
hxwindow_open(HXMainWindow* window, const gchar* uri)
{
    gboolean result;

    if(window->zoom == CUSTOM_SIZE && !window->is_fullscreened)
    {
        /* This will get applied in hpw_ideal_size_changed */
        window->zoom = NORMAL_SIZE;
    }

    if(strcmp(uri, "mem://") == 0)
    {
        HXDataStream* stream = NULL;
        GIOChannel* channel = NULL;
        GError* error = NULL;

#ifdef G_OS_UNIX
        channel = g_io_channel_unix_new(fileno(stdin));
#endif
        if(channel)
        {
            g_io_channel_set_encoding(channel, NULL, &error);
            g_io_channel_set_buffered(channel, FALSE);

            // FIXME: This should use the recognizer to determine content type
            stream = hx_player_open_data_stream(HX_PLAYER(window->player),
                                                "mem://",
                                                "application/x-pn-realmedia",
                                                0,
                                                TRUE);
                
            g_object_set_data(G_OBJECT(window->player), "stream", stream);
                
            g_io_add_watch(channel,
                           G_IO_IN,
                           write_stream_data,
                           window->player);
        }
            
        result = (stream && channel);
    }
    else
    {    
        result = hx_player_open_url(HX_PLAYER(window->player), uri);
    }

    if(result)
    {                        
        hx_player_play(HX_PLAYER(window->player));
    }		    
}

void
hmw_open_location(GtkWidget* widget)
{
    HXMainWindow* window = hxwindow_get_from_widget(widget);
    hxwindow_open_location(window);
}

static void
open_dialog_response(GtkDialog* dialog,
                     gint response_id,
                     HXMainWindow* window)
{
    const gchar* str;
    gchar* location = NULL;

    g_return_if_fail(window != NULL && dialog != NULL);
    
    if(response_id == GTK_RESPONSE_OK || response_id == GTK_RESPONSE_ACCEPT)
    {
        str = hxplay_open_dialog_get_location(dialog);
        if(str)
        {
            location = g_strdup(str);
        }
    }

    if(dialog == window->open_location_dialog)
    {
        window->open_location_dialog = NULL;
    }

    if(dialog == window->open_file_dialog)
    {
        window->open_location_dialog = NULL;
    }
    
    if(location)
    {
        hxwindow_open(window, location);
        g_free(location);
    }
}

void
hxwindow_open_location(HXMainWindow* window)
{
    if(!window->open_location_dialog)
    {
        window->open_location_dialog = hxplay_open_location_dialog_new (window->recent_urls_list);
        if(window->open_location_dialog)
        {
            gtk_window_set_transient_for(GTK_WINDOW(window->open_location_dialog),
                                         GTK_WINDOW(window->window));

            g_signal_connect (G_OBJECT (window->open_location_dialog),
                              "response",
                              G_CALLBACK (open_dialog_response),
                              window);

            g_signal_connect_after (G_OBJECT(window->open_location_dialog),
                                    "response",
                                    G_CALLBACK(hxcommon_close_dialog),
                                    &window->open_location_dialog);

        }
    }

    if(window->open_location_dialog)
    {
        gtk_widget_show_all(GTK_WIDGET(window->open_location_dialog));
    }
}

void
hmw_open_file(GtkWidget* widget)
{
    HXMainWindow* window = hxwindow_get_from_widget(widget);
    hxwindow_open_file(window);
}

static void
validate_filename_and_close(GtkWidget* dialog,
                            gint response,
                            gpointer* data_ptr)    
{
    const gchar* filename;

    if(response == GTK_RESPONSE_OK || response == GTK_RESPONSE_ACCEPT)
    {
#if GTK_CHECK_VERSION(2, 4, 0)
        filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
#else
        filename = gtk_file_selection_get_filename(GTK_FILE_SELECTION(dialog));
#endif

        if(g_file_test(filename, G_FILE_TEST_EXISTS) &&
           !g_file_test(filename, G_FILE_TEST_IS_DIR))
        {
            hxcommon_close_dialog(GTK_WIDGET(dialog), response, data_ptr);
        }
    }
    else
    {
        /* Response was not ok */
        hxcommon_close_dialog(GTK_WIDGET(dialog), response, data_ptr);
    }
}

void
hxwindow_open_file(HXMainWindow* window)
{
    if(!window->open_file_dialog)
    {
        window->open_file_dialog = hxplay_open_file_dialog_new (window);
        if(window->open_file_dialog)
        {
            gtk_window_set_transient_for(GTK_WINDOW(window->open_file_dialog),
                                         GTK_WINDOW(window->window));
            
            g_signal_connect (G_OBJECT (window->open_file_dialog),
                              "response",
                              G_CALLBACK (open_dialog_response),
                              window);

            g_signal_connect_after (G_OBJECT(window->open_file_dialog),
                                    "response",
                                    G_CALLBACK(validate_filename_and_close),
                                    &window->open_file_dialog);
        }
    }

    if(window->open_file_dialog)
    {
        gtk_widget_show_all(GTK_WIDGET(window->open_file_dialog));
    }
}

void
hmw_quit(GtkWidget*)
{
    gtk_main_quit();
}

void
hmw_close(GtkWidget* widget)
{
    HXMainWindow* window = hxwindow_get_from_widget(widget);
    gtk_widget_destroy(window->window);
}

void
hmw_play(GtkWidget* widget)
{
    HXMainWindow* window = hxwindow_get_from_widget(widget);
    hxwindow_play(window);
}

void
hxwindow_play(HXMainWindow* window)
{
    hx_player_play(HX_PLAYER(window->player));    
}

void
hmw_pause(GtkWidget* widget)
{
    HXMainWindow* window = hxwindow_get_from_widget(widget);
    hxwindow_pause(window);
}

void
hxwindow_pause(HXMainWindow* window)
{
    hx_player_pause(HX_PLAYER(window->player));
}

void
hmw_play_pause(GtkWidget* widget)
{
    HXMainWindow* window = hxwindow_get_from_widget(widget);
    hxwindow_play_pause(window);
}

void
hxwindow_play_pause(HXMainWindow* window)
{
    HXContentStateType state;
    state = hx_player_get_content_state(HX_PLAYER(window->player));

    if(state == HX_CONTENT_STATE_PLAYING)
    {
        hxwindow_pause(window);
    }
    else
    {
        hxwindow_play(window);
    }
}

void
hmw_stop(GtkWidget* widget)
{
    HXMainWindow* window = hxwindow_get_from_widget(widget);
    hxwindow_stop(window);
}

void
hxwindow_stop(HXMainWindow* window)
{
    hx_player_stop(HX_PLAYER(window->player));
}

void
hmw_previous(GtkWidget* widget)
{
    HXMainWindow* window = hxwindow_get_from_widget(widget);
    hxwindow_previous(window);
}

void
hxwindow_previous(HXMainWindow* window)
{
    guint group;

    group = hx_player_get_current_group(HX_PLAYER(window->player));
    
    if(group > 0)
    {
	group--;
	hx_player_set_current_group(HX_PLAYER(window->player),
				       group);
    }
}

void
hmw_next(GtkWidget* widget)
{
    HXMainWindow* window = hxwindow_get_from_widget(widget);
    hxwindow_next(window);
}

void
hxwindow_next(HXMainWindow* window)
{
    guint group, max_group;

    max_group = hx_player_get_group_count(HX_PLAYER(window->player));
    group = hx_player_get_current_group(HX_PLAYER(window->player));
    group++;

    if(group < max_group)
    {
	hx_player_set_current_group(HX_PLAYER(window->player),
				       group);
    }
}

void
hxwindow_fast_forward(HXMainWindow* window)
{
    guint length;
    guint adjustment;
    guint position;
    HXPlayer* player;

    player = HX_PLAYER(window->player);

    if(hx_player_is_live(player))
    {
        return;
    }

    length = hx_player_get_length(player);
    position = hx_player_get_position(player);
    adjustment = length / 100;

    position += adjustment;
    if(position > length)
    {
        position = length;
    }

    hx_player_start_seeking(player);
    hx_player_set_position(player, position);
    hx_player_stop_seeking(player);
}

void
hxwindow_rewind(HXMainWindow* window)
{
    guint length;
    guint adjustment;
    guint position;
    HXPlayer* player;

    player = HX_PLAYER(window->player);

    if(hx_player_is_live(player))
    {
        return;
    }
    
    length = hx_player_get_length(player);
    position = hx_player_get_position(player);
    adjustment = length / 100;

    if(position < adjustment)
    {
        position = 0;
    }
    else
    {
        position -= adjustment;
    }

    hx_player_start_seeking(player);
    hx_player_set_position(player, position);
    hx_player_stop_seeking(player);       
}

void
hmw_volume_changed(GtkWidget* widget)
{
    guint vol, new_vol;
    HXMainWindow* window = hxwindow_get_from_widget(widget);

    new_vol = (guint)gtk_range_get_value(GTK_RANGE(window->volume_scale));
    vol = hx_player_get_volume(HX_PLAYER(window->player));
    if(vol != new_vol)
    {
        hx_player_set_volume(HX_PLAYER(window->player), (guint)new_vol);
    }
}

void
hxwindow_volume_change(HXMainWindow* window,
                       gint         delta)
{
    gint vol;
    vol = (gint)hx_player_get_volume(HX_PLAYER(window->player));

    vol += delta;

    if(vol < 0)
    {
        vol = 0;        
    }
    else if(vol > 100)
    {
        vol = 100;
    }
    
    hx_player_set_volume(HX_PLAYER(window->player), (guint)vol);    
}


void
hmw_volume_up(GtkWidget* widget)
{
    HXMainWindow* window = hxwindow_get_from_widget(widget);
    hxwindow_volume_change(window, +10);
}


void
hmw_volume_down(GtkWidget* widget)
{    
    HXMainWindow* window = hxwindow_get_from_widget(widget);
    hxwindow_volume_change(window, -10);
}

void
hmw_mute(GtkWidget* widget)
{
    gboolean mute;
    gboolean new_mute = TRUE;
    HXMainWindow* window = hxwindow_get_from_widget(widget);

    if(GTK_IS_CHECK_MENU_ITEM(widget))
    {
        new_mute = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
    }
    else if(GTK_IS_TOGGLE_BUTTON(widget))
    {
        new_mute = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
    }

    mute = hx_player_is_muted(HX_PLAYER(window->player));

    if(mute != new_mute)
    {
        /* Toggle mute */
        hx_player_set_mute(HX_PLAYER(window->player), new_mute);

        /* ui is updated in hpw_mute_changed */
    }        
}

void
hmw_drag_data_received(GtkWidget*        widget,
                       GdkDragContext*   /* context */,
                       gint              /* x */,
                       gint              /* y */,
                       GtkSelectionData* selection_data,
                       guint             /* info */,
                       guint             /* time */)
{
    HXMainWindow* window = hxwindow_get_from_widget(widget);
    
    g_return_if_fail(selection_data != NULL);
    g_return_if_fail(selection_data->length >= 0);
    g_return_if_fail(window != NULL);
    g_return_if_fail(window->player != NULL);

    /* XXXRGG: we need to parse this into a playlist if
       multiple files are being dropped */
    hx_player_open_url(HX_PLAYER(window->player), (gchar*)selection_data->data);
    hx_player_play(HX_PLAYER(window->player));
}

void
hmw_fullscreen(GtkWidget* widget)
{
    gboolean fullscreen = FALSE;
    HXMainWindow* window = hxwindow_get_from_widget(widget);

    if(GTK_IS_CHECK_MENU_ITEM(widget))
    {
        fullscreen = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
    }

    if(fullscreen != window->is_fullscreened)
    {
        /* window->is_fullscreened and the checkbox will be updated
           in hmw_window_state_event. Here, initiate a
           fullscreen/unfullscreen as appropriate. */

        if(fullscreen)
        {
            gtk_widget_grab_focus(window->player);
            hxwindow_fullscreen (window);	    
        }
        else
        {
            hxwindow_unfullscreen (window);
        }
    }
}


void
hmw_normal_size(GtkWidget* widget)
{
    gboolean active = TRUE;
    gboolean is_normal_size;
    
    if(GTK_IS_CHECK_MENU_ITEM(widget))
    {
        active = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
    }

    HXMainWindow* window = hxwindow_get_from_widget(widget);        
    is_normal_size = (window->zoom == NORMAL_SIZE);
    
    if(active && !is_normal_size)
    {
        hxwindow_set_zoom(window, NORMAL_SIZE);
    }
}

void
hmw_double_size(GtkWidget* widget)
{
    gboolean active = TRUE;
    gboolean is_double_size;
    
    if(GTK_IS_CHECK_MENU_ITEM(widget))
    {
        active = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
    }

    HXMainWindow* window = hxwindow_get_from_widget(widget);
    is_double_size = (window->zoom == DOUBLE_SIZE);
    
    if(active && !is_double_size)
    {
        hxwindow_set_zoom(window, DOUBLE_SIZE);
    }        
}

void
hmw_manage_favorites(GtkWidget* widget)
{
    HXMainWindow* window = hxwindow_get_from_widget(widget);

    if(!window->manage_favorites_dialog)
    {
        window->manage_favorites_dialog = hxplay_manage_favorites_dialog_new (&g_hx_main_app->favorites_list);
        if(window->manage_favorites_dialog)
        {
            gtk_window_set_transient_for(GTK_WINDOW(window->manage_favorites_dialog),
                                         GTK_WINDOW(window->window));

            g_signal_connect_swapped (G_OBJECT(window->manage_favorites_dialog),
                                      "response",
                                      G_CALLBACK(favorites_menu_refresh),
                                      window);

            g_signal_connect_swapped (G_OBJECT(window->manage_favorites_dialog),
                                      "response",
                                      G_CALLBACK(hxcommon_save_preferences),
                                      window);

            g_signal_connect_after (G_OBJECT(window->manage_favorites_dialog),
                                    "response",
                                    G_CALLBACK(hxcommon_close_dialog),
                                    &window->manage_favorites_dialog);
        }
    }

    if(window->manage_favorites_dialog)
    {
        gtk_widget_show_all(GTK_WIDGET(window->manage_favorites_dialog));
    }
}

void
hmw_add_favorite(GtkWidget* widget)
{
    const gchar* title;
    const gchar* url;
    gchar* str = NULL;
    
    HXMainWindow* window = hxwindow_get_from_widget(widget);

    title = hx_player_get_title(HX_PLAYER(window->player));
    url = hx_player_get_url(HX_PLAYER(window->player));

    g_return_if_fail(url != NULL);
    
    if(!title || !*title)
    {
        str = hxcommon_get_title_from_url(url);
        title = str;
    }
    
    hxwindow_favorite_add(window, title, url);
    
    hxcommon_save_preferences(window);

    g_free(str);
}

void
hmw_export_favorites(GtkWidget* widget)
{ 
    HXMainWindow* window = hxwindow_get_from_widget(widget);

    if(!window->export_favorites_dialog)
    {
        window->export_favorites_dialog = hxplay_export_favorites_dialog_new (g_hx_main_app->favorites_list);
        if(window->export_favorites_dialog)
        {
            gtk_window_set_transient_for(GTK_WINDOW(window->export_favorites_dialog),
                                         GTK_WINDOW(window->window));

            g_signal_connect_after (G_OBJECT(window->export_favorites_dialog),
                                    "response",
                                    G_CALLBACK(hxcommon_close_dialog),
                                    &window->export_favorites_dialog);
        }
    }

    if(window->export_favorites_dialog)
    {
        gtk_widget_show_all(GTK_WIDGET(window->export_favorites_dialog));
    }
}

void
hmw_import_favorites(GtkWidget* widget)
{
    HXMainWindow* window = hxwindow_get_from_widget(widget);

    if(!window->import_favorites_dialog)
    {
        window->import_favorites_dialog = hxplay_import_favorites_dialog_new (&g_hx_main_app->favorites_list);
                                                                              
        if(window->import_favorites_dialog)
        {
            gtk_window_set_transient_for(GTK_WINDOW(window->import_favorites_dialog),
                                         GTK_WINDOW(window->window));
            
            g_signal_connect_swapped (G_OBJECT(window->import_favorites_dialog),
                                      "response",
                                      G_CALLBACK(favorites_menu_refresh),
                                      window);
            
            g_signal_connect_swapped (G_OBJECT(window->preferences_dialog),
                                      "response",
                                      G_CALLBACK(hxcommon_save_preferences),
                                      window);

            g_signal_connect_after (G_OBJECT(window->import_favorites_dialog),
                                    "response",
                                    G_CALLBACK(hxcommon_close_dialog),
                                    &window->import_favorites_dialog);
        }
    }

    if(window->import_favorites_dialog)
    {
        gtk_widget_show_all(GTK_WIDGET(window->import_favorites_dialog));
    }
}

void
hmw_clip_details(GtkWidget* widget)
{
    HXMainWindow* window = hxwindow_get_from_widget(widget);

    if(!window->clip_details_dialog)
    {
        window->clip_details_dialog = hxplay_clip_details_dialog_new (HX_PLAYER(window->player));
        if(window->clip_details_dialog)
        {
            gtk_window_set_transient_for(GTK_WINDOW(window->clip_details_dialog),
                                         GTK_WINDOW(window->window));
            
            g_signal_connect_after (G_OBJECT(window->clip_details_dialog),
                                    "response",
                                    G_CALLBACK(hxcommon_close_dialog),
                                    &window->clip_details_dialog);
        }
    }

    if(window->clip_details_dialog)
    {
        gtk_widget_show_all(GTK_WIDGET(window->clip_details_dialog));
    }
}

void
hmw_statistics(GtkWidget* widget)
{
    HXMainWindow* window = hxwindow_get_from_widget(widget);

    if(!window->statistics_dialog)
    {
        window->statistics_dialog = hxplay_statistics_dialog_new (HX_PLAYER(window->player));
        if(window->statistics_dialog)
        {
            gtk_window_set_transient_for(GTK_WINDOW(window->statistics_dialog),
                                         GTK_WINDOW(window->window));

            g_signal_connect_after (G_OBJECT(window->statistics_dialog),
                                    "response",
                                    G_CALLBACK(hxcommon_close_dialog),
                                    &window->statistics_dialog);
        }
    }

    if(window->statistics_dialog)
    {
        gtk_widget_show_all(GTK_WIDGET(window->statistics_dialog));
    }
}

void     
hmw_video_controls(GtkWidget* widget)
{
#ifdef HELIX_FEATURE_HARDWARE_COLOR_CONTROLS
    HXMainWindow* window = hxwindow_get_from_widget(widget);

    if(!window->video_controls_dialog)
    {
        window->video_controls_dialog = hxplay_video_controls_new(HX_PLAYER(window->player));
        if(window->video_controls_dialog)
        {
            gtk_window_set_transient_for(GTK_WINDOW(window->video_controls_dialog),
                                         GTK_WINDOW(window->window));

            g_signal_connect_after (G_OBJECT(window->video_controls_dialog),
                                    "response",
                                    G_CALLBACK(hxcommon_close_dialog),
                                    &window->video_controls_dialog);
        }
    }

    if(window->video_controls_dialog)
    {
        gtk_widget_show_all(GTK_WIDGET(window->video_controls_dialog));
    }  
#endif  
}


void
hmw_show_menu_and_caption(GtkWidget* widget)
{
    /* Show/hide controls */
    gboolean show = TRUE;
    
    HXMainWindow* window = hxwindow_get_from_widget(widget);
    
    if(GTK_IS_CHECK_MENU_ITEM(widget))
    {
        show = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
    }

    hxwindow_show_menu_and_caption(window, show);
}

void
hxwindow_show_menu_and_caption(HXMainWindow* window, gboolean show)
{
    gboolean current_show = GTK_WIDGET_VISIBLE(window->menu_bar)? TRUE: FALSE;

    if(show != current_show)
    {                
        /* Show/hide the widget */
        if(show)
        {
            gtk_widget_show(window->menu_bar);
            gtk_window_set_decorated(GTK_WINDOW(window->window), TRUE);
        }
        else
        {
            gtk_widget_hide(window->menu_bar);
            gtk_window_set_decorated(GTK_WINDOW(window->window), FALSE);
        }

        hxwindow_change_layout(window);
    }
}

void
hmw_show_controls(GtkWidget* widget)
{
    /* Show/hide controls */
    gboolean show = TRUE;
    
    HXMainWindow* window = hxwindow_get_from_widget(widget);
    
    if(GTK_IS_CHECK_MENU_ITEM(widget))
    {
        show = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
    }

    hxwindow_show_controls(window, show);
}

void
hxwindow_show_controls(HXMainWindow* window, gboolean show)
{
    gboolean current_show = GTK_WIDGET_VISIBLE(window->controls_hbox)? TRUE: FALSE;

    if(show != current_show)
    {
        /* Show/hide controls */
        if(show)
        {
            gtk_widget_show(window->controls_hbox);
            gtk_widget_show(window->seek_scale);
        }
        else
        {
            gtk_widget_hide(window->controls_hbox);
            gtk_widget_hide(window->seek_scale);
        }
            
        hxwindow_change_layout(window);
    }
}

void
hmw_show_status_bar(GtkWidget* widget)
{
    /* Show/hide controls */
    gboolean show = TRUE;
    
    HXMainWindow* window = hxwindow_get_from_widget(widget);
    
    if(GTK_IS_CHECK_MENU_ITEM(widget))
    {
        show = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
    }

    hxwindow_show_status_bar(window, show);
}

void
hxwindow_show_status_bar(HXMainWindow* window, gboolean show)
{
    gboolean current_show = GTK_WIDGET_VISIBLE(window->status_bar)? TRUE: FALSE;
    
    if(show != current_show)
    {
        if(show)
        {
            gtk_widget_show(window->status_bar);
        }
        else
        {
            gtk_widget_hide(window->status_bar);
        }

        hxwindow_change_layout(window);                
    }
}

void
hxwindow_on_top_never(HXMainWindow* window)
{
    hxwindow_on_top(window, FALSE);
}

void
hmw_on_top_never(GtkWidget *widget)
{
    gboolean active = FALSE;
    HXMainWindow* window = hxwindow_get_from_widget(widget);

    if(GTK_IS_CHECK_MENU_ITEM(widget))
    {
        active = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
    }

    if(active && window->on_top != ON_TOP_NEVER)
    {
        hxwindow_on_top_never(window);
    }
}

void
hxwindow_on_top_while_playing(HXMainWindow* window)
{
    HXContentStateType state;

    window->on_top = ON_TOP_WHILE_PLAYING;
    window->on_top_while_playing_request_pending = TRUE;
    state = hx_player_get_content_state(HX_PLAYER(window->player));
    switch(state)
    {
        case HX_CONTENT_STATE_CONTACTING:
        case HX_CONTENT_STATE_BUFFERING:
        case HX_CONTENT_STATE_PLAYING:
        case HX_CONTENT_STATE_PAUSED:
            hxwindow_on_top(window, TRUE);
            break;

        default:
            hxwindow_on_top(window, FALSE);
            break;
    }    
}

void
hmw_on_top_while_playing(GtkWidget *widget)
{
    gboolean active = FALSE;
    HXMainWindow* window = hxwindow_get_from_widget(widget);

    if(GTK_IS_CHECK_MENU_ITEM(widget))
    {
        active = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
    }

    if(active && window->on_top != ON_TOP_WHILE_PLAYING)
    {
        hxwindow_on_top_while_playing(window);
    }
}

void
hxwindow_on_top_always(HXMainWindow* window)
{
    hxwindow_on_top(window, TRUE);
}

void
hmw_on_top_always(GtkWidget *widget)
{
    gboolean active = FALSE;
    HXMainWindow* window = hxwindow_get_from_widget(widget);

    if(GTK_IS_CHECK_MENU_ITEM(widget))
    {
        active = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
    }

    if(active && window->on_top != ON_TOP_ALWAYS)
    {    
        hxwindow_on_top(window, TRUE);
    }
}

void
hmw_intro(GtkWidget* /* widget */)
{
    gchar* url = hx_switchboard_get_context_help_url("Introduction");
    hxcommon_url_show(url);
    g_free(url);
}

void
hmw_release_notes(GtkWidget* /* widget */)
{
    gchar* url = hx_switchboard_get_context_help_url("ReleaseNotes");
    hxcommon_url_show(url);
    g_free(url);
}

void
hmw_faq(GtkWidget* /* widget */)
{
    gchar* url = hx_switchboard_get_context_help_url("FAQ");
    hxcommon_url_show(url);
    g_free(url);
}

void
hmw_report_issue(GtkWidget* /* widget */)
{
    gchar* url = hx_switchboard_get_context_help_url("ReportIssue");
    hxcommon_url_show(url);
    g_free(url);
}

void
hmw_nightly_build(GtkWidget* /* widget */)
{
    gchar* url = hx_switchboard_get_context_help_url("NightlyBuild");
    hxcommon_url_show(url);
    g_free(url);
}

void
hmw_check_for_updates(GtkWidget* /* widget */)
{
    gchar* url = hx_switchboard_get_upgrade_url(NULL);
    hxcommon_url_show(url);
    g_free(url);    
}

void
hmw_about(GtkWidget* widget)
{
    HXMainWindow* window = hxwindow_get_from_widget(widget);

    if(!window->about_dialog)
    {
        window->about_dialog = hxplay_about_dialog_new (HX_PLAYER(window->player),
                                                        window->distcode,
                                                        window->origcode);
        if(window->about_dialog)
        {
            gtk_window_set_transient_for(GTK_WINDOW(window->about_dialog),
                                         GTK_WINDOW(window->window));

            g_signal_connect_after (G_OBJECT(window->about_dialog),
                                    "response",
                                    G_CALLBACK(hxcommon_close_dialog),
                                    &window->about_dialog);
        }
    }

    if(window->about_dialog)
    {
        gtk_widget_show_all(GTK_WIDGET(window->about_dialog));
    }
}

void
hmw_plugins(GtkWidget* widget)
{
    HXMainWindow* window = hxwindow_get_from_widget(widget);

    if(!window->plugins_dialog)
    {
        window->plugins_dialog = hxplay_plugins_dialog_new (HX_PLAYER(window->player));
        if(window->plugins_dialog)
        {
            gtk_window_set_transient_for(GTK_WINDOW(window->plugins_dialog),
                                         GTK_WINDOW(window->window));

            g_signal_connect_after (G_OBJECT(window->plugins_dialog),
                                    "response",
                                    G_CALLBACK(hxcommon_close_dialog),
                                    &window->plugins_dialog);
        }
    }

    if(window->plugins_dialog)
    {
        gtk_widget_show_all(GTK_WIDGET(window->plugins_dialog));
    }
}


void
hmw_view_source(GtkWidget* widget)
{
    /* Use COM access for view source vs hxclientkit
       (keep the widget simple) */
    gboolean result;
    HXMainWindow* window;

    HX_RESULT retVal;
    IUnknown* pUnk = NULL;
    IHXViewSourceCommand* pViewSrcCommand = NULL;
        
    window = hxwindow_get_from_widget(widget);

    result = hx_player_get_unknown(HX_PLAYER(window->player),
                                   (void**)&pUnk);
    g_return_if_fail(result != FALSE);

    retVal = pUnk->QueryInterface(IID_IHXViewSourceCommand,
                                     (void**)&pViewSrcCommand);
    g_return_if_fail(pViewSrcCommand && SUCCEEDED(retVal));

    pViewSrcCommand->DoViewSource(NULL);

    HX_RELEASE(pViewSrcCommand);
}

void
hmw_file_menu_update(GtkWidget* widget)
{
    HXMainWindow* window;
    GtkWidget* view_source_menu_item = NULL;

    window = hxwindow_get_from_widget(widget);

    view_source_menu_item = glade_xml_get_widget(window->xml, "hmw_view_source_menu_item");

    if(view_source_menu_item)
    {
        /* See if we should show the view source menu item */
        HX_RESULT retVal;
        IUnknown* pUnk = NULL;
        IHXViewSourceCommand* pViewSrcCommand = NULL;
        gboolean show_view_source = FALSE;
        gboolean result;
        
        result = hx_player_get_unknown(HX_PLAYER(window->player),
                                       (void**)&pUnk);
        g_return_if_fail(result != FALSE);

        retVal = pUnk->QueryInterface(IID_IHXViewSourceCommand,
                                         (void**)&pViewSrcCommand);
        if(pViewSrcCommand && SUCCEEDED(retVal))
        {
            show_view_source = (gboolean)pViewSrcCommand->CanViewSource(NULL);
            HX_RELEASE(pViewSrcCommand);
        }
    
        if(show_view_source)
        {
            /* show the view source menu item */
            gtk_widget_set_sensitive(view_source_menu_item, TRUE);
        }
        else
        {
            /* hide the view source menu item */
            gtk_widget_set_sensitive(view_source_menu_item, FALSE);
        }
    }
}

void
hmw_favorites_menu_update(GtkWidget* widget)
{
    HXMainWindow* window;
    GtkWidget* add_favorite_menu_item = NULL;
    GtkWidget* export_favorites_menu_item = NULL;
    const gchar* url;

    window = hxwindow_get_from_widget(widget);

    add_favorite_menu_item = glade_xml_get_widget(window->xml, "hmw_add_favorite_menu_item");
    g_return_if_fail(add_favorite_menu_item != NULL);
    
    url = hx_player_get_url(HX_PLAYER(window->player));

    gtk_widget_set_sensitive(add_favorite_menu_item, url != NULL);

    export_favorites_menu_item = glade_xml_get_widget(window->xml, "hmw_export_favorites_menu_item");
    g_return_if_fail(export_favorites_menu_item != NULL);

    gtk_widget_set_sensitive(export_favorites_menu_item,
                             g_hx_main_app->favorites_list != NULL);    
}

void
hmw_player_reset(GtkWidget* widget)
{
    HXMainWindow* window = hxwindow_get_from_widget(widget);
    gint result;
    const gchar* message = _(
        "Warning: Player reset will delete all player information, including "
        "all network settings, preferences, favorites, and recent clips\n"
        "\n"
        "Do you wish to continue?"        
        );
    
    /* Show warning */    
    GtkWidget* dialog;

    dialog = gtk_message_dialog_new(GTK_WINDOW(window->window),
                                    GTK_DIALOG_MODAL,
                                    GTK_MESSAGE_WARNING,
                                    GTK_BUTTONS_YES_NO,
                                    "%s",
                                    message);

    gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);

    result = gtk_dialog_run (GTK_DIALOG (dialog));
    gtk_widget_destroy (dialog);

    if(result == GTK_RESPONSE_YES)
    {            
        hxcommon_reset_player();
    }
}


void
hmw_preferences(GtkWidget* widget)
{    
    HXMainWindow* window = hxwindow_get_from_widget(widget);

    if(!window->preferences_dialog)
    {
        window->preferences_dialog = hxplay_preferences_dialog_new (HX_PLAYER(window->player),
                                                                    window);
        if(window->preferences_dialog)
        {
            gtk_window_set_transient_for(GTK_WINDOW(window->preferences_dialog),
                                         GTK_WINDOW(window->window));

            g_signal_connect_swapped (G_OBJECT(window->preferences_dialog),
                                      "response",
                                      G_CALLBACK(hxcommon_save_preferences),
                                      window);
            
            g_signal_connect_after (G_OBJECT(window->preferences_dialog),
                                    "response",
                                    G_CALLBACK(hxcommon_close_dialog),
                                    &window->preferences_dialog);
        }
    }

    if(window->preferences_dialog)
    {
        gtk_widget_show_all(GTK_WIDGET(window->preferences_dialog));
    }
}

gboolean
hmw_key_press_event(GtkWidget *widget,
		    GdkEventKey *event,
		    gpointer)
{
    gboolean handled = FALSE;
    /* Set up some additional "accelerators":

       <SPACE> => Toggle play/pause
       <Left>  => FF
       <Right> => RW
       <Up>    => Volume up
       <Down>  => Volume down
       <F,F11> => Toggle fullscreen
       <Esc>   => Exit fullscreen

       RGG: if we ever make HXMainWindow a gobject, we could
       use GtkAccel* and signals
    */

    HXMainWindow* window = hxwindow_get_from_widget(widget);

    switch (event->keyval)
    {
        case GDK_space:
            hxwindow_play_pause(window);
            handled = TRUE;
            break;

        case GDK_Up:
            hxwindow_volume_change(window, +1);
            handled = TRUE;
            break;

        case GDK_Down:
            hxwindow_volume_change(window, -1);
            handled = TRUE;
            break;

        case GDK_Left:
            hxwindow_rewind(window);
            handled = TRUE;
            break;

        case GDK_Right:
            hxwindow_fast_forward(window);            
            handled = TRUE;
            break;
            
        case GDK_F:
        case GDK_F11:
            if(window->is_fullscreened)
            {
                hxwindow_unfullscreen (window);
            }
            else
            {
                hxwindow_fullscreen (window);
            }
            handled = TRUE;
            break;

	case GDK_Escape:
            if(window->is_fullscreened)
            {
                hxwindow_unfullscreen (window);
            }
            handled = TRUE;
	    break;
            
        default:
            break;
    }

    return handled; // propagate
}

#ifdef HELIX_FEATURE_RP_SIGN_IN
void
hmw_sign_in(GtkWidget* widget)
{
    HXMainWindow* window = hxwindow_get_from_widget(widget);
    hxcommon_run_sign_in_dialog(window->window->window);
}
#endif


gboolean
hmw_seek_start(GtkWidget* widget)
{
    HXMainWindow* window = hxwindow_get_from_widget(widget);

    hx_player_start_seeking(HX_PLAYER(window->player));

    return FALSE; // propagate
}

gboolean
hmw_seek_stop(GtkWidget* widget)
{
    gdouble pos;
    HXMainWindow* window = hxwindow_get_from_widget(widget);

    pos = gtk_range_get_value(GTK_RANGE(window->seek_scale));
    hx_player_set_position(HX_PLAYER(window->player), (guint)pos);
    hx_player_stop_seeking(HX_PLAYER(window->player));

    return FALSE; // propagate
}

void
hmw_window_size_allocate(HXMainWindow* window, GtkAllocation *allocation)
{
    GtkRequisition req;
    
    gtk_widget_size_request(window->window, &req);

    if((allocation->height - 1) > req.height)
    {
        gtk_widget_show(window->hxbin);
    }        
}

/* Note that this handler must be called with swapped args, and HXMainWindow
   as the user data */
void
hmw_hxbin_size_allocate(HXMainWindow* window, GtkAllocation *allocation)
{
    gint ideal_width = 0, ideal_height = 0;
    HXContentStateType state;
    gboolean has_visual_content;
    
    if(allocation->height == 1)
    {
        /* Can't go lower than 1, so hide the widget to make it 0. */
        gtk_widget_hide(window->hxbin);

        window->custom_zoom_width = 0;
        window->custom_zoom_height = 0;
    }
    else
    {        
        /* We'll only change window->zoom if the player is playing or paused,
           and has visual content, as at this point, we've had
           hpw_ideal_size_changed called, and the sizing code is generally
           on its feet, window size is set appropriately, etc. */
        has_visual_content = hx_player_has_visual_content(HX_PLAYER(window->player));        
        state = hx_player_get_content_state(HX_PLAYER(window->player));
        
        if(has_visual_content && (state == HX_CONTENT_STATE_PLAYING ||
                                  state == HX_CONTENT_STATE_PAUSED))
        {
            hx_player_get_ideal_size(HX_PLAYER(window->player), &ideal_width, &ideal_height);

            gboolean is_normal_size = (ideal_width == allocation->width) && (ideal_height == allocation->height);
            gboolean is_double_size = (2 * ideal_width == allocation->width) && (2 * ideal_height == allocation->height);
            
            if(is_normal_size || is_double_size)
            {
                /* It's possible for us to get an allocate from the idle resizer between a call
                   to gtk_window_resize and the window actually being resized. Because of
                   this, we don't call hxwindow_set_zoom here, as this would clobber the outstanding
                   resize request. */
                window->custom_zoom_width = allocation->width;
                window->custom_zoom_height = allocation->height;
            }
            else
            {
                window->custom_zoom_width = allocation->width;
                window->custom_zoom_height = allocation->height;
                hxwindow_set_zoom(window, CUSTOM_SIZE);
            }
        }
    }
}

void
hmw_play_from_menu_item(GtkWidget* widget, gchar* url)
{
    HXMainWindow* window = hxwindow_get_from_widget(widget);

    hxwindow_open(window, url);
}

void
hmw_play_group(GtkWidget* widget, guint group)
{
    HXMainWindow* window = hxwindow_get_from_widget(widget);

    hx_player_set_current_group(HX_PLAYER(window->player), group);
}

/* HXMainWindow widget callbacks
 * =========================
 * designed to be hooked up to a HXMainWindow
 */
void
hpw_play(HXMainWindow* window)
{
    // save the url
    const gchar* url;

    url = hx_player_get_url(HX_PLAYER(window->player));
    if(url)
    {
        hxwindow_recent_urls_prepend(window, url);
        hxcommon_save_preferences(window);
    }

    /* Update the window title */
    gchar* window_title;
    const gchar* title = hx_player_get_title(HX_PLAYER(window->player));

    if(title && *title)
    {
        window_title = g_strdup_printf("%s - %s", APP_NAME_LONG, title);
    }
    else
    {
        const gchar* url = hx_player_get_url(HX_PLAYER(window->player));
        if(url)
        {
            gchar* url_title = hxcommon_get_title_from_url(url);
            window_title = g_strdup_printf("%s - %s", APP_NAME_LONG, url_title);
            g_free(url_title);
        }
        else
        {
            window_title = g_strdup(APP_NAME_LONG);
        }
    }   
    
    gtk_window_set_title(GTK_WINDOW(window->window), window_title);

    g_free(window_title);
}


void
hpw_start_seeking(HXMainWindow* window)
{
    window->is_seeking = TRUE;
}

void
hpw_stop_seeking(HXMainWindow* window)
{
    window->is_seeking = FALSE;    
}

void
hpw_play_buffer_contact(HXMainWindow* window)
{
    /* Set UI for playing, buffering, or contacting */
    if(window->on_top == ON_TOP_WHILE_PLAYING)
    {
        hxwindow_on_top_while_playing(window);
    }

    /* Update buttons */
    if(window->play_button)
    {
        gtk_widget_set_sensitive(window->play_button, FALSE);
    }
    if(window->pause_button)
    {
        gtk_widget_set_sensitive(window->pause_button, TRUE);
    }
    if(window->stop_button)
    {
        gtk_widget_set_sensitive(window->stop_button, TRUE);
    }
    if(window->play_pause_button)
    {
        gtk_widget_set_sensitive(window->play_pause_button, TRUE);

        if(window->play_pause_image && window->pause_pixbuf)
        {
            gtk_image_set_from_pixbuf(GTK_IMAGE(window->play_pause_image),
                                      window->pause_pixbuf);
        }
    }
    
    /* Update menu items */
    if(window->play_menu_item)
    {
        gtk_widget_set_sensitive(window->play_menu_item, FALSE);
    }
    if(window->pause_menu_item)
    {
        gtk_widget_set_sensitive(window->pause_menu_item, TRUE);
    }
    if(window->stop_menu_item)
    {
        gtk_widget_set_sensitive(window->stop_menu_item, TRUE);
    }

    if(window->seek_scale)
    {
        gboolean is_live = hx_player_is_live(HX_PLAYER(window->player));
        gtk_widget_set_sensitive(window->seek_scale, !is_live);
    }
}

void
hpw_stop(HXMainWindow* window)
{
    gboolean enable_play;
    HXContentStateType state;
    
    if(window->on_top == ON_TOP_WHILE_PLAYING)
    {
        hxwindow_on_top_while_playing(window);
    }

    state = hx_player_get_content_state(HX_PLAYER(window->player));
    enable_play = (state != HX_CONTENT_STATE_NOT_LOADED);
    
    if(window->play_button)
    {
        gtk_widget_set_sensitive(window->play_button, enable_play);
    }
    if(window->pause_button)
    {
        gtk_widget_set_sensitive(window->pause_button, FALSE);
    }
    if(window->stop_button)
    {
        gtk_widget_set_sensitive(window->stop_button, FALSE);
    }
    if(window->play_pause_button)
    {
        gtk_widget_set_sensitive(window->play_pause_button, enable_play);

        if(window->play_pause_image && window->play_pixbuf)
        {
            gtk_image_set_from_pixbuf(GTK_IMAGE(window->play_pause_image),
                                      window->play_pixbuf);
        }
    }

    /* Update menu items */
    if(window->play_menu_item)
    {
        gtk_widget_set_sensitive(window->play_menu_item, enable_play);
    }
    if(window->pause_menu_item)
    {
        gtk_widget_set_sensitive(window->pause_menu_item, FALSE);
    }
    if(window->stop_menu_item)
    {
        gtk_widget_set_sensitive(window->stop_menu_item, FALSE);
    }

    gtk_widget_set_sensitive(GTK_WIDGET(window->previous_button), FALSE);
    gtk_widget_set_sensitive(GTK_WIDGET(window->previous_menu_item), FALSE);

    gtk_widget_set_sensitive(GTK_WIDGET(window->next_button), FALSE);
    gtk_widget_set_sensitive(GTK_WIDGET(window->next_menu_item), FALSE);
    
    gtk_widget_set_sensitive(window->seek_scale, FALSE);
}

void
hpw_pause(HXMainWindow* window)
{
    if(window->on_top == ON_TOP_WHILE_PLAYING)
    {
        hxwindow_on_top_while_playing(window);
    }

    if(window->play_button)
    {
        gtk_widget_set_sensitive(window->play_button, window->is_seeking);
    }
    if(window->pause_button)
    {
        gtk_widget_set_sensitive(window->pause_button, !window->is_seeking);
    }
    if(window->stop_button)
    {
        gtk_widget_set_sensitive(window->stop_button, TRUE);
    }
    if(window->play_pause_button)
    {
        gtk_widget_set_sensitive(window->play_pause_button, TRUE);

        if(window->is_seeking)
        {
            if(window->play_pause_image && window->play_pixbuf)
            {
                gtk_image_set_from_pixbuf(GTK_IMAGE(window->play_pause_image),
                                          window->pause_pixbuf);
            }
        }
        else
        {
            if(window->play_pause_image && window->pause_pixbuf)
            {
                gtk_image_set_from_pixbuf(GTK_IMAGE(window->play_pause_image),
                                          window->play_pixbuf);
            }
        }
    }

    /* Update menu items */
    if(window->play_menu_item)
    {
        gtk_widget_set_sensitive(window->play_menu_item, TRUE);
    }
    if(window->pause_menu_item)
    {
        gtk_widget_set_sensitive(window->pause_menu_item, FALSE);
    }
    if(window->stop_menu_item)
    {
        gtk_widget_set_sensitive(window->stop_menu_item, TRUE);
    }

    if(window->seek_scale)
    {
        gboolean is_live = hx_player_is_live(HX_PLAYER(window->player));
        gtk_widget_set_sensitive(window->seek_scale, !is_live);
    }
}

static gint
hpw_popup_menu(HXMainWindow* window,
               GdkEvent* event)
{
    GdkEventButton* event_button;

    g_return_val_if_fail (window != NULL, FALSE);
    g_return_val_if_fail (window->context_menu != NULL, FALSE);
                               
    if (event->type == GDK_BUTTON_PRESS)
    {
        event_button = (GdkEventButton *) event;
        if (event_button->button == 3)
	{
            hx_context_menu_popup(window->context_menu, &event->button);
            return TRUE;
	}
    }
    
    return FALSE; // propagate
}


static void
update_volume_icon(HXMainWindow* window)
{
    gboolean mute = hx_player_is_muted(HX_PLAYER(window->player));
    guint vol = hx_player_get_volume(HX_PLAYER(window->player));
    
    if(mute)
    {
        gtk_image_set_from_pixbuf(GTK_IMAGE(window->volume_image),
                                  window->mute_pixbuf);
    }
    else if(vol == 0)
    {
        gtk_image_set_from_pixbuf(GTK_IMAGE(window->volume_image),
                                  window->volume_off_pixbuf);
    }
    else if(vol < 33)
    {
        gtk_image_set_from_pixbuf(GTK_IMAGE(window->volume_image),
                                  window->volume_low_pixbuf);
    }
    else if(vol < 66)
    {
        gtk_image_set_from_pixbuf(GTK_IMAGE(window->volume_image),
                                  window->volume_mid_pixbuf);
    }
    else
    {
        gtk_image_set_from_pixbuf(GTK_IMAGE(window->volume_image),
                                  window->volume_high_pixbuf);
    }                  
}


void
hpw_volume_changed(HXMainWindow* window, guint vol)
{
    gtk_range_set_value(GTK_RANGE(window->volume_scale), (gdouble)vol);
    update_volume_icon(window);
}

void
hpw_mute_changed(HXMainWindow* window, gboolean mute)
{
    GtkWidget* mute_menu_item = glade_xml_get_widget(window->xml, "hmw_mute_menu_item");

    if(mute_menu_item)
    {
        gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(mute_menu_item),
                                       mute);
    }
    
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(window->mute_button),
                                 mute);
    update_volume_icon(window);
}

void
hpw_open_window(HXMainWindow* window, const gchar* url, gchar* /*target*/)
{
    /* Launch a new player. We do this in the same process for a couple
       of reasons:
       - We can use helix's software audio mixing if the device we're
         dealing with is braindead
       - We can do a better job of several smil linking features.

       The downside is that if one player crashes, they all go down.
    */
    HXMainWindow* new_window;
    GtkWidget* window_widget;

    /* Ensure that the current window has its close menu item showing */
    GtkWidget* close_menu_item = glade_xml_get_widget(window->xml, "hmw_close_menu_item");
    if(close_menu_item)
    {
        gtk_widget_show(close_menu_item);
    }

    /* Open new window */
    new_window = hxwindow_new();

    window_widget = hxwindow_get_window(new_window);
    
    gtk_widget_show_all(window_widget);

    hxwindow_open(new_window, url);
}

void
hpw_goto_url(HXMainWindow* /* window */, const  gchar* url, gchar* /*target*/)
{
    hxcommon_url_show(url);
}

void
hpw_request_upgrade(HXMainWindow* window, gchar* url, GList* components_list, gboolean blocking)
{
    hxcommon_run_request_upgrade_dialog(GTK_WINDOW(window->window),
                                        NULL,
                                        url,
                                        components_list,
                                        blocking);
}

void
hpw_group_started(HXMainWindow* window)
{
    hxwindow_change_layout(window);
}

void
hpw_groups_changed(HXMainWindow* window)
{
    hxwindow_update_ui(window);
}

void
hpw_visual_state_changed(HXMainWindow* window, gboolean /* has_video */)
{
    hxwindow_change_layout(window);
}


void
hpw_hxerror(HXMainWindow* window,
            gint          hxcode,
            gint          user_code,
            const gchar*  error_string,
            const gchar*  user_string,
            const gchar*  more_info_url)
{
#ifndef HELIX_FEATURE_REAL_BRANDING
    /* Bad protocol errors are (hopefully) the result of the Helix Player
       trying to play an rtsp stream that uses RDT, which is a RP-only
       feature. Forward these errors to the AU system */

    if(hxcode == HXR_BAD_TRANSPORT)
    {        
        const gchar* url = hx_player_get_url(HX_PLAYER(window->player));
        gchar rtsp_component[] = "protocol_rtsp_rdt";

        GList* component_list = NULL;
        component_list = g_list_append(component_list, rtsp_component);

        hxcommon_run_request_upgrade_dialog(GTK_WINDOW(window->window),
                                            NULL,
                                            url,
                                            component_list,
                                            TRUE);
        g_list_free(component_list);
        
        return;
    }
    else if(hxcode == HXR_INVALID_PROTOCOL)
    {
        const gchar* url = hx_player_get_url(HX_PLAYER(window->player));
        gchar pnm_component[] = "protocol_pnm";

        GList* component_list = NULL;
        component_list = g_list_append(component_list, pnm_component);

        hxcommon_run_request_upgrade_dialog(GTK_WINDOW(window->window),
                                            NULL,
                                            url,
                                            component_list,
                                            TRUE);
        g_list_free(component_list);
        
        return;
    }
    
#endif

    hxcommon_run_error_dialog(GTK_WINDOW(window->window),
                              NULL,
                              hxcode,
                              user_code,
                              error_string,
                              user_string,
                              more_info_url);
}

void
hpw_request_authentication(HXMainWindow* window,
                           const gchar* server_proxy,
                           const gchar* realm,
                           gboolean is_proxy_server)
{
    hxcommon_run_authentication_dialog(GTK_WINDOW(window->window),
                                       NULL,
                                       HX_PLAYER(window->player),
                                       server_proxy,
                                       realm,
                                       is_proxy_server);
}

void
hpw_content_concluded(HXMainWindow* window)
{
    if(window->quit_on_done)
    {
        gtk_main_quit();
    }
}

void
hpw_ideal_size_changed(HXMainWindow* window,
                       gint /* ideal_width */,
                       gint /* ideal_height */)
{
    // ignore the resize request if fullscreened.
    if(!window->is_fullscreened)
    {
        hxwindow_change_layout(window);
    }
}

/* Setup/Destroy functions called from hxcommon.cpp
 * ================================================
 */
/* Set up as a non-embedded media window application */

/* setup_player_client supporting function */
static gboolean
get_widgets(HXMainWindow* window)
{
    window->window = glade_xml_get_widget(window->xml, "hxplayer_main_window");
    g_return_val_if_fail(window->window != NULL, FALSE);
	
    window->hxbin = glade_xml_get_widget(window->xml, "hmw_hxbin");
    g_return_val_if_fail(window->hxbin != NULL, FALSE);

    window->vbox = glade_xml_get_widget(window->xml, "hmw_vbox");
    g_return_val_if_fail(window->vbox != NULL, FALSE);

    window->menu_bar = glade_xml_get_widget(window->xml, "hmw_menu_bar");
    g_return_val_if_fail(window->menu_bar != NULL, FALSE);

    /* Get menu items */
    window->play_menu_item = glade_xml_get_widget(window->xml, "hmw_play_menu_item");
    g_return_val_if_fail(window->play_menu_item != NULL, FALSE);

    window->pause_menu_item = glade_xml_get_widget(window->xml, "hmw_pause_menu_item");
    g_return_val_if_fail(window->pause_menu_item != NULL, FALSE);
    
    window->stop_menu_item = glade_xml_get_widget(window->xml, "hmw_stop_menu_item");
    g_return_val_if_fail(window->stop_menu_item != NULL, FALSE);

    window->next_menu_item = glade_xml_get_widget(window->xml, "hmw_next_menu_item");
    g_return_val_if_fail(window->next_menu_item != NULL, FALSE);
    
    window->previous_menu_item = glade_xml_get_widget(window->xml, "hmw_previous_menu_item");
    g_return_val_if_fail(window->previous_menu_item != NULL, FALSE);

    window->play_button = glade_xml_get_widget(window->xml, "hmw_play_button");
    window->pause_button = glade_xml_get_widget(window->xml, "hmw_pause_button");
    window->stop_button = glade_xml_get_widget(window->xml, "hmw_stop_button");
    window->play_pause_button = glade_xml_get_widget(window->xml, "hmw_play_pause_button");
    window->play_pause_image = glade_xml_get_widget(window->xml, "hmw_play_pause_image");
        
    window->previous_button = glade_xml_get_widget(window->xml, "hmw_previous_button");
    g_return_val_if_fail(window->previous_button != NULL, FALSE);

    window->next_button = glade_xml_get_widget(window->xml, "hmw_next_button");
    g_return_val_if_fail(window->next_button != NULL, FALSE);

    window->seek_scale = glade_xml_get_widget(window->xml, "hmw_seek_scale");
    g_return_val_if_fail(window->seek_scale != NULL, FALSE);

    window->volume_scale = glade_xml_get_widget(window->xml, "hmw_volume_scale");
    g_return_val_if_fail(window->volume_scale != NULL, FALSE);

    window->mute_button = glade_xml_get_widget(window->xml, "hmw_mute_toggle_button");
    g_return_val_if_fail(window->mute_button != NULL, FALSE);

    window->volume_image = glade_xml_get_widget(window->xml, "hmw_volume_image");
    g_return_val_if_fail(window->volume_image != NULL, FALSE);

    window->controls_hbox = glade_xml_get_widget(window->xml, "hmw_controls_hbox");
    g_return_val_if_fail(window->controls_hbox != NULL, FALSE);

    window->status_bar = glade_xml_get_widget(window->xml, "hmw_status_area");
    g_return_val_if_fail(window->status_bar != NULL, FALSE);

    window->on_top_never_menu_item = glade_xml_get_widget(window->xml, "hmw_on_top_never_menu_item");
    g_return_val_if_fail(window->on_top_never_menu_item != NULL, FALSE);

    window->on_top_while_playing_menu_item = glade_xml_get_widget(window->xml, "hmw_on_top_while_playing_menu_item");
    g_return_val_if_fail(window->on_top_while_playing_menu_item != NULL, FALSE);

    window->on_top_always_menu_item = glade_xml_get_widget(window->xml, "hmw_on_top_always_menu_item");
    g_return_val_if_fail(window->on_top_always_menu_item != NULL, FALSE);

    window->normal_size_menu_item = glade_xml_get_widget(window->xml, "hmw_normal_size_menu_item");
    g_return_val_if_fail(window->normal_size_menu_item != NULL, FALSE);

    window->double_size_menu_item = glade_xml_get_widget(window->xml, "hmw_double_size_menu_item");
    g_return_val_if_fail(window->double_size_menu_item != NULL, FALSE);
    
    return TRUE;
}

/* setup_player_client supporting function. Initialize the
   HXMainWindow widget */
static gboolean
setup_player(HXMainWindow* window)
{
    guint vol;

    hx_bin_stretch_to_fit(HX_BIN(window->hxbin), TRUE);
    hx_bin_maintain_aspect_ratio(HX_BIN(window->hxbin), TRUE);

    g_signal_connect_swapped(G_OBJECT(window->hxbin),
                             "button_press_event",
                             G_CALLBACK(hpw_popup_menu),
                             window);    

    window->player = hx_player_new();
    g_return_val_if_fail(window->player != NULL, FALSE);

    gtk_widget_show(window->player);        

    /* Manually connect up the signals for the hxwindow widget,
       since it is not integrated into glade. We could do this
       in the glade xml file, and register the widget with libglade,
       but this confuses the glade gui editor, and doesn't work with
       generated code. */

    g_signal_connect_swapped(G_OBJECT(window->player),
                             "play",
                             G_CALLBACK(hpw_play_buffer_contact),
                             window);

    g_signal_connect_swapped(G_OBJECT(window->player),
                             "play",
                             G_CALLBACK(hpw_play),
                             window);

    g_signal_connect_swapped(G_OBJECT(window->player),
                             "buffering",
                             G_CALLBACK(hpw_play_buffer_contact),
                             window);
    
    g_signal_connect_swapped(G_OBJECT(window->player),
                             "contacting",
                             G_CALLBACK(hpw_play_buffer_contact),
                             window);

    g_signal_connect_swapped(G_OBJECT(window->player),
                             "pause",
                             G_CALLBACK(hpw_pause),
                             window);

    g_signal_connect_swapped(G_OBJECT(window->player),
                             "stop",
                             G_CALLBACK(hpw_stop),
                             window);    

    g_signal_connect_swapped(G_OBJECT(window->player),
                             "button_press_event",
                             G_CALLBACK(hpw_popup_menu),
                             window);    

    g_signal_connect_swapped(G_OBJECT(window->player),
                             "volume_changed",
                             G_CALLBACK(hpw_volume_changed),
                             window);    

    g_signal_connect_swapped(G_OBJECT(window->player),
                             "mute_changed",
                             G_CALLBACK(hpw_mute_changed),
                             window);    

    g_signal_connect_swapped(G_OBJECT(window->player),
                             "open_window",
                             G_CALLBACK(hpw_open_window),
                             window);    

    g_signal_connect_swapped(G_OBJECT(window->player),
                             "goto_url",
                             G_CALLBACK(hpw_goto_url),
                             window);    

    g_signal_connect_swapped(G_OBJECT(window->player),
                             "request_upgrade",
                             G_CALLBACK(hpw_request_upgrade),
                             window);    

    g_signal_connect_swapped(G_OBJECT(window->player),
                             "groups_changed",
                             G_CALLBACK(hpw_groups_changed),
                             window);    

    g_signal_connect_swapped(G_OBJECT(window->player),
                             "group_started",
                             G_CALLBACK(hpw_group_started),
                             window);    

    g_signal_connect_swapped(G_OBJECT(window->player),
                             "visual_state_changed",
                             G_CALLBACK(hpw_visual_state_changed),
                             window);    

    g_signal_connect_swapped(G_OBJECT(window->player),
                             "hxerror",
                             G_CALLBACK(hpw_hxerror),
                             window);

    g_signal_connect_swapped(G_OBJECT(window->player),
                             "request_authentication",
                             G_CALLBACK(hpw_request_authentication),
                             window);

    g_signal_connect_swapped(G_OBJECT(window->player),
                             "content_concluded",
                             G_CALLBACK(hpw_content_concluded),
                             window);

    g_signal_connect_swapped(G_OBJECT(window->player),
                             "ideal_size_changed",
                             G_CALLBACK(hpw_ideal_size_changed),
                             window);    

    g_signal_connect_swapped(G_OBJECT(window->player),
                             "has_feature",
                             G_CALLBACK(hxcommon_player_has_feature),
                             window->player);    

    g_signal_connect_swapped(G_OBJECT(window->player),
                             "start_seeking",
                             G_CALLBACK(hpw_start_seeking),
                             window);    

    g_signal_connect_swapped(G_OBJECT(window->player),
                             "stop_seeking",
                             G_CALLBACK(hpw_stop_seeking),
                             window);
    
    /* This will cause the window widget to be realized */
    gtk_container_add(GTK_CONTAINER(window->hxbin), window->player);

    /* Set the initial volume */
    vol = hx_player_get_volume(HX_PLAYER(window->player));
    // XXXRGG: Client core clobbers mixer volume
    gtk_range_set_value(GTK_RANGE(window->volume_scale), (gdouble)vol);
    
    return TRUE;
}

HXMainWindow*
hxwindow_new(void)
{    
    gchar* filename;
    GdkPixmap* pixmap;
    GError* error = NULL;

    if(!g_hx_main_app)
    {
        g_hx_main_app = g_new0(HXMainApp, 1);
    }
    
    HXMainWindow* window = g_new0(HXMainWindow, 1);

    window->enable_url_hurling = TRUE;
    window->enable_recent_urls = TRUE;
       
    filename = hxcommon_locate_file("main.glade");
    window->xml = glade_xml_new (filename, NULL, NULL);
    g_free(filename);
    g_return_val_if_fail(window->xml != NULL, FALSE);
    
    glade_xml_signal_autoconnect(window->xml);

    window->status_bar = hxstatus_display_top_new();

    g_return_val_if_fail(window->status_bar != NULL, FALSE);
    
    if(!get_widgets(window))
    {
	return FALSE;
    }

    gtk_window_set_title (GTK_WINDOW (window->window), APP_NAME_LONG);
                         
    gtk_widget_add_events (window->window, GDK_PROPERTY_CHANGE_MASK);
    
    g_signal_connect_swapped (G_OBJECT(window->window), "destroy",
                              G_CALLBACK(hxwindow_destroy),
                              window);
    
    filename = hxcommon_locate_file("play.png");
    window->play_pixbuf = gdk_pixbuf_new_from_file(filename, &error);
    g_free(filename);

    filename = hxcommon_locate_file("pause.png");
    window->pause_pixbuf = gdk_pixbuf_new_from_file(filename, &error);
    g_free(filename);

    filename = hxcommon_locate_file("volume_mute.png");
    window->mute_pixbuf = gdk_pixbuf_new_from_file(filename, &error);
    g_free(filename);

    filename = hxcommon_locate_file("volume_off.png");
    window->volume_off_pixbuf = gdk_pixbuf_new_from_file(filename, &error);
    g_free(filename);

    filename = hxcommon_locate_file("volume_low.png");
    window->volume_low_pixbuf = gdk_pixbuf_new_from_file(filename, &error);
    g_free(filename);

    filename = hxcommon_locate_file("volume_mid.png");
    window->volume_mid_pixbuf = gdk_pixbuf_new_from_file(filename, &error);
    g_free(filename);

    filename = hxcommon_locate_file("volume_high.png");
    window->volume_high_pixbuf = gdk_pixbuf_new_from_file(filename, &error);
    g_free(filename);

    /* Set up the window */
    /* For session management -- the wm should restore our position */
    gtk_window_set_role(GTK_WINDOW(window->window), APP_NAME_SHORT);
    
    /* XXXRGG: Read these from prefs */
    hxwindow_show_controls(window, TRUE);
    hxwindow_show_menu_and_caption(window, TRUE);
    hxwindow_show_status_bar(window, TRUE);

    g_object_set_data(G_OBJECT(window->window), "hxwindow", window);

    if (g_hx_main_app->windows_list == NULL)
    {
        /* We're the first player window -- hide the close option */
        GtkWidget* close_menu_item = glade_xml_get_widget(window->xml, "hmw_close_menu_item");
        if(close_menu_item)
        {
            gtk_widget_hide(close_menu_item);
        }
    }
    g_hx_main_app->windows_list = g_list_append(g_hx_main_app->windows_list, window);

    hxcommon_load_preferences(window);

    /* Update OrigCode and DistCode */
    const gchar* distcode_lang = hxcommon_get_distcode_language();

    if(!distcode_lang)
    {
        distcode_lang = "";
    }
    
    snprintf(window->distcode, sizeof(window->distcode), DISTCODE, distcode_lang);

    if(!window->origcode[0])
    {
        snprintf(window->origcode, sizeof(window->origcode), DISTCODE, distcode_lang);
    }

    /* XXXRGG: Hack!*/
    hx_sys_info_set_distcode(window->distcode);
    hx_sys_info_set_origcode(window->origcode);
    /* End hack */
    
    if(!setup_player(window))
    {
	return FALSE;
    }

    g_signal_connect_swapped(G_OBJECT(window->hxbin),
                             "size-allocate",
                             G_CALLBACK(hmw_hxbin_size_allocate),
                             window);

    g_signal_connect_swapped(G_OBJECT(window->window),
                             "size-allocate",
                             G_CALLBACK(hmw_window_size_allocate),
                             window);
    
    hxstatus_display_set_player(HX_STATUS_DISPLAY(window->status_bar),
                                HX_PLAYER(window->player));
    
    hxstatus_display_set_player(HX_STATUS_DISPLAY(window->seek_scale),
                                HX_PLAYER(window->player));

    update_volume_icon(window);

    if (!check_registered())
    {
	if (show_setup_assistant(window) < 0)
	{
	    // the EULA was not accepted
	    return NULL;
	}

        /* The client defaults to turning TurboPlay off. Turn it on here at first run */
        HXValue* value = hx_value_new(HX_VALUE_STRING);
        if (value)
        {
            hx_value_set_string(value, "1");
            hx_prefs_set_entry("TurboPlay", value);
            hx_value_free(value);
        }
    }

#ifdef HELIX_FEATURE_RP_SIGN_IN
    /* Init sign-in and add a menu item */
    if(sign_in_init(window))
    {
        GtkWidget* hmw_sign_in_menu_item;
        GtkWidget *separator;
        GtkWidget *file_menu, *file_menu_item;
        gint pos;
        GList *children;

        separator = glade_xml_get_widget(window->xml, "hmw_mru_separator");        
        file_menu_item = glade_xml_get_widget(window->xml, "hmw_file_menu");

        if(file_menu_item)
        {
            file_menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(file_menu_item));
        }

        if(file_menu && separator)
        {
            children = GTK_MENU_SHELL(file_menu)->children;
    
            pos = g_list_index(children, separator);
            if(pos > 0)
            {            
                pos--;
        
                hmw_sign_in_menu_item = gtk_menu_item_new_with_mnemonic (_("_Sign in..."));
                gtk_widget_set_name (hmw_sign_in_menu_item, "hmw_sign_in_menu_item");
                gtk_widget_show (hmw_sign_in_menu_item);

                gtk_menu_shell_insert(GTK_MENU_SHELL(file_menu), hmw_sign_in_menu_item, pos);

                g_signal_connect(G_OBJECT(hmw_sign_in_menu_item), "activate",
                                 G_CALLBACK(hmw_sign_in), NULL);
            }
        }
    }
#endif
    /* Load the context menu */
    window->context_menu = hx_context_menu_new_with_main_window(window);

    /* Set initial button states to stop */
    hpw_stop(window);
    hxwindow_update_ui(window);

    /* Do an initial resize */
    hxwindow_change_layout(window);

    /* enable drag 'n drop */
    static const GtkTargetEntry targets[] =
    {
        { "text/uri-list", 0, 0 },            
    };
    
    gtk_drag_dest_set (GTK_WIDGET(window->window),
                       GTK_DEST_DEFAULT_ALL,
                       targets,
                       sizeof(targets) / sizeof(*targets),
                       GDK_ACTION_COPY);
    
    if(window->zoom == CUSTOM_SIZE && !window->is_fullscreened)
    {
        /* This will get applied in hpw_ideal_size_changed */
        window->zoom = NORMAL_SIZE;
    }

    /* Show the window */
    gtk_widget_show(GTK_WIDGET(window->window));
        
    /* Render the logo */
    pixmap = hxcommon_get_logo_pixmap(window->player, window->window->window);
    hx_player_set_logo_pixmap(HX_PLAYER(window->player), pixmap);
    g_object_unref(G_OBJECT(pixmap));

    return window;
}

static void
hxwindow_destroy(HXMainWindow* window)
{
    GList* item;
    
#ifdef HELIX_FEATURE_RP_SIGN_IN
    /* Init sign-in and add a menu item */
    sign_in_destroy();
#endif

    hxcommon_save_preferences(window);
    
    g_list_foreach(window->recent_urls_list, (GFunc)g_free, NULL);
    g_list_free(window->recent_urls_list);

    if(window->context_menu)
    {
        hx_context_menu_destroy(window->context_menu);
    }
    
    g_free(window);

    item = g_list_find(g_hx_main_app->windows_list, window);
    if(item)
    {
        g_hx_main_app->windows_list = 
            g_list_delete_link(g_hx_main_app->windows_list, item);
    }

    if(g_list_length(g_hx_main_app->windows_list) == 0)
    {
        if(g_hx_main_app)
        {
            g_free(g_hx_main_app);
        }

        gtk_main_quit();
    }
}
