OSDN Git Service

LinGui: remove target file size option
[handbrake-jp/handbrake-jp-git.git] / gtk / src / callbacks.c
index 9e8bc23..9355e80 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
 /*
  * callbacks.c
- * Copyright (C) John Stebbins 2008 <stebbins@stebbins>
+ * Copyright (C) John Stebbins 2008-2011 <stebbins@stebbins>
  * 
  * callbacks.c is free software.
  * 
 #endif
 
 #include <string.h>
-#include <poll.h>
 #include <fcntl.h>
 #include <sys/stat.h>
+#include <time.h>
+
+#if !defined(_WIN32)
+#include <poll.h>
+#define G_UDEV_API_IS_SUBJECT_TO_CHANGE 1
+#include <gudev/gudev.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
 #include <netinet/in.h>
 #include <netdb.h>
-#include <libhal-storage.h>
+
+#if !defined(_NO_UPDATE_CHECK)
+#if defined(_OLD_WEBKIT)
+#include <webkit.h>
+#else
+#include <webkit/webkit.h>
+#endif
+#endif
+
+#include <libnotify/notify.h>
+#include <gdk/gdkx.h>
+#else
+#define WINVER 0x0500
+#include <winsock2.h>
+#include <dbt.h>
+#endif
+
 #include <gtk/gtk.h>
-#include <gtkhtml/gtkhtml.h>
 #include <gdk/gdkkeysyms.h>
 #include <glib/gstdio.h>
-#include <dbus/dbus-glib.h>
-#include <dbus/dbus-glib-lowlevel.h>
 #include <gio/gio.h>
 
 #include "hb.h"
 #include "callbacks.h"
 #include "queuehandler.h"
 #include "audiohandler.h"
+#include "subtitlehandler.h"
 #include "resources.h"
 #include "settings.h"
 #include "presets.h"
 #include "ghb-dvd.h"
 #include "ghbcellrenderertext.h"
 
+static void reset_chapter_list(signal_user_data_t *ud, GValue *settings);
 static void update_chapter_list(signal_user_data_t *ud);
 static GList* dvd_device_list();
 static void prune_logs(signal_user_data_t *ud);
+void ghb_notify_done(signal_user_data_t *ud);
+gpointer ghb_check_update(signal_user_data_t *ud);
+static gboolean ghb_can_shutdown_gsm();
+static void ghb_shutdown_gsm();
+static gboolean ghb_can_suspend_gpm();
+static void ghb_suspend_gpm();
+static gboolean appcast_busy = FALSE;
 
 // This is a dependency map used for greying widgets
 // that are dependent on the state of another widget.
@@ -89,8 +119,7 @@ dep_check(signal_user_data_t *ud, const gchar *name, gboolean *out_hide)
                widget_name = ghb_value_string(ghb_array_get_nth(data, 0));
                widget = GHB_WIDGET(ud->builder, widget_name);
                dep_object = gtk_builder_get_object(ud->builder, name);
-               g_free(widget_name);
-               if (!GTK_WIDGET_SENSITIVE(widget))
+               if (widget != NULL && !GTK_WIDGET_SENSITIVE(widget))
                        continue;
                if (dep_object == NULL)
                {
@@ -113,7 +142,7 @@ dep_check(signal_user_data_t *ud, const gchar *name, gboolean *out_hide)
                        if (widget)
                                value = ghb_widget_string(widget);
                        else
-                               value = ghb_settings_get_string(ud->settings, name);
+                               value = ghb_settings_get_string(ud->settings, widget_name);
                        while (values && values[jj])
                        {
                                if (values[jj][0] == '>')
@@ -152,12 +181,16 @@ dep_check(signal_user_data_t *ud, const gchar *name, gboolean *out_hide)
                        g_strfreev (values);
                        g_free(value);
                }
+               g_free(widget_name);
        }
        return result;
 }
 
 void
-ghb_check_dependency(signal_user_data_t *ud, GtkWidget *widget)
+ghb_check_dependency(
+       signal_user_data_t *ud, 
+       GtkWidget *widget, 
+       const char *alt_name)
 {
        GObject *dep_object;
        const gchar *name;
@@ -166,11 +199,16 @@ ghb_check_dependency(signal_user_data_t *ud, GtkWidget *widget)
        gchar *dep_name;
        GType type;
 
-       type = GTK_WIDGET_TYPE(widget);
-       if (type == GTK_TYPE_COMBO_BOX || type == GTK_TYPE_COMBO_BOX_ENTRY)
-               if (gtk_combo_box_get_active(GTK_COMBO_BOX(widget)) < 0) return;
+       if (widget != NULL)
+       {
+               type = GTK_WIDGET_TYPE(widget);
+               if (type == GTK_TYPE_COMBO_BOX || type == GTK_TYPE_COMBO_BOX_ENTRY)
+                       if (gtk_combo_box_get_active(GTK_COMBO_BOX(widget)) < 0) return;
+               name = ghb_get_setting_key(widget);
+       }
+       else
+               name = alt_name;
 
-       name = gtk_widget_get_name(widget);
        g_debug("ghb_check_dependency () %s", name);
 
        if (dep_map == NULL) return;
@@ -258,14 +296,14 @@ ghb_check_all_depencencies(signal_user_data_t *ud)
        }
 }
 
-void
+G_MODULE_EXPORT void
 on_quit1_activate(GtkMenuItem *quit, signal_user_data_t *ud)
 {
        gint state = ghb_get_queue_state();
        g_debug("on_quit1_activate ()");
-       if (state & GHB_STATE_WORKING)
-       {
-               if (ghb_cancel_encode("Closing HandBrake will terminate encoding.\n"))
+       if (state & (GHB_STATE_WORKING|GHB_STATE_SEARCHING))
+       {
+               if (ghb_cancel_encode2(ud, "Closing HandBrake will terminate encoding.\n"))
                {
                        ghb_hb_cleanup(FALSE);
                        prune_logs(ud);
@@ -279,63 +317,11 @@ on_quit1_activate(GtkMenuItem *quit, signal_user_data_t *ud)
        gtk_main_quit();
 }
 
-static void
-set_destination(signal_user_data_t *ud)
-{
-       g_debug("set_destination");
-       if (ghb_settings_get_boolean(ud->settings, "use_source_name"))
-       {
-               GString *str = g_string_new("");
-               gchar *vol_name, *filename, *extension;
-               gchar *new_name;
-               gint title;
-               
-               filename = ghb_settings_get_string(ud->settings, "dest_file");
-               extension = ghb_settings_get_string(ud->settings, "FileFormat");
-               vol_name = ghb_settings_get_string(ud->settings, "volume_label");
-               g_string_append_printf(str, "%s", vol_name);
-               title = ghb_settings_combo_int(ud->settings, "title");
-               if (title >= 0)
-               {
-                       if (ghb_settings_get_boolean(
-                                       ud->settings, "title_no_in_destination"))
-                       {
-
-                               title = ghb_settings_combo_int(ud->settings, "title");
-                               g_string_append_printf(str, " - %d", title+1);
-                       }
-                       if (ghb_settings_get_boolean(
-                                       ud->settings, "chapters_in_destination"))
-                       {
-                               gint start, end;
-
-                               if (!ghb_settings_get_boolean(
-                                               ud->settings, "title_no_in_destination"))
-                               {
-                                       g_string_append_printf(str, " -");
-                               }
-                               start = ghb_settings_get_int(ud->settings, "start_chapter");
-                               end = ghb_settings_get_int(ud->settings, "end_chapter");
-                               if (start == end)
-                                       g_string_append_printf(str, " Ch %d", start);
-                               else
-                                       g_string_append_printf(str, " Ch %d-%d", start, end);
-                       }
-               }
-               g_string_append_printf(str, ".%s", extension);
-               new_name = g_string_free(str, FALSE);
-               ghb_ui_update(ud, "dest_file", ghb_string_value(new_name));
-               g_free(filename);
-               g_free(extension);
-               g_free(vol_name);
-               g_free(new_name);
-       }
-}
-
 gboolean
-uppers_and_unders(const gchar *str)
+uppers_and_unders(gchar *str)
 {
        if (str == NULL) return FALSE;
+       str = g_strchomp(g_strchug(str));
        while (*str)
        {
                if (*str == ' ')
@@ -388,51 +374,345 @@ camel_convert(gchar *str)
        }
 }
 
+#if defined(_WIN32)
+static gchar*
+get_dvd_device_name(gchar *device)
+{
+       return g_strdup(device);
+}
+#else
+static gchar*
+get_dvd_device_name(GDrive *gd)
+{
+       return g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
+}
+#endif
+
+static GHashTable *volname_hash = NULL;
+static GMutex     *volname_mutex = NULL;
+
+static void
+free_volname_key(gpointer data)
+{
+       if (data != NULL)
+               g_free(data);
+}
+
+static void
+free_volname_value(gpointer data)
+{
+       if (data != NULL)
+               g_free(data);
+}
+
+#if defined(_WIN32)
+static gchar*
+get_direct_dvd_volume_name(const gchar *drive)
+{
+       gchar *result = NULL;
+       gchar vname[51], fsname[51];
+
+       if (GetVolumeInformation(drive, vname, 50, NULL, NULL, NULL, fsname, 50))
+       {
+               result = g_strdup_printf("%s", vname);
+       }
+       return result;
+}
+#else
+static gchar*
+get_direct_dvd_volume_name(const gchar *drive)
+{
+       gchar *result;
+
+       result = ghb_dvd_volname (drive);
+       return result;
+}
+#endif
+
+static gchar*
+get_dvd_volume_name(gpointer gd)
+{
+       gchar *label = NULL;
+       gchar *result;
+       gchar *drive;
+
+       drive = get_dvd_device_name(gd);
+       g_mutex_lock(volname_mutex);
+       label = g_strdup(g_hash_table_lookup(volname_hash, drive));
+       g_mutex_unlock(volname_mutex);
+       if (label != NULL)
+       {
+               if (uppers_and_unders(label))
+               {
+                       camel_convert(label);
+               }
+#if defined(_WIN32)
+               result = g_strdup_printf("%s (%s)", label, drive);
+#else
+               result = g_strdup_printf("%s - %s", drive, label);
+#endif
+               g_free(label);
+       }
+       else
+       {
+               result = g_strdup_printf("%s", drive);
+       }
+       g_free(drive);
+       return result;
+}
+
+void
+ghb_volname_cache_init(void)
+{
+       volname_mutex = g_mutex_new();
+       volname_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
+                                                                               free_volname_key, free_volname_value);
+}
+
+static void
+free_drive(gpointer drive)
+{
+#if defined(_WIN32)
+               g_free(drive);
+#else
+               g_object_unref(drive);
+#endif
+}
+
+gpointer
+ghb_cache_volnames(signal_user_data_t *ud)
+{
+       GList *link, *drives;
+
+       g_debug("ghb_cache_volnames()");
+       link = drives = dvd_device_list();
+       if (drives == NULL)
+               return NULL;
+
+       g_mutex_lock(volname_mutex);
+       g_hash_table_remove_all(volname_hash);
+       while (link != NULL)
+       {
+               gchar *name, *drive;
+
+#if !defined(_WIN32)
+               if (!g_drive_has_media (link->data))
+               {
+                       g_object_unref(link->data);
+                       link = link->next;
+                       continue;
+               }
+#endif
+               drive = get_dvd_device_name(link->data);
+               name = get_direct_dvd_volume_name(drive);
+
+               if (drive != NULL && name != NULL)
+               {
+                       g_hash_table_insert(volname_hash, drive, name);
+               }
+               else
+               {
+                       if (drive != NULL)
+                               g_free(drive);
+                       if (name != NULL)
+                               g_free(name);
+               }
+       
+               free_drive(link->data);
+               link = link->next;
+       }
+       g_mutex_unlock(volname_mutex);
+
+       g_list_free(drives);
+
+       g_idle_add((GSourceFunc)ghb_file_menu_add_dvd, ud);
+
+       return NULL;
+}
+
+static const gchar*
+get_extension(signal_user_data_t *ud)
+{
+       int container;
+       const gchar *extension = "error";
+
+       container = ghb_settings_combo_int(ud->settings, "FileFormat");
+       if (container == HB_MUX_MP4)
+       {
+               extension = "mp4";
+               if (ghb_settings_get_boolean(ud->settings, "UseM4v"))
+               {
+                       extension = "m4v";
+               }
+       }
+       else if (container == HB_MUX_MKV)
+       {
+               extension = "mkv";
+       }
+       return extension;
+}
+
+static void
+set_destination(signal_user_data_t *ud)
+{
+       g_debug("set_destination");
+       if (ghb_settings_get_boolean(ud->settings, "use_source_name"))
+       {
+               GString *str = g_string_new("");
+               gchar *vol_name, *filename;
+               const gchar *extension;
+               gchar *new_name;
+               gint title;
+               
+               filename = ghb_settings_get_string(ud->settings, "dest_file");
+               extension = get_extension(ud);
+               vol_name = ghb_settings_get_string(ud->settings, "volume_label");
+               g_string_append_printf(str, "%s", vol_name);
+               title = ghb_settings_combo_int(ud->settings, "title");
+               if (title >= 0)
+               {
+                       if (ghb_settings_get_boolean(
+                                       ud->settings, "title_no_in_destination"))
+                       {
+
+                               title = ghb_settings_combo_int(ud->settings, "title");
+                               g_string_append_printf(str, " - %d", title+1);
+                       }
+                       if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0 && 
+                               ghb_settings_get_boolean(
+                                       ud->settings, "chapters_in_destination"))
+                       {
+                               gint start, end;
+
+                               if (!ghb_settings_get_boolean(
+                                               ud->settings, "title_no_in_destination"))
+                               {
+                                       g_string_append_printf(str, " -");
+                               }
+                               start = ghb_settings_get_int(ud->settings, "start_point");
+                               end = ghb_settings_get_int(ud->settings, "end_point");
+                               if (start == end)
+                                       g_string_append_printf(str, " Ch %d", start);
+                               else
+                                       g_string_append_printf(str, " Ch %d-%d", start, end);
+                       }
+               }
+               g_string_append_printf(str, ".%s", extension);
+               new_name = g_string_free(str, FALSE);
+               ghb_ui_update(ud, "dest_file", ghb_string_value(new_name));
+               g_free(filename);
+               g_free(vol_name);
+               g_free(new_name);
+       }
+}
+
 static gchar*
 get_file_label(const gchar *filename)
 {
-       static gchar *containers[] = 
-               {".vob", ".mpg", ".m2ts", ".mkv", ".mp4", ".m4v", ".avi", ".ogm", NULL};
-       gchar *base;
-       gint ii;
+       gchar *base, *pos, *end;
 
        base = g_path_get_basename(filename);
-       for (ii = 0; containers[ii] != NULL; ii++)
+       pos = strrchr(base, '.');
+       if (pos != NULL)
        {
-               if (g_str_has_suffix(base, containers[ii]))
-               {
-                       gchar *pos;
-                       pos = strrchr(base, '.');
+               // If the last '.' is within 4 chars of end of name, assume
+               // there is an extension we want to strip.
+               end = &base[strlen(base) - 1];
+               if (end - pos <= 4)
                        *pos = 0;
-                       break;
-               }
        }
        return base;
 }
 
+static gchar*
+resolve_drive_name(gchar *filename)
+{
+#if defined(_WIN32)
+       if (filename[1] == ':')
+       {
+               gchar drive[4];
+               gchar *name;
+               gint dtype;
+
+               g_strlcpy(drive, filename, 4);
+               dtype = GetDriveType(drive);
+               if (dtype == DRIVE_CDROM)
+               {
+                       gchar vname[51], fsname[51];
+                       GetVolumeInformation(drive, vname, 50, NULL, 
+                                                               NULL, NULL, fsname, 50);
+                       name = g_strdup(vname);
+                       return name;
+               }
+       }
+       return NULL;
+#else
+       return NULL;
+#endif
+}
+
 static gboolean
-update_source_label(signal_user_data_t *ud, const gchar *source)
+update_source_label(signal_user_data_t *ud, const gchar *source, gboolean update_dest)
 {
        gchar *label = NULL;
        gint len;
        gchar **path;
+       gchar *start;
        gchar *filename = g_strdup(source);
        
+       g_debug("update_source_label()");
        len = strlen(filename);
-       if (filename[len-1] == '/') filename[len-1] = 0;
        if (g_file_test(filename, G_FILE_TEST_IS_DIR))
        {
-               path = g_strsplit(filename, "/", -1);
-               len = g_strv_length (path);
-               if ((len > 1) && (strcmp("VIDEO_TS", path[len-1]) == 0))
+               // Skip dos drive letters
+#if defined(_WIN32)
+               start = strchr(filename, ':');
+#else
+               start = filename;
+#endif
+               label = resolve_drive_name(filename);
+               if (label != NULL)
                {
-                       label = g_strdup(path[len-2]);
+                       if (uppers_and_unders(label))
+                       {
+                               camel_convert(label);
+                       }
                }
                else
                {
-                       label = g_strdup(path[len-1]);
+                       if (filename[len-1] == G_DIR_SEPARATOR) filename[len-1] = 0;
+                       if (start != NULL)
+                               start++;
+                       else
+                               start = filename;
+                       
+                       path = g_strsplit(start, G_DIR_SEPARATOR_S, -1);
+                       len = g_strv_length (path);
+                       if ((len > 1) && (strcmp("VIDEO_TS", path[len-1]) == 0))
+                       {
+                               label = g_strdup(path[len-2]);
+                               if (uppers_and_unders(label))
+                               {
+                                       camel_convert(label);
+                               }
+                       }
+                       else if (len > 0)
+                       {
+                               if (path[len-1][0] != 0)
+                               {
+                                       label = g_strdup(path[len-1]);
+                                       if (uppers_and_unders(label))
+                                       {
+                                               camel_convert(label);
+                                       }
+                               }
+                               else
+                                       label = g_strdup("new_video");
+                       }
+                       else
+                               label = g_strdup("new_video");
+                       g_strfreev (path);
                }
-               g_strfreev (path);
        }
        else
        {
@@ -458,7 +738,8 @@ update_source_label(signal_user_data_t *ud, const gchar *source)
                gtk_label_set_text (GTK_LABEL(widget), label);
                ghb_settings_set_string(ud->settings, "volume_label", label);
                g_free(label);
-               set_destination(ud);
+               if (update_dest)
+                       set_destination(ud);
        }
        else
        {
@@ -470,16 +751,18 @@ update_source_label(signal_user_data_t *ud, const gchar *source)
        return TRUE;
 }
 
-void
+G_MODULE_EXPORT void
 chooser_file_selected_cb(GtkFileChooser *dialog, signal_user_data_t *ud)
 {
-       const gchar *name = gtk_file_chooser_get_filename (dialog);
+       gchar *name = gtk_file_chooser_get_filename (dialog);
        GtkTreeModel *store;
        GtkTreeIter iter;
        const gchar *device;
        gboolean foundit = FALSE;
        GtkComboBox *combo;
        
+       g_debug("chooser_file_selected_cb ()");
+
        if (name == NULL) return;
        combo = GTK_COMBO_BOX(GHB_WIDGET(ud->builder, "source_device"));
        store = gtk_combo_box_get_model(combo);
@@ -499,89 +782,121 @@ chooser_file_selected_cb(GtkFileChooser *dialog, signal_user_data_t *ud)
                gtk_combo_box_set_active_iter (combo, &iter);
        else
                gtk_combo_box_set_active (combo, 0);
+
+       g_free(name);
 }
 
-void
+G_MODULE_EXPORT void
 dvd_device_changed_cb(GtkComboBox *combo, signal_user_data_t *ud)
 {
        GtkWidget *dialog;
        gint ii;
 
+       g_debug("dvd_device_changed_cb ()");
        ii = gtk_combo_box_get_active (combo);
        if (ii > 0)
        {
-               const gchar *device, *name;
+               const gchar *device;
+               gchar *name;
 
                dialog = GHB_WIDGET(ud->builder, "source_dialog");
                device = gtk_combo_box_get_active_text (combo);
                name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog));
                if (name == NULL || strcmp(name, device) != 0)
                        gtk_file_chooser_select_filename (GTK_FILE_CHOOSER(dialog), device);
-       }
-}
-
-void
-source_type_changed_cb(GtkToggleButton *toggle, signal_user_data_t *ud)
-{
-       gchar *folder;
-       GtkFileChooser *chooser;
-       GtkWidget *dvd_device_combo;
-       
-       g_debug("source_type_changed_cb ()");
-       chooser = GTK_FILE_CHOOSER(GHB_WIDGET(ud->builder, "source_dialog"));
-       dvd_device_combo = GHB_WIDGET(ud->builder, "source_device");
-       folder = gtk_file_chooser_get_current_folder (chooser);
-       if (gtk_toggle_button_get_active (toggle))
-       {
-               gtk_file_chooser_set_action (chooser, 
-                                                                       GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
-               gtk_widget_set_sensitive (dvd_device_combo, FALSE);
-               gtk_combo_box_set_active (GTK_COMBO_BOX(dvd_device_combo), 0);
-       }
-       else
-       {
-               gtk_file_chooser_set_action (chooser, GTK_FILE_CHOOSER_ACTION_OPEN);
-               gtk_widget_set_sensitive (dvd_device_combo, TRUE);
-       }
-       if (folder != NULL)
-       {
-               gtk_file_chooser_set_current_folder(chooser, folder);
-               g_free(folder);
+               if (name != NULL)
+                       g_free(name);
        }
 }
 
 static void
 source_dialog_extra_widgets(
        signal_user_data_t *ud,
-       GtkWidget *dialog, 
-       gboolean checkbutton_active)
+       GtkWidget *dialog)
 {
-       GtkToggleButton *checkbutton;
        GtkComboBox *combo;
        GList *drives, *link;
        
-       checkbutton = GTK_TOGGLE_BUTTON(
-               GHB_WIDGET(ud->builder, "source_folder_flag"));
-       gtk_toggle_button_set_active(checkbutton, checkbutton_active);
+       g_debug("source_dialog_extra_widgets ()");
        combo = GTK_COMBO_BOX(GHB_WIDGET(ud->builder, "source_device"));
-       gtk_list_store_clear(GTK_LIST_STORE(
-                                               gtk_combo_box_get_model(combo)));
+       gtk_list_store_clear(GTK_LIST_STORE(gtk_combo_box_get_model(combo)));
 
        link = drives = dvd_device_list();
        gtk_combo_box_append_text (combo, "Not Selected");
        while (link != NULL)
        {
-               gchar *name = (gchar*)link->data;
+               gchar *name = get_dvd_device_name(link->data);
                gtk_combo_box_append_text(combo, name);
                g_free(name);
+               free_drive(link->data);
                link = link->next;
        }
        g_list_free(drives);
+       gtk_combo_box_set_active (combo, 0);
 }
 
 extern GValue *ghb_queue_edit_settings;
 static gchar *last_scan_file = NULL;
 
+static void 
+show_scan_progress(signal_user_data_t *ud)
+{
+       GtkProgressBar *progress;
+       GtkLabel *label;
+
+       progress = GTK_PROGRESS_BAR(GHB_WIDGET(ud->builder, "scan_prog"));
+       gtk_progress_bar_set_fraction (progress, 0);
+       gtk_widget_show(GTK_WIDGET(progress));
+
+       label = GTK_LABEL(GHB_WIDGET(ud->builder, "source_title"));
+       gtk_label_set_text( label, "Scanning ..." );
+}
+
+static void
+start_scan(
+       signal_user_data_t *ud, 
+       const gchar *path, 
+       gint titlenum, 
+       gint preview_count)
+{
+       GtkWidget *widget;
+       GtkAction *action;
+       ghb_status_t status;
+
+       ghb_get_status(&status);
+       if (status.scan.state != GHB_STATE_IDLE)
+               return;
+
+       widget = GHB_WIDGET(ud->builder, "sourcetoolbutton");
+       gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(widget), "hb-stop");
+       gtk_tool_button_set_label(GTK_TOOL_BUTTON(widget), "Stop Scan");
+       gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(widget), "Stop Scan");
+       //gtk_widget_set_sensitive(widget, FALSE);
+
+       action = GHB_ACTION(ud->builder, "source_action");
+       gtk_action_set_sensitive(action, FALSE);
+       action = GHB_ACTION(ud->builder, "source_single_action");
+       gtk_action_set_sensitive(action, FALSE);
+       ghb_backend_scan(path, titlenum, preview_count, 
+                       90000L * ghb_settings_get_int64(ud->settings, "MinTitleDuration"));
+}
+
+gboolean
+ghb_idle_scan(signal_user_data_t *ud)
+{
+       gchar *path;
+       gint preview_count;
+
+       show_scan_progress(ud);
+       path = ghb_settings_get_string( ud->settings, "scan_source");
+       prune_logs(ud);
+
+       preview_count = ghb_settings_get_int(ud->settings, "preview_count");
+       start_scan(ud, path, 0, preview_count);
+       g_free(path);
+       return FALSE;
+}
+
 void
 ghb_do_scan(
        signal_user_data_t *ud, 
@@ -589,20 +904,17 @@ ghb_do_scan(
        gint titlenum, 
        gboolean force)
 {
+       g_debug("ghb_do_scan()");
        if (!force && last_scan_file != NULL &&
                strcmp(last_scan_file, filename) == 0)
        {
                if (ghb_queue_edit_settings)
                {
-                       gint jstatus;
-
-                       jstatus = ghb_settings_get_int(ghb_queue_edit_settings, "job_status");
                        ghb_settings_to_ui(ud, ghb_queue_edit_settings);
                        ghb_set_audio(ud, ghb_queue_edit_settings);
-                       if (jstatus == GHB_QUEUE_PENDING)
-                       {
-                               ghb_value_free(ghb_queue_edit_settings);
-                       }
+                       ghb_reset_subtitles(ud, ghb_queue_edit_settings);
+                       reset_chapter_list(ud, ghb_queue_edit_settings);
+                       ghb_value_free(ghb_queue_edit_settings);
                        ghb_queue_edit_settings = NULL;
                }
                return;
@@ -613,19 +925,18 @@ ghb_do_scan(
        if (filename != NULL)
        {
                last_scan_file = g_strdup(filename);
-               ghb_settings_set_string(ud->settings, "source", filename);
-               if (update_source_label(ud, filename))
+               ghb_settings_set_string(ud->settings, "scan_source", filename);
+               if (update_source_label(ud, filename, TRUE))
                {
-                       GtkProgressBar *progress;
-                       progress = GTK_PROGRESS_BAR(GHB_WIDGET(ud->builder, "progressbar"));
                        gchar *path;
-                       path = ghb_settings_get_string( ud->settings, "source");
-                       gtk_progress_bar_set_fraction (progress, 0);
-                       gtk_progress_bar_set_text (progress, "Scanning ...");
-                       prune_logs(ud);
                        gint preview_count;
+
+                       show_scan_progress(ud);
+                       path = ghb_settings_get_string( ud->settings, "scan_source");
+                       prune_logs(ud);
+
                        preview_count = ghb_settings_get_int(ud->settings, "preview_count");
-                       ghb_backend_scan(path, titlenum, preview_count);
+                       start_scan(ud, path, titlenum, preview_count);
                        g_free(path);
                }
                else
@@ -635,41 +946,15 @@ ghb_do_scan(
        }
 }
 
-static gboolean 
-update_source_name(gpointer data)
-{
-       signal_user_data_t *ud = (signal_user_data_t*)data;
-       GtkWidget *dialog;
-       gchar *sourcename;
-
-       sourcename = ghb_settings_get_string(ud->settings, "source");
-       dialog = GHB_WIDGET(ud->builder, "source_dialog");
-       gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), sourcename);
-       g_free(sourcename);
-       return FALSE;
-}
-
 static void
 do_source_dialog(GtkButton *button, gboolean single, signal_user_data_t *ud)
 {
        GtkWidget *dialog;
        gchar *sourcename;
        gint    response;
-       GtkFileChooserAction action;
-       gboolean checkbutton_active;
 
        g_debug("source_browse_clicked_cb ()");
-       sourcename = ghb_settings_get_string(ud->settings, "source");
-       checkbutton_active = FALSE;
-       if (g_file_test(sourcename, G_FILE_TEST_IS_DIR))
-       {
-               action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
-               checkbutton_active = TRUE;
-       }
-       else
-       {
-               action = GTK_FILE_CHOOSER_ACTION_OPEN;
-       }
+       sourcename = ghb_settings_get_string(ud->settings, "scan_source");
        GtkWidget *widget;
        widget = GHB_WIDGET(ud->builder, "single_title_box");
        if (single)
@@ -677,18 +962,13 @@ do_source_dialog(GtkButton *button, gboolean single, signal_user_data_t *ud)
        else
                gtk_widget_hide(widget);
        dialog = GHB_WIDGET(ud->builder, "source_dialog");
-       source_dialog_extra_widgets(ud, dialog, checkbutton_active);
-       gtk_file_chooser_set_action(GTK_FILE_CHOOSER(dialog), action);
-       // Updating the filename in the file chooser dialog doesn't seem
-       // to work unless the dialog is running for some reason.
-       // So handle it in an "idle" event.
-       //gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), sourcename);
-       g_idle_add((GSourceFunc)update_source_name, ud);
+       source_dialog_extra_widgets(ud, dialog);
+       gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), sourcename);
        response = gtk_dialog_run(GTK_DIALOG (dialog));
        gtk_widget_hide(dialog);
-       if (response == GTK_RESPONSE_ACCEPT)
+       if (response == GTK_RESPONSE_NO)
        {
-               char *filename;
+               gchar *filename;
 
                filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
                if (filename != NULL)
@@ -713,26 +993,35 @@ do_source_dialog(GtkButton *button, gboolean single, signal_user_data_t *ud)
        g_free(sourcename);
 }
 
-void
+G_MODULE_EXPORT void
 source_button_clicked_cb(GtkButton *button, signal_user_data_t *ud)
 {
-       do_source_dialog(button, FALSE, ud);
+       ghb_status_t status;
+       ghb_get_status(&status);
+       if (status.scan.state & GHB_STATE_SCANNING)
+       {
+               ghb_backend_scan_stop();
+       }
+       else
+       {
+               do_source_dialog(button, FALSE, ud);
+       }
 }
 
-void
+G_MODULE_EXPORT void
 single_title_source_cb(GtkButton *button, signal_user_data_t *ud)
 {
        do_source_dialog(button, TRUE, ud);
 }
 
-void
+G_MODULE_EXPORT void
 dvd_source_activate_cb(GtkAction *action, signal_user_data_t *ud)
 {
        const gchar *filename;
        gchar *sourcename;
 
-       sourcename = ghb_settings_get_string(ud->settings, "source");
-       filename = gtk_action_get_name(action);
+       sourcename = ghb_settings_get_string(ud->settings, "scan_source");
+       filename = gtk_buildable_get_name(GTK_BUILDABLE(action));
        ghb_do_scan(ud, filename, 0, TRUE);
        if (strcmp(sourcename, filename) != 0)
        {
@@ -743,17 +1032,24 @@ dvd_source_activate_cb(GtkAction *action, signal_user_data_t *ud)
        g_free(sourcename);
 }
 
-static void
-update_destination_extension(signal_user_data_t *ud)
+void
+ghb_update_destination_extension(signal_user_data_t *ud)
 {
-       static gchar *containers[] = {".mkv", ".mp4", ".m4v", ".avi", ".ogm", NULL};
+       static gchar *containers[] = {".mkv", ".mp4", ".m4v", NULL};
        gchar *filename;
-       gchar *extension;
+       const gchar *extension;
        gint ii;
        GtkEntry *entry;
+       static gboolean busy = FALSE;
 
-       g_debug("update_destination_extension ()");
-       extension = ghb_settings_get_string(ud->settings, "FileFormat");
+       g_debug("ghb_update_destination_extension ()");
+       // Since this function modifies the thing that triggers it's
+       // invocation, check to see if busy to prevent accidental infinite
+       // recursion.
+       if (busy)
+               return;
+       busy = TRUE;
+       extension = get_extension(ud);
        entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "dest_file"));
        filename = g_strdup(gtk_entry_get_text(entry));
        for (ii = 0; containers[ii] != NULL; ii++)
@@ -781,8 +1077,8 @@ update_destination_extension(signal_user_data_t *ud)
                        break;
                }
        }
-       g_free(extension);
        g_free(filename);
+       busy = FALSE;
 }
 
 static void
@@ -801,7 +1097,7 @@ destination_select_title(GtkEntry *entry)
        }
        for (start = end; start >= 0; start--)
        {
-               if (dest[start] == '/')
+               if (dest[start] == G_DIR_SEPARATOR)
                {
                        start++;
                        break;
@@ -814,7 +1110,7 @@ destination_select_title(GtkEntry *entry)
        }
 }
 
-gboolean
+G_MODULE_EXPORT gboolean
 destination_grab_cb(
        GtkEntry *entry, 
        signal_user_data_t *ud)
@@ -825,7 +1121,7 @@ destination_grab_cb(
 
 static gboolean update_default_destination = FALSE;
 
-void
+G_MODULE_EXPORT void
 dest_dir_set_cb(GtkFileChooserButton *dest_chooser, signal_user_data_t *ud)
 {
        gchar *dest_file, *dest_dir, *dest;
@@ -834,7 +1130,7 @@ dest_dir_set_cb(GtkFileChooserButton *dest_chooser, signal_user_data_t *ud)
        ghb_widget_to_setting(ud->settings, (GtkWidget*)dest_chooser);
        dest_file = ghb_settings_get_string(ud->settings, "dest_file");
        dest_dir = ghb_settings_get_string(ud->settings, "dest_dir");
-       dest = g_strdup_printf("%s/%s", dest_dir, dest_file);
+       dest = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dest_dir, dest_file);
        ghb_settings_set_string(ud->settings, "destination", dest);
        g_free(dest_file);
        g_free(dest_dir);
@@ -842,19 +1138,19 @@ dest_dir_set_cb(GtkFileChooserButton *dest_chooser, signal_user_data_t *ud)
        update_default_destination = TRUE;
 }
 
-void
+G_MODULE_EXPORT void
 dest_file_changed_cb(GtkEntry *entry, signal_user_data_t *ud)
 {
        gchar *dest_file, *dest_dir, *dest;
        
        g_debug("dest_file_changed_cb ()");
-       update_destination_extension(ud);
+       ghb_update_destination_extension(ud);
        ghb_widget_to_setting(ud->settings, (GtkWidget*)entry);
        // This signal goes off with ever keystroke, so I'm putting this
        // update on the timer.
        dest_file = ghb_settings_get_string(ud->settings, "dest_file");
        dest_dir = ghb_settings_get_string(ud->settings, "dest_dir");
-       dest = g_strdup_printf("%s/%s", dest_dir, dest_file);
+       dest = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dest_dir, dest_file);
        ghb_settings_set_string(ud->settings, "destination", dest);
        g_free(dest_file);
        g_free(dest_dir);
@@ -862,7 +1158,7 @@ dest_file_changed_cb(GtkEntry *entry, signal_user_data_t *ud)
        update_default_destination = TRUE;
 }
 
-void
+G_MODULE_EXPORT void
 destination_browse_clicked_cb(GtkButton *button, signal_user_data_t *ud)
 {
        GtkWidget *dialog;
@@ -902,7 +1198,7 @@ destination_browse_clicked_cb(GtkButton *button, signal_user_data_t *ud)
        gtk_widget_destroy(dialog);
 }
 
-gboolean
+G_MODULE_EXPORT gboolean
 window_destroy_event_cb(GtkWidget *widget, GdkEvent *event, signal_user_data_t *ud)
 {
        g_debug("window_destroy_event_cb ()");
@@ -912,14 +1208,14 @@ window_destroy_event_cb(GtkWidget *widget, GdkEvent *event, signal_user_data_t *
        return FALSE;
 }
 
-gboolean
+G_MODULE_EXPORT gboolean
 window_delete_event_cb(GtkWidget *widget, GdkEvent *event, signal_user_data_t *ud)
 {
        gint state = ghb_get_queue_state();
        g_debug("window_delete_event_cb ()");
-       if (state & GHB_STATE_WORKING)
+       if (state & (GHB_STATE_WORKING|GHB_STATE_SEARCHING))
        {
-               if (ghb_cancel_encode("Closing HandBrake will terminate encoding.\n"))
+               if (ghb_cancel_encode2(ud, "Closing HandBrake will terminate encoding.\n"))
                {
                        ghb_hb_cleanup(FALSE);
                        prune_logs(ud);
@@ -940,30 +1236,18 @@ update_acodec_combo(signal_user_data_t *ud)
        ghb_grey_combo_options (ud->builder);
 }
 
-void
+G_MODULE_EXPORT void
 container_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
 {
-       const GValue *audio_list;
        g_debug("container_changed_cb ()");
        ghb_widget_to_setting(ud->settings, widget);
-       update_destination_extension(ud);
-       ghb_check_dependency(ud, widget);
+       ghb_check_dependency(ud, widget, NULL);
        update_acodec_combo(ud);
+       ghb_update_destination_extension(ud);
        ghb_clear_presets_selection(ud);
        ghb_live_reset(ud);
-
-       audio_list = ghb_settings_get_value(ud->settings, "audio_list");
-       if (ghb_ac3_in_audio_list (audio_list))
-       {
-               gchar *container;
-
-               container = ghb_settings_get_string(ud->settings, "FileFormat");
-               if (strcmp(container, "mp4") == 0)
-               {
-                       ghb_ui_update(ud, "FileFormat", ghb_string_value("m4v"));
-               }
-               g_free(container);
-       }
+       ghb_subtitle_prune(ud);
+       ghb_audio_list_refresh_selected(ud);
 }
 
 static gchar*
@@ -992,24 +1276,88 @@ get_rate_string(gint rate_base, gint rate)
        rate_s = g_strdup_printf("%.6g", rate_f);
        return rate_s;
 }
+
 static void
-show_title_info(signal_user_data_t *ud, ghb_title_info_t *tinfo)
+update_title_duration(signal_user_data_t *ud)
 {
-       GtkWidget *widget;
+       gint ti;
+       gint hh, mm, ss, start, end;
        gchar *text;
+       GtkWidget *widget;
 
+       ti = ghb_settings_combo_int(ud->settings, "title");
        widget = GHB_WIDGET (ud->builder, "title_duration");
-       if (tinfo->duration != 0)
+
+       if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0)
        {
-               text = g_strdup_printf ("%02d:%02d:%02d", tinfo->hours, 
-                               tinfo->minutes, tinfo->seconds);
+               start = ghb_settings_get_int(ud->settings, "start_point");
+               end = ghb_settings_get_int(ud->settings, "end_point");
+               ghb_part_duration(ti, start, end, &hh, &mm, &ss);
        }
-       else
+       else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1)
+       {
+               gint duration;
+
+               start = ghb_settings_get_int(ud->settings, "start_point");
+               end = ghb_settings_get_int(ud->settings, "end_point");
+               duration = end - start;
+               hh = duration / (60*60);
+               mm = (duration / 60) % 60;
+               ss = duration % 60;
+       }
+       else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 2)
        {
-               text = g_strdup_printf ("Unknown");
+               ghb_title_info_t tinfo;
+
+               if (ghb_get_title_info (&tinfo, ti))
+               {
+                       gint64 frames;
+                       gint duration;
+
+                       start = ghb_settings_get_int(ud->settings, "start_point");
+                       end = ghb_settings_get_int(ud->settings, "end_point");
+                       frames = end - start + 1;
+                       duration = frames * tinfo.rate_base / tinfo.rate;
+                       hh = duration / (60*60);
+                       mm = (duration / 60) % 60;
+                       ss = duration % 60;
+               }
+               else
+               {
+                       hh = mm = ss = 0;
+               }
        }
+       text = g_strdup_printf("%02d:%02d:%02d", hh, mm, ss);
        gtk_label_set_text (GTK_LABEL(widget), text);
        g_free(text);
+}
+
+static void
+show_title_info(signal_user_data_t *ud, ghb_title_info_t *tinfo)
+{
+       GtkWidget *widget;
+       gchar *text;
+
+       ghb_settings_set_string(ud->settings, "source", tinfo->path);
+       if (tinfo->type == HB_STREAM_TYPE)
+       {
+               GtkWidget *widget = GHB_WIDGET (ud->builder, "source_title");
+               if (tinfo->name != NULL && tinfo->name[0] != 0)
+               {
+                       gtk_label_set_text (GTK_LABEL(widget), tinfo->name);
+                       ghb_settings_set_string(ud->settings, "volume_label", tinfo->name);
+                       set_destination(ud);
+               }
+               else
+               {
+                       gchar *label = "No Title Found";
+                       gtk_label_set_text (GTK_LABEL(widget), label);
+                       ghb_settings_set_string(ud->settings, "volume_label", label);
+               }
+       }
+       ud->dont_clear_presets = TRUE;
+       ud->scale_busy = TRUE;
+       update_title_duration(ud);
        widget = GHB_WIDGET (ud->builder, "source_dimensions");
        text = g_strdup_printf ("%d x %d", tinfo->width, tinfo->height);
        gtk_label_set_text (GTK_LABEL(widget), text);
@@ -1026,13 +1374,17 @@ show_title_info(signal_user_data_t *ud, ghb_title_info_t *tinfo)
        gtk_label_set_text (GTK_LABEL(widget), text);
        g_free(text);
 
+       //widget = GHB_WIDGET (ud->builder, "source_interlaced");
+       //gtk_label_set_text (GTK_LABEL(widget), tinfo->interlaced ? "Yes" : "No");
+
        ghb_ui_update(ud, "scale_width", 
                ghb_int64_value(tinfo->width - tinfo->crop[2] - tinfo->crop[3]));
        // If anamorphic or keep_aspect, the hight will be automatically calculated
-       gboolean keep_aspect, anamorphic;
+       gboolean keep_aspect;
+       gint pic_par;
        keep_aspect = ghb_settings_get_boolean(ud->settings, "PictureKeepRatio");
-       anamorphic = ghb_settings_get_boolean(ud->settings, "anamorphic");
-       if (!(keep_aspect || anamorphic))
+       pic_par = ghb_settings_combo_int(ud->settings, "PicturePAR");
+       if (!(keep_aspect || pic_par) || pic_par == 3)
        {
                ghb_ui_update(ud, "scale_height", 
                        ghb_int64_value(tinfo->height - tinfo->crop[0] - tinfo->crop[1]));
@@ -1041,12 +1393,12 @@ show_title_info(signal_user_data_t *ud, ghb_title_info_t *tinfo)
        // Set the limits of cropping.  hb_set_anamorphic_size crashes if
        // you pass it a cropped width or height == 0.
        gint bound;
-       bound = tinfo->height / 2 - 2;
+       bound = tinfo->height / 2 - 8;
        widget = GHB_WIDGET (ud->builder, "PictureTopCrop");
        gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
        widget = GHB_WIDGET (ud->builder, "PictureBottomCrop");
        gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
-       bound = tinfo->width / 2 - 2;
+       bound = tinfo->width / 2 - 8;
        widget = GHB_WIDGET (ud->builder, "PictureLeftCrop");
        gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
        widget = GHB_WIDGET (ud->builder, "PictureRightCrop");
@@ -1058,7 +1410,8 @@ show_title_info(signal_user_data_t *ud, ghb_title_info_t *tinfo)
                ghb_ui_update(ud, "PictureLeftCrop", ghb_int64_value(tinfo->crop[2]));
                ghb_ui_update(ud, "PictureRightCrop", ghb_int64_value(tinfo->crop[3]));
        }
-       ghb_set_scale (ud, GHB_SCALE_KEEP_NONE);
+       ud->scale_busy = FALSE;
+       ghb_set_scale (ud, GHB_PIC_KEEP_PAR|GHB_PIC_USE_MAX);
        gint width, height, crop[4];
        crop[0] = ghb_settings_get_int(ud->settings, "PictureTopCrop");
        crop[1] = ghb_settings_get_int(ud->settings, "PictureBottomCrop");
@@ -1071,18 +1424,52 @@ show_title_info(signal_user_data_t *ud, ghb_title_info_t *tinfo)
        gtk_label_set_text (GTK_LABEL(widget), text);
        g_free(text);
 
-       g_debug("setting max end chapter %d", tinfo->num_chapters);
-       widget = GHB_WIDGET (ud->builder, "end_chapter");
-       gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, tinfo->num_chapters);
-       gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), tinfo->num_chapters);
-       widget = GHB_WIDGET (ud->builder, "start_chapter");
+
+       gint duration = tinfo->duration / 90000;
+
+       if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0)
+       {
+               widget = GHB_WIDGET (ud->builder, "start_point");
+               gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, tinfo->num_chapters);
+               gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), 1);
+
+               widget = GHB_WIDGET (ud->builder, "end_point");
+               gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, tinfo->num_chapters);
+               gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), tinfo->num_chapters);
+       }
+       else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1)
+       {
+
+               widget = GHB_WIDGET (ud->builder, "start_point");
+               gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, duration-1);
+               gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), 0);
+
+               widget = GHB_WIDGET (ud->builder, "end_point");
+               gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, duration);
+               gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), duration);
+       }
+       else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 2)
+       {
+               gdouble max_frames = (gdouble)duration * tinfo->rate / tinfo->rate_base;
+               widget = GHB_WIDGET (ud->builder, "start_point");
+               gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, max_frames);
+               gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), 1);
+
+               widget = GHB_WIDGET (ud->builder, "end_point");
+               gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, max_frames);
+               gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), max_frames);
+       }
+
+       widget = GHB_WIDGET (ud->builder, "angle");
        gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), 1);
-       gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, tinfo->num_chapters);
+       gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, tinfo->angle_count);
+       ghb_settings_set_int(ud->settings, "angle_count", tinfo->angle_count);
+       ud->dont_clear_presets = FALSE;
 }
 
 static gboolean update_preview = FALSE;
 
-void
+G_MODULE_EXPORT void
 title_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
 {
        ghb_title_info_t tinfo;
@@ -1090,25 +1477,20 @@ title_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        
        g_debug("title_changed_cb ()");
        ghb_widget_to_setting(ud->settings, widget);
-       ghb_check_dependency(ud, widget);
 
        titleindex = ghb_settings_combo_int(ud->settings, "title");
-       ghb_update_ui_combo_box (ud->builder, "AudioTrack", titleindex, FALSE);
-       ghb_update_ui_combo_box (ud->builder, "Subtitles", titleindex, FALSE);
+       ghb_update_ui_combo_box (ud, "AudioTrack", titleindex, FALSE);
+       ghb_update_ui_combo_box (ud, "SubtitleTrack", titleindex, FALSE);
 
-       ghb_update_from_preset(ud, "Subtitles");
        if (ghb_get_title_info (&tinfo, titleindex))
        {
                show_title_info(ud, &tinfo);
        }
+       ghb_check_dependency(ud, widget, NULL);
        update_chapter_list (ud);
        ghb_adjust_audio_rate_combos(ud);
        ghb_set_pref_audio(titleindex, ud);
-       if (ghb_settings_get_boolean(ud->settings, "vquality_type_target"))
-       {
-               gint bitrate = ghb_calculate_target_bitrate (ud->settings, titleindex);
-               ghb_ui_update(ud, "VideoAvgBitrate", ghb_int64_value(bitrate));
-       }
+       ghb_set_pref_subtitle(titleindex, ud);
 
        // Unfortunately, there is no way to query how many frames were
        // actually generated during the scan.
@@ -1118,7 +1500,7 @@ title_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        gint preview_count;
        preview_count = ghb_settings_get_int(ud->settings, "preview_count");
        widget = GHB_WIDGET(ud->builder, "preview_frame");
-       gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, preview_count);
+       gtk_range_set_range (GTK_RANGE(widget), 1, preview_count);
        ghb_ui_update(ud, "preview_frame", ghb_int64_value(2));
 
        ghb_set_preview_image (ud);
@@ -1126,22 +1508,115 @@ title_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        {
                set_destination(ud);
        }
+       ghb_preview_set_visible(ud);
+
+       gint end;
+       widget = GHB_WIDGET (ud->builder, "ChapterMarkers");
+       end = ghb_settings_get_int(ud->settings, "end_point");
+       if (1 == end)
+       {
+               gtk_widget_set_sensitive(widget, FALSE);
+       }
+       else
+       {
+               gtk_widget_set_sensitive(widget, TRUE);
+       }
 }
 
-void
+G_MODULE_EXPORT void
+ptop_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
+{
+       gint ti;
+       ghb_title_info_t tinfo;
+
+       ghb_widget_to_setting(ud->settings, widget);
+       ghb_check_dependency(ud, widget, NULL);
+       ghb_live_reset(ud);
+
+       ti = ghb_settings_combo_int(ud->settings, "title");
+       if (!ghb_get_title_info (&tinfo, ti))
+               return;
+
+       gint duration = tinfo.duration / 90000;
+       if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0)
+       {
+               widget = GHB_WIDGET (ud->builder, "start_point");
+               gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, tinfo.num_chapters);
+               gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), 1);
+
+               widget = GHB_WIDGET (ud->builder, "end_point");
+               gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, tinfo.num_chapters);
+               gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), tinfo.num_chapters);
+       }
+       else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1)
+       {
+               widget = GHB_WIDGET (ud->builder, "start_point");
+               gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, duration-1);
+               gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), 0);
+
+               widget = GHB_WIDGET (ud->builder, "end_point");
+               gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, duration);
+               gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), duration);
+       }
+       else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 2)
+       {
+               gdouble max_frames = (gdouble)duration * tinfo.rate / tinfo.rate_base;
+               widget = GHB_WIDGET (ud->builder, "start_point");
+               gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, max_frames);
+               gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), 1);
+
+               widget = GHB_WIDGET (ud->builder, "end_point");
+               gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, max_frames);
+               gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), max_frames);
+       }
+}
+
+G_MODULE_EXPORT void
+framerate_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
+{
+       ghb_widget_to_setting(ud->settings, widget);
+
+       if (ghb_settings_combo_int(ud->settings, "VideoFramerate") != 0)
+       {
+               if (!ghb_settings_get_boolean(ud->settings, "VideoFrameratePFR"))
+        {
+                   ghb_ui_update(ud, "VideoFramerateCFR", ghb_boolean_value(TRUE));
+        }
+       }
+       if (ghb_settings_combo_int(ud->settings, "VideoFramerate") == 0 &&
+               ghb_settings_get_boolean(ud->settings, "VideoFrameratePFR"))
+       {
+               ghb_ui_update(ud, "VideoFramerateVFR", ghb_boolean_value(TRUE));
+       }
+       ghb_check_dependency(ud, widget, NULL);
+       ghb_clear_presets_selection(ud);
+       ghb_live_reset(ud);
+}
+
+G_MODULE_EXPORT void
 setting_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
 {
        ghb_widget_to_setting(ud->settings, widget);
-       ghb_check_dependency(ud, widget);
+       ghb_check_dependency(ud, widget, NULL);
        ghb_clear_presets_selection(ud);
        ghb_live_reset(ud);
 }
 
-void
+G_MODULE_EXPORT void
+chapter_markers_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
+{
+       ghb_widget_to_setting(ud->settings, widget);
+       ghb_check_dependency(ud, widget, NULL);
+       ghb_clear_presets_selection(ud);
+       ghb_live_reset(ud);
+       ghb_update_destination_extension(ud);
+}
+
+G_MODULE_EXPORT void
 vquality_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
 {
        ghb_widget_to_setting(ud->settings, widget);
-       ghb_check_dependency(ud, widget);
+       ghb_check_dependency(ud, widget, NULL);
        ghb_clear_presets_selection(ud);
        ghb_live_reset(ud);
 
@@ -1161,18 +1636,18 @@ vquality_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        gtk_range_set_value(GTK_RANGE(widget), val);
 }
 
-void
+G_MODULE_EXPORT void
 http_opt_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
 {
        ghb_widget_to_setting(ud->settings, widget);
-       ghb_check_dependency(ud, widget);
+       ghb_check_dependency(ud, widget, NULL);
        ghb_clear_presets_selection(ud);
        ghb_live_reset(ud);
        // AC3 is not allowed when Web optimized
        ghb_grey_combo_options (ud->builder);
 }
 
-void
+G_MODULE_EXPORT void
 vcodec_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
 {
        gdouble vqmin, vqmax, step, page;
@@ -1180,7 +1655,7 @@ vcodec_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        gint digits;
 
        ghb_widget_to_setting(ud->settings, widget);
-       ghb_check_dependency(ud, widget);
+       ghb_check_dependency(ud, widget, NULL);
        ghb_clear_presets_selection(ud);
        ghb_live_reset(ud);
        ghb_vquality_range(ud, &vqmin, &vqmax, &step, &page, &digits, &inverted);
@@ -1191,69 +1666,119 @@ vcodec_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        gtk_range_set_inverted (GTK_RANGE(qp), inverted);
 }
 
-void
-target_size_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
-{
-       const gchar *name = gtk_widget_get_name(widget);
-       g_debug("target_size_changed_cb () %s", name);
-       ghb_widget_to_setting(ud->settings, widget);
-       ghb_check_dependency(ud, widget);
-       ghb_clear_presets_selection(ud);
-       ghb_live_reset(ud);
-       if (ghb_settings_get_boolean(ud->settings, "vquality_type_target"))
-       {
-               gint titleindex;
-               titleindex = ghb_settings_combo_int(ud->settings, "title");
-               gint bitrate = ghb_calculate_target_bitrate (ud->settings, titleindex);
-               ghb_ui_update(ud, "VideoAvgBitrate", ghb_int64_value(bitrate));
-       }
-}
-
-void
-start_chapter_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
+G_MODULE_EXPORT void
+start_point_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
 {
        gint start, end;
-       const gchar *name = gtk_widget_get_name(widget);
+       const gchar *name = ghb_get_setting_key(widget);
 
-       g_debug("start_chapter_changed_cb () %s", name);
+       g_debug("start_point_changed_cb () %s", name);
        ghb_widget_to_setting(ud->settings, widget);
-       start = ghb_settings_get_int(ud->settings, "start_chapter");
-       end = ghb_settings_get_int(ud->settings, "end_chapter");
-       if (start > end)
-               ghb_ui_update(ud, "end_chapter", ghb_int_value(start));
-       ghb_check_dependency(ud, widget);
-       if (ghb_settings_get_boolean(ud->settings, "chapters_in_destination"))
+       if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0)
        {
-               set_destination(ud);
+               start = ghb_settings_get_int(ud->settings, "start_point");
+               end = ghb_settings_get_int(ud->settings, "end_point");
+               if (start > end)
+                       ghb_ui_update(ud, "end_point", ghb_int_value(start));
+               ghb_check_dependency(ud, widget, NULL);
+               if (ghb_settings_get_boolean(ud->settings, "chapters_in_destination"))
+               {
+                       set_destination(ud);
+               }
+               widget = GHB_WIDGET (ud->builder, "ChapterMarkers");
+               // End may have been changed above, get it again
+               end = ghb_settings_get_int(ud->settings, "end_point");
+               if (start == end)
+               {
+                       gtk_widget_set_sensitive(widget, FALSE);
+               }
+               else
+               {
+                       gtk_widget_set_sensitive(widget, TRUE);
+               }
+               update_title_duration(ud);
+       }
+       else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1)
+       {
+               start = ghb_settings_get_int(ud->settings, "start_point");
+               end = ghb_settings_get_int(ud->settings, "end_point");
+               if (start >= end)
+                       ghb_ui_update(ud, "end_point", ghb_int_value(start+1));
+               ghb_check_dependency(ud, widget, NULL);
+               update_title_duration(ud);
+       }
+       else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 2)
+       {
+               start = ghb_settings_get_int(ud->settings, "start_point");
+               end = ghb_settings_get_int(ud->settings, "end_point");
+               if (start > end)
+                       ghb_ui_update(ud, "end_point", ghb_int_value(start));
+               ghb_check_dependency(ud, widget, NULL);
+               update_title_duration(ud);
        }
 }
 
-void
-end_chapter_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
+G_MODULE_EXPORT void
+end_point_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
 {
        gint start, end;
-       const gchar *name = gtk_widget_get_name(widget);
+       const gchar *name = ghb_get_setting_key(widget);
 
-       g_debug("end_chapter_changed_cb () %s", name);
+       g_debug("end_point_changed_cb () %s", name);
        ghb_widget_to_setting(ud->settings, widget);
-       start = ghb_settings_get_int(ud->settings, "start_chapter");
-       end = ghb_settings_get_int(ud->settings, "end_chapter");
-       if (start > end)
-               ghb_ui_update(ud, "start_chapter", ghb_int_value(end));
-       ghb_check_dependency(ud, widget);
-       if (ghb_settings_get_boolean(ud->settings, "chapters_in_destination"))
+       if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0)
        {
-               set_destination(ud);
+               start = ghb_settings_get_int(ud->settings, "start_point");
+               end = ghb_settings_get_int(ud->settings, "end_point");
+               if (start > end)
+                       ghb_ui_update(ud, "start_point", ghb_int_value(end));
+               ghb_check_dependency(ud, widget, NULL);
+               if (ghb_settings_get_boolean(ud->settings, "chapters_in_destination"))
+               {
+                       set_destination(ud);
+               }
+               widget = GHB_WIDGET (ud->builder, "ChapterMarkers");
+               // Start may have been changed above, get it again
+               start = ghb_settings_get_int(ud->settings, "start_point");
+               if (start == end)
+               {
+                       gtk_widget_set_sensitive(widget, FALSE);
+               }
+               else
+               {
+                       gtk_widget_set_sensitive(widget, TRUE);
+               }
+               update_title_duration(ud);
+       }
+       else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1)
+       {
+               start = ghb_settings_get_int(ud->settings, "start_point");
+               end = ghb_settings_get_int(ud->settings, "end_point");
+               if (start >= end)
+                       ghb_ui_update(ud, "start_point", ghb_int_value(end-1));
+               ghb_check_dependency(ud, widget, NULL);
+               update_title_duration(ud);
+       }
+       else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 2)
+       {
+               start = ghb_settings_get_int(ud->settings, "start_point");
+               end = ghb_settings_get_int(ud->settings, "end_point");
+               if (start > end)
+                       ghb_ui_update(ud, "start_point", ghb_int_value(end));
+               ghb_check_dependency(ud, widget, NULL);
+               update_title_duration(ud);
        }
 }
 
-void
+G_MODULE_EXPORT void
 scale_width_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
 {
        g_debug("scale_width_changed_cb ()");
        ghb_widget_to_setting(ud->settings, widget);
-       ghb_check_dependency(ud, widget);
-       ghb_set_scale (ud, GHB_SCALE_KEEP_WIDTH);
+       ghb_check_dependency(ud, widget, NULL);
+       ghb_clear_presets_selection(ud);
+       if (GTK_WIDGET_SENSITIVE(widget))
+               ghb_set_scale (ud, GHB_PIC_KEEP_WIDTH);
        update_preview = TRUE;
        gchar *text;
        gint width = ghb_settings_get_int(ud->settings, "scale_width");
@@ -1265,13 +1790,15 @@ scale_width_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        ghb_live_reset(ud);
 }
 
-void
+G_MODULE_EXPORT void
 scale_height_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
 {
        g_debug("scale_height_changed_cb ()");
        ghb_widget_to_setting(ud->settings, widget);
-       ghb_check_dependency(ud, widget);
-       ghb_set_scale (ud, GHB_SCALE_KEEP_HEIGHT);
+       ghb_check_dependency(ud, widget, NULL);
+       ghb_clear_presets_selection(ud);
+       if (GTK_WIDGET_SENSITIVE(widget))
+               ghb_set_scale (ud, GHB_PIC_KEEP_HEIGHT);
        update_preview = TRUE;
        gchar *text;
        gint width = ghb_settings_get_int(ud->settings, "scale_width");
@@ -1283,7 +1810,7 @@ scale_height_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        ghb_live_reset(ud);
 }
 
-void
+G_MODULE_EXPORT void
 crop_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
 {
        gint titleindex, crop[4];
@@ -1291,8 +1818,10 @@ crop_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        
        g_debug("crop_changed_cb ()");
        ghb_widget_to_setting(ud->settings, widget);
-       ghb_check_dependency(ud, widget);
-       ghb_set_scale (ud, GHB_SCALE_KEEP_NONE);
+       ghb_check_dependency(ud, widget, NULL);
+       ghb_clear_presets_selection(ud);
+       if (GTK_WIDGET_SENSITIVE(widget))
+               ghb_set_scale (ud, 0);
 
        crop[0] = ghb_settings_get_int(ud->settings, "PictureTopCrop");
        crop[1] = ghb_settings_get_int(ud->settings, "PictureBottomCrop");
@@ -1309,6 +1838,8 @@ crop_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
                widget = GHB_WIDGET (ud->builder, "crop_dimensions");
                text = g_strdup_printf ("%d x %d", width, height);
                gtk_label_set_text (GTK_LABEL(widget), text);
+               widget = GHB_WIDGET (ud->builder, "crop_dimensions2");
+               gtk_label_set_text (GTK_LABEL(widget), text);
                g_free(text);
        }
        gchar *text;
@@ -1320,15 +1851,58 @@ crop_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        ghb_live_reset(ud);
 }
 
-void
+G_MODULE_EXPORT void
+display_width_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
+{
+       g_debug("display_width_changed_cb ()");
+       ghb_widget_to_setting(ud->settings, widget);
+       ghb_check_dependency(ud, widget, NULL);
+       ghb_clear_presets_selection(ud);
+       ghb_live_reset(ud);
+       if (GTK_WIDGET_SENSITIVE(widget))
+               ghb_set_scale (ud, GHB_PIC_KEEP_DISPLAY_WIDTH);
+
+       update_preview = TRUE;
+}
+
+G_MODULE_EXPORT void
+display_height_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
+{
+       g_debug("display_height_changed_cb ()");
+       ghb_widget_to_setting(ud->settings, widget);
+       ghb_check_dependency(ud, widget, NULL);
+       ghb_clear_presets_selection(ud);
+       ghb_live_reset(ud);
+       if (GTK_WIDGET_SENSITIVE(widget))
+               ghb_set_scale (ud, GHB_PIC_KEEP_DISPLAY_HEIGHT);
+
+       update_preview = TRUE;
+}
+
+G_MODULE_EXPORT void
+par_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
+{
+       g_debug("par_changed_cb ()");
+       ghb_widget_to_setting(ud->settings, widget);
+       ghb_check_dependency(ud, widget, NULL);
+       ghb_clear_presets_selection(ud);
+       ghb_live_reset(ud);
+       if (GTK_WIDGET_SENSITIVE(widget))
+               ghb_set_scale (ud, GHB_PIC_KEEP_PAR);
+
+       update_preview = TRUE;
+}
+
+G_MODULE_EXPORT void
 scale_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
 {
        g_debug("scale_changed_cb ()");
        ghb_widget_to_setting(ud->settings, widget);
-       ghb_check_dependency(ud, widget);
+       ghb_check_dependency(ud, widget, NULL);
        ghb_clear_presets_selection(ud);
        ghb_live_reset(ud);
-       ghb_set_scale (ud, GHB_SCALE_KEEP_NONE);
+       if (GTK_WIDGET_SENSITIVE(widget))
+               ghb_set_scale (ud, 0);
        update_preview = TRUE;
        
        gchar *text;
@@ -1339,12 +1913,41 @@ scale_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        text = ghb_settings_get_boolean(ud->settings, "autoscale") ? "On" : "Off";
        widget = GHB_WIDGET (ud->builder, "scale_auto");
        gtk_label_set_text (GTK_LABEL(widget), text);
-       text = ghb_settings_get_boolean(ud->settings, "anamorphic") ? "On" : "Off";
-       widget = GHB_WIDGET (ud->builder, "scale_anamorphic");
+       switch (ghb_settings_combo_int(ud->settings, "PicturePAR"))
+       {
+               case 0:
+                       text = "Off";
+                       break;
+               case 1:
+                       text = "Strict";
+                       break;
+               case 2:
+                       text = "Loose";
+                       break;
+               case 3:
+                       text = "Custom";
+                       break;
+               default:
+                       text = "Unknown";
+                       break;
+       }
+       widget = GHB_WIDGET (ud->builder, "scale_anamorphic");
        gtk_label_set_text (GTK_LABEL(widget), text);
 }
 
-void
+G_MODULE_EXPORT void
+show_crop_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
+{
+       g_debug("show_crop_changed_cb ()");
+       ghb_widget_to_setting(ud->settings, widget);
+       ghb_check_dependency(ud, widget, NULL);
+       ghb_live_reset(ud);
+       if (GTK_WIDGET_SENSITIVE(widget))
+               ghb_set_scale (ud, 0);
+       update_preview = TRUE;
+}
+
+G_MODULE_EXPORT void
 generic_entry_changed_cb(GtkEntry *entry, signal_user_data_t *ud)
 {
        // Normally (due to user input) I only want to process the entry
@@ -1361,7 +1964,7 @@ generic_entry_changed_cb(GtkEntry *entry, signal_user_data_t *ud)
        }
 }
 
-void
+G_MODULE_EXPORT void
 prefs_dialog_cb(GtkWidget *xwidget, signal_user_data_t *ud)
 {
        GtkWidget *dialog;
@@ -1373,6 +1976,123 @@ prefs_dialog_cb(GtkWidget *xwidget, signal_user_data_t *ud)
        gtk_widget_hide(dialog);
 }
 
+typedef struct
+{
+       GtkMessageDialog *dlg;
+       const gchar *msg;
+       const gchar *action;
+       gint timeout;
+       signal_user_data_t *ud;
+} countdown_t;
+
+static gboolean
+quit_cb(countdown_t *cd)
+{
+       gchar *str;
+
+       cd->timeout--;
+       if (cd->timeout == 0)
+       {
+               ghb_hb_cleanup(FALSE);
+               prune_logs(cd->ud);
+
+               gtk_widget_destroy (GTK_WIDGET(cd->dlg));
+               gtk_main_quit();
+               return FALSE;
+       }
+       str = g_strdup_printf("%s\n\n%s in %d seconds ...", 
+                                                       cd->msg, cd->action, cd->timeout);
+       gtk_message_dialog_set_markup(cd->dlg, str);
+       g_free(str);
+       return TRUE;
+}
+
+static gboolean
+shutdown_cb(countdown_t *cd)
+{
+       gchar *str;
+
+       cd->timeout--;
+       if (cd->timeout == 0)
+       {
+               ghb_hb_cleanup(FALSE);
+               prune_logs(cd->ud);
+
+               ghb_shutdown_gsm();
+               gtk_main_quit();
+               return FALSE;
+       }
+       str = g_strdup_printf("%s\n\n%s in %d seconds ...", 
+                                                       cd->msg, cd->action, cd->timeout);
+       gtk_message_dialog_set_markup(cd->dlg, str);
+       g_free(str);
+       return TRUE;
+}
+
+static gboolean
+suspend_cb(countdown_t *cd)
+{
+       gchar *str;
+
+       cd->timeout--;
+       if (cd->timeout == 0)
+       {
+               gtk_widget_destroy (GTK_WIDGET(cd->dlg));
+               ghb_suspend_gpm();
+               return FALSE;
+       }
+       str = g_strdup_printf("%s\n\n%s in %d seconds ...", 
+                                                       cd->msg, cd->action, cd->timeout);
+       gtk_message_dialog_set_markup(cd->dlg, str);
+       g_free(str);
+       return TRUE;
+}
+
+void
+ghb_countdown_dialog(
+       GtkMessageType type, 
+       const gchar *message, 
+       const gchar *action, 
+       const gchar *cancel, 
+       GSourceFunc action_func,
+       signal_user_data_t *ud,
+       gint timeout)
+{
+       GtkWidget *dialog;
+       GtkResponseType response;
+       guint timeout_id;
+       countdown_t cd;
+                       
+       cd.msg = message;
+       cd.action = action;
+       cd.timeout = timeout;
+       cd.ud = ud;
+
+       // Toss up a warning dialog
+       dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
+                                                       type, GTK_BUTTONS_NONE,
+                                                       "%s\n\n%s in %d seconds ...", 
+                                                       message, action, timeout);
+       gtk_dialog_add_buttons( GTK_DIALOG(dialog), 
+                                                  cancel, GTK_RESPONSE_CANCEL,
+                                                  NULL);
+
+       cd.dlg = GTK_MESSAGE_DIALOG(dialog);
+       timeout_id = g_timeout_add(1000, action_func, &cd);
+       response = gtk_dialog_run(GTK_DIALOG(dialog));
+       gtk_widget_destroy (dialog);
+       if (response == GTK_RESPONSE_CANCEL)
+       {
+               GMainContext *mc;
+               GSource *source;
+
+               mc = g_main_context_default();
+               source = g_main_context_find_source_by_id(mc, timeout_id);
+               if (source != NULL)
+                       g_source_destroy(source);
+       }
+}
+
 gboolean
 ghb_message_dialog(GtkMessageType type, const gchar *message, const gchar *no, const gchar *yes)
 {
@@ -1395,8 +2115,64 @@ ghb_message_dialog(GtkMessageType type, const gchar *message, const gchar *no, c
        return TRUE;
 }
 
+void
+ghb_error_dialog(GtkMessageType type, const gchar *message, const gchar *cancel)
+{
+       GtkWidget *dialog;
+       GtkResponseType response;
+                       
+       // Toss up a warning dialog
+       dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
+                                                       type, GTK_BUTTONS_NONE,
+                                                       "%s", message);
+       gtk_dialog_add_buttons( GTK_DIALOG(dialog), 
+                                                  cancel, GTK_RESPONSE_CANCEL, NULL);
+       response = gtk_dialog_run(GTK_DIALOG(dialog));
+       gtk_widget_destroy (dialog);
+}
+
+void
+ghb_cancel_encode(signal_user_data_t *ud, const gchar *extra_msg)
+{
+       GtkWidget *dialog;
+       GtkResponseType response;
+       
+       if (extra_msg == NULL) extra_msg = "";
+       // Toss up a warning dialog
+       dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
+                               GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE,
+                               "%sYour movie will be lost if you don't continue encoding.",
+                               extra_msg);
+       gtk_dialog_add_buttons( GTK_DIALOG(dialog), 
+                                                  "Cancel Current and Stop", 1,
+                                                  "Cancel Current, Start Next", 2,
+                                                  "Finish Current, then Stop", 3,
+                                                  "Continue Encoding", 4,
+                                                  NULL);
+       response = gtk_dialog_run(GTK_DIALOG(dialog));
+       gtk_widget_destroy (dialog);
+       switch ((int)response)
+       {
+               case 1:
+                       ghb_stop_queue();
+                       ud->cancel_encode = GHB_CANCEL_ALL;
+                       break;
+               case 2:
+                       ghb_stop_queue();
+                       ud->cancel_encode = GHB_CANCEL_CURRENT;
+                       break;
+               case 3:
+                       ud->cancel_encode = GHB_CANCEL_FINISH;
+                       break;
+               case 4:
+               default:
+                       ud->cancel_encode = GHB_CANCEL_NONE;
+                       break;
+       }
+}
+
 gboolean
-ghb_cancel_encode(const gchar *extra_msg)
+ghb_cancel_encode2(signal_user_data_t *ud, const gchar *extra_msg)
 {
        GtkWidget *dialog;
        GtkResponseType response;
@@ -1408,13 +2184,22 @@ ghb_cancel_encode(const gchar *extra_msg)
                                "%sYour movie will be lost if you don't continue encoding.",
                                extra_msg);
        gtk_dialog_add_buttons( GTK_DIALOG(dialog), 
-                                                  "Continue Encoding", GTK_RESPONSE_NO,
-                                                  "Stop Encoding", GTK_RESPONSE_YES, NULL);
+                                                  "Cancel Current and Stop", 1,
+                                                  "Continue Encoding", 4,
+                                                  NULL);
        response = gtk_dialog_run(GTK_DIALOG(dialog));
        gtk_widget_destroy (dialog);
-       if (response == GTK_RESPONSE_NO) return FALSE;
-       ghb_stop_queue();
-       return TRUE;
+       switch ((int)response)
+       {
+               case 1:
+                       ghb_stop_queue();
+                       ud->cancel_encode = GHB_CANCEL_ALL;
+                       return TRUE;
+               case 4:
+               default:
+                       break;
+       }
+       return FALSE;
 }
 
 static void
@@ -1446,13 +2231,19 @@ static void
 prune_logs(signal_user_data_t *ud)
 {
        gchar *dest_dir;
+       gint days;
 
        // Only prune logs stored in the default config dir location
+       days = ghb_settings_combo_int(ud->settings, "LogLongevity");
+       if (days > 365)
+               return;
+
        dest_dir = ghb_get_user_config_dir("EncodeLogs");
        if (g_file_test(dest_dir, G_FILE_TEST_IS_DIR))
        {
                const gchar *file;
-               int week = 7*24*60*60;
+               gint duration = days * 24 * 60 * 60;
+               
                GDir *gdir = g_dir_open(dest_dir, 0, NULL);
                time_t now;
 
@@ -1465,7 +2256,7 @@ prune_logs(signal_user_data_t *ud)
 
                        path = g_strdup_printf("%s/%s", dest_dir, file);
                        g_stat(path, &stbuf);
-                       if (now - stbuf.st_mtime > week)
+                       if (now - stbuf.st_mtime > duration)
                        {
                                g_unlink(path);
                        }
@@ -1505,10 +2296,11 @@ queue_scan(signal_user_data_t *ud, GValue *js)
        {
                *pos = 0;
        }
-       log_path = g_strdup_printf("%s/%d-%02d-%02d %02d-%02d-%02d %s.log",
+       log_path = g_strdup_printf("%s/%s %d-%02d-%02d %02d-%02d-%02d.log",
                dest_dir,
+               basename,
                now->tm_year + 1900, now->tm_mon + 1, now->tm_mday,
-               now->tm_hour, now->tm_min, now->tm_sec, basename);
+               now->tm_hour, now->tm_min, now->tm_sec);
        g_free(basename);
        g_free(dest_dir);
        if (ud->job_activity_log)
@@ -1532,6 +2324,42 @@ queue_scan(signal_user_data_t *ud, GValue *js)
        g_free(path);
 }
 
+static gint
+queue_pending_count(GValue *queue)
+{
+       gint nn, ii, count;
+       GValue *js;
+       gint status;
+
+       nn = 0;
+       count = ghb_array_len(queue);
+       for (ii = 0; ii < count; ii++)
+       {
+
+               js = ghb_array_get_nth(queue, ii);
+               status = ghb_settings_get_int(js, "job_status");
+               if (status == GHB_QUEUE_PENDING)
+               {
+                       nn++;
+               }
+       }
+       return nn;
+}
+
+void
+ghb_update_pending(signal_user_data_t *ud)
+{
+       GtkLabel *label;
+       gint pending;
+       gchar *str;
+
+       label = GTK_LABEL(GHB_WIDGET(ud->builder, "pending_status"));
+       pending = queue_pending_count(ud->queue);
+       str = g_strdup_printf("%d encode(s) pending", pending);
+       gtk_label_set_text(label, str);
+       g_free(str);
+}
+
 GValue* 
 ghb_start_next_job(signal_user_data_t *ud, gboolean find_first)
 {
@@ -1539,8 +2367,12 @@ ghb_start_next_job(signal_user_data_t *ud, gboolean find_first)
        gint count, ii, jj;
        GValue *js;
        gint status;
+       GtkWidget *prog;
 
        g_debug("start_next_job");
+       prog = GHB_WIDGET(ud->builder, "progressbar");
+       gtk_widget_show(prog);
+
        count = ghb_array_len(ud->queue);
        if (find_first)
        {       // Start the first pending item in the queue
@@ -1553,13 +2385,15 @@ ghb_start_next_job(signal_user_data_t *ud, gboolean find_first)
                        if (status == GHB_QUEUE_PENDING)
                        {
                                current = ii;
-                               ghb_inhibit_gpm();
+                               ghb_inhibit_gsm(ud);
                                queue_scan(ud, js);
+                               ghb_update_pending(ud);
                                return js;
                        }
                }
                // Nothing pending
-               ghb_uninhibit_gpm();
+               ghb_uninhibit_gsm();
+               ghb_notify_done(ud);
                return NULL;
        }
        // Find the next pending item after the current running item
@@ -1576,8 +2410,9 @@ ghb_start_next_job(signal_user_data_t *ud, gboolean find_first)
                                if (status == GHB_QUEUE_PENDING)
                                {
                                        current = jj;
-                                       ghb_inhibit_gpm();
+                                       ghb_inhibit_gsm(ud);
                                        queue_scan(ud, js);
+                                       ghb_update_pending(ud);
                                        return js;
                                }
                        }
@@ -1592,13 +2427,17 @@ ghb_start_next_job(signal_user_data_t *ud, gboolean find_first)
                if (status == GHB_QUEUE_PENDING)
                {
                        current = ii;
-                       ghb_inhibit_gpm();
+                       ghb_inhibit_gsm(ud);
                        queue_scan(ud, js);
+                       ghb_update_pending(ud);
                        return js;
                }
        }
        // Nothing found
-       ghb_uninhibit_gpm();
+       ghb_uninhibit_gsm();
+       ghb_notify_done(ud);
+       ghb_update_pending(ud);
+       gtk_widget_hide(prog);
        return NULL;
 }
 
@@ -1611,6 +2450,9 @@ find_queue_job(GValue *queue, gint unique_id, GValue **job)
        
        *job = NULL;
        g_debug("find_queue_job");
+       if (unique_id == 0)  // Invalid Id
+               return -1;
+
        count = ghb_array_len(queue);
        for (ii = 0; ii < count; ii++)
        {
@@ -1632,35 +2474,88 @@ working_status_string(signal_user_data_t *ud, ghb_instance_status_t *status)
        gint qcount;
        gint index;
        GValue *js;
+       gboolean subtitle_scan = FALSE;
 
+       qcount = ghb_array_len(ud->queue);
+       index = find_queue_job(ud->queue, status->unique_id, &js);
+       if (js != NULL)
+       {
+               subtitle_scan = ghb_settings_get_boolean(js, "subtitle_scan");
+       }
+       if (qcount > 1)
+       {
+               job_str = g_strdup_printf("job %d of %d, ", index+1, qcount);
+       }
+       else
+       {
+               job_str = g_strdup("");
+       }
        if (status->job_count > 1)
        {
-               task_str = g_strdup_printf("pass %d of %d, ", 
-                       status->job_cur, status->job_count);
+               if (status->job_cur == 1 && subtitle_scan)
+               {
+                       task_str = g_strdup_printf("pass %d (subtitle scan) of %d, ", 
+                               status->job_cur, status->job_count);
+               }
+               else
+               {
+                       task_str = g_strdup_printf("pass %d of %d, ", 
+                               status->job_cur, status->job_count);
+               }
        }
        else
        {
                task_str = g_strdup("");
        }
+       if(status->seconds > -1)
+       {
+               status_str= g_strdup_printf(
+                       "Encoding: %s%s%.2f %%"
+                       " (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)",
+                       job_str, task_str,
+                       100.0 * status->progress,
+                       status->rate_cur, status->rate_avg, status->hours, 
+                       status->minutes, status->seconds );
+       }
+       else
+       {
+               status_str= g_strdup_printf(
+                       "Encoding: %s%s%.2f %%",
+                       job_str, task_str,
+                       100.0 * status->progress );
+       }
+       g_free(task_str);
+       g_free(job_str);
+       return status_str;
+}
+
+gchar*
+searching_status_string(signal_user_data_t *ud, ghb_instance_status_t *status)
+{
+       gchar *task_str, *job_str, *status_str;
+       gint qcount;
+       gint index;
+       GValue *js;
+
        qcount = ghb_array_len(ud->queue);
+       index = find_queue_job(ud->queue, status->unique_id, &js);
        if (qcount > 1)
        {
-               index = find_queue_job(ud->queue, status->unique_id, &js);
                job_str = g_strdup_printf("job %d of %d, ", index+1, qcount);
        }
        else
        {
                job_str = g_strdup("");
        }
+       task_str = g_strdup_printf("Searching for start time, ");
        if(status->seconds > -1)
        {
                status_str= g_strdup_printf(
                        "Encoding: %s%s%.2f %%"
-                       " (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)",
+                       " (ETA %02dh%02dm%02ds)",
                        job_str, task_str,
                        100.0 * status->progress,
-                       status->rate_cur, status->rate_avg, status->hours, 
-                       status->minutes, status->seconds );
+                       status->hours, status->minutes, status->seconds );
        }
        else
        {
@@ -1680,22 +2575,48 @@ ghb_backend_events(signal_user_data_t *ud)
        ghb_status_t status;
        gchar *status_str;
        GtkProgressBar *progress;
+       GtkLabel       *work_status;
        gint titleindex;
        GValue *js;
        gint index;
        GtkTreeView *treeview;
        GtkTreeStore *store;
        GtkTreeIter iter;
-       static gint working = 0;
-       static gboolean work_started = FALSE;
+       static gint prev_scan_state = 0;
+       static gint prev_queue_state = 0;
        
        ghb_track_status();
        ghb_get_status(&status);
+       if (prev_scan_state != status.scan.state ||
+               prev_queue_state != status.queue.state)
+       {
+               ghb_queue_buttons_grey(ud);
+               prev_scan_state = status.scan.state;
+               prev_queue_state = status.queue.state;
+       }
        progress = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "progressbar"));
+       work_status = GTK_LABEL(GHB_WIDGET (ud->builder, "work_status"));
+       if (status.scan.state == GHB_STATE_IDLE && 
+               status.queue.state == GHB_STATE_IDLE)
+       {
+               static gboolean prev_dvdnav;
+               gboolean dvdnav = ghb_settings_get_boolean(ud->settings, "use_dvdnav");
+               if (dvdnav != prev_dvdnav)
+               {
+                       hb_dvd_set_dvdnav(dvdnav);
+                       prev_dvdnav = dvdnav;
+               }
+       }
        // First handle the status of title scans
        // Then handle the status of the queue
        if (status.scan.state & GHB_STATE_SCANNING)
        {
+               GtkProgressBar *scan_prog;
+               GtkLabel *label;
+
+               scan_prog = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "scan_prog"));
+               label = GTK_LABEL(GHB_WIDGET (ud->builder, "source_title"));
+
                if (status.scan.title_cur == 0)
                {
                        status_str = g_strdup ("Scanning...");
@@ -1705,56 +2626,74 @@ ghb_backend_events(signal_user_data_t *ud)
                        status_str = g_strdup_printf ("Scanning title %d of %d...", 
                                                          status.scan.title_cur, status.scan.title_count );
                }
-               gtk_progress_bar_set_text (progress, status_str);
+               gtk_label_set_text (label, status_str);
                g_free(status_str);
                if (status.scan.title_count > 0)
                {
-                       gtk_progress_bar_set_fraction (progress
+                       gtk_progress_bar_set_fraction (scan_prog
                                (gdouble)status.scan.title_cur / status.scan.title_count);
                }
        }
        else if (status.scan.state & GHB_STATE_SCANDONE)
        {
-               status_str = g_strdup_printf ("Scan done"); 
-               gtk_progress_bar_set_text (progress, status_str);
-               g_free(status_str);
-               gtk_progress_bar_set_fraction (progress, 1.0);
+               gchar *source;
+               GtkProgressBar *scan_prog;
+               GtkLabel *label;
+
+               GtkWidget *widget;
+               GtkAction *action;
+
+               widget = GHB_WIDGET(ud->builder, "sourcetoolbutton");
+               gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(widget), "hb-source");
+               gtk_tool_button_set_label(GTK_TOOL_BUTTON(widget), "Source");
+               gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(widget), "Choose Video Source");
+
+               action = GHB_ACTION(ud->builder, "source_action");
+               gtk_action_set_sensitive(action, TRUE);
+               action = GHB_ACTION(ud->builder, "source_single_action");
+               gtk_action_set_sensitive(action, TRUE);
+
+               source = ghb_settings_get_string(ud->settings, "scan_source");
+               update_source_label(ud, source, FALSE);
+
+               scan_prog = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "scan_prog"));
+               gtk_progress_bar_set_fraction (scan_prog, 1.0);
+               gtk_widget_hide(GTK_WIDGET(scan_prog));
+
+               if (!ghb_settings_get_boolean(ud->settings, "preset_modified"))
+               {
+                       ghb_refresh_preset(ud);
+               }
 
                ghb_title_info_t tinfo;
                        
-               ghb_update_ui_combo_box(ud->builder, "title", 0, FALSE);
+               ghb_update_ui_combo_box(ud, "title", 0, FALSE);
                titleindex = ghb_longest_title();
                ghb_ui_update(ud, "title", ghb_int64_value(titleindex));
 
+               label = GTK_LABEL(GHB_WIDGET (ud->builder, "source_title"));
                // Are there really any titles.
                if (!ghb_get_title_info(&tinfo, titleindex))
                {
-                       GtkProgressBar *progress;
-                       progress = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "progressbar"));
-                       gtk_progress_bar_set_fraction (progress, 0);
-                       gtk_progress_bar_set_text (progress, "No Source");
+                       gtk_label_set_text(label, "None");
                }
                ghb_clear_scan_state(GHB_STATE_SCANDONE);
-               ghb_queue_buttons_grey(ud, work_started);
                if (ghb_queue_edit_settings)
                {
-                       gint jstatus;
-
-                       jstatus = ghb_settings_get_int(ghb_queue_edit_settings, "job_status");
                        ghb_settings_to_ui(ud, ghb_queue_edit_settings);
                        ghb_set_audio(ud, ghb_queue_edit_settings);
-                       if (jstatus == GHB_QUEUE_PENDING)
-                       {
-                               ghb_value_free(ghb_queue_edit_settings);
-                       }
+                       ghb_reset_subtitles(ud, ghb_queue_edit_settings);
+                       reset_chapter_list(ud, ghb_queue_edit_settings);
+                       ghb_value_free(ghb_queue_edit_settings);
                        ghb_queue_edit_settings = NULL;
                }
        }
-       else if (status.queue.state & GHB_STATE_SCANNING)
+
+       if (status.queue.state & GHB_STATE_SCANNING)
        {
-               status_str = g_strdup_printf ("Scanning ...");
-               gtk_progress_bar_set_text (progress, status_str);
-               g_free(status_str);
+               // This needs to be in scanning and working since scanning
+               // happens fast enough that it can be missed
+               gtk_label_set_text (work_status, "Scanning ...");
                gtk_progress_bar_set_fraction (progress, 0);
        }
        else if (status.queue.state & GHB_STATE_SCANDONE)
@@ -1762,36 +2701,103 @@ ghb_backend_events(signal_user_data_t *ud)
                ghb_clear_queue_state(GHB_STATE_SCANDONE);
                usleep(2000000);
                submit_job(ud->current_job);
+               ghb_update_pending(ud);
        }
        else if (status.queue.state & GHB_STATE_PAUSED)
        {
-               status_str = g_strdup_printf ("Paused"); 
-               gtk_progress_bar_set_text (progress, status_str);
-               g_free(status_str);
+               gtk_label_set_text (work_status, "Paused");
        }
-       else if (status.queue.state & GHB_STATE_WORKING)
+       else if (status.queue.state & GHB_STATE_SEARCHING)
        {
-               status_str = working_status_string(ud, &status.queue);
-               gtk_progress_bar_set_text (progress, status_str);
+               static gint working = 0;
+
+               // This needs to be in scanning and working since scanning
+               // happens fast enough that it can be missed
+               index = find_queue_job(ud->queue, status.queue.unique_id, &js);
+               if (status.queue.unique_id != 0 && index >= 0)
+               {
+                       gchar working_icon[] = "hb-working0";
+                       working_icon[10] = '0' + working;
+                       working = (working+1) % 6;
+                       treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
+                       store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
+                       gchar *path = g_strdup_printf ("%d", index);
+                       if (gtk_tree_model_get_iter_from_string(
+                                       GTK_TREE_MODEL(store), &iter, path))
+                       {
+                               gtk_tree_store_set(store, &iter, 0, working_icon, -1);
+                       }
+                       g_free(path);
+               }
+               GtkLabel *label;
+               gchar *status_str;
+
+               status_str = searching_status_string(ud, &status.queue);
+               label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_status"));
+               gtk_label_set_text (label, status_str);
+#if !GTK_CHECK_VERSION(2, 16, 0)
+               GtkStatusIcon *si;
+
+               si = GTK_STATUS_ICON(GHB_OBJECT(ud->builder, "hb_status"));
+               gtk_status_icon_set_tooltip(si, status_str);
+#endif
+               gtk_label_set_text (work_status, status_str);
                gtk_progress_bar_set_fraction (progress, status.queue.progress);
                g_free(status_str);
        }
-       else if (status.queue.state & GHB_STATE_WORKDONE)
+       else if (status.queue.state & GHB_STATE_WORKING)
        {
-               gint qstatus;
+               static gint working = 0;
 
-               work_started = FALSE;
-               ghb_queue_buttons_grey(ud, FALSE);
+               // This needs to be in scanning and working since scanning
+               // happens fast enough that it can be missed
                index = find_queue_job(ud->queue, status.queue.unique_id, &js);
-               treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
-               store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
-               if (ud->cancel_encode)
-                       status.queue.error = GHB_ERROR_CANCELED;
-               switch( status.queue.error )
+               if (status.queue.unique_id != 0 && index >= 0)
                {
-                       case GHB_ERROR_NONE:
-                               gtk_progress_bar_set_text( progress, "Rip done!" );
-                               qstatus = GHB_QUEUE_DONE;
+                       gchar working_icon[] = "hb-working0";
+                       working_icon[10] = '0' + working;
+                       working = (working+1) % 6;
+                       treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
+                       store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
+                       gchar *path = g_strdup_printf ("%d", index);
+                       if (gtk_tree_model_get_iter_from_string(
+                                       GTK_TREE_MODEL(store), &iter, path))
+                       {
+                               gtk_tree_store_set(store, &iter, 0, working_icon, -1);
+                       }
+                       g_free(path);
+               }
+               GtkLabel *label;
+               gchar *status_str;
+
+               status_str = working_status_string(ud, &status.queue);
+               label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_status"));
+               gtk_label_set_text (label, status_str);
+#if !GTK_CHECK_VERSION(2, 16, 0)
+               GtkStatusIcon *si;
+
+               si = GTK_STATUS_ICON(GHB_OBJECT(ud->builder, "hb_status"));
+               gtk_status_icon_set_tooltip(si, status_str);
+#endif
+               gtk_label_set_text (work_status, status_str);
+               gtk_progress_bar_set_fraction (progress, status.queue.progress);
+               g_free(status_str);
+       }
+       else if (status.queue.state & GHB_STATE_WORKDONE)
+       {
+               gint qstatus;
+
+               index = find_queue_job(ud->queue, status.queue.unique_id, &js);
+               treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
+               store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
+               if (ud->cancel_encode == GHB_CANCEL_ALL || 
+                       ud->cancel_encode == GHB_CANCEL_CURRENT)
+                       status.queue.error = GHB_ERROR_CANCELED;
+               switch( status.queue.error )
+               {
+                       case GHB_ERROR_NONE:
+                               gtk_label_set_text (work_status, "Rip Done!");
+                               qstatus = GHB_QUEUE_DONE;
                                if (js != NULL)
                                {
                                        gchar *path = g_strdup_printf ("%d", index);
@@ -1804,7 +2810,7 @@ ghb_backend_events(signal_user_data_t *ud)
                                }
                                break;
                        case GHB_ERROR_CANCELED:
-                               gtk_progress_bar_set_text( progress, "Rip canceled." );
+                               gtk_label_set_text (work_status, "Rip Canceled.");
                                qstatus = GHB_QUEUE_CANCELED;
                                if (js != NULL)
                                {
@@ -1818,7 +2824,7 @@ ghb_backend_events(signal_user_data_t *ud)
                                }
                                break;
                        default:
-                               gtk_progress_bar_set_text( progress, "Rip failed.");
+                               gtk_label_set_text (work_status, "Rip Failed.");
                                qstatus = GHB_QUEUE_CANCELED;
                                if (js != NULL)
                                {
@@ -1836,67 +2842,33 @@ ghb_backend_events(signal_user_data_t *ud)
                if (ud->job_activity_log)
                        g_io_channel_unref(ud->job_activity_log);
                ud->job_activity_log = NULL;
-               if (!ud->cancel_encode)
+               if (ud->cancel_encode != GHB_CANCEL_ALL &&
+                       ud->cancel_encode != GHB_CANCEL_FINISH)
                {
                        ud->current_job = ghb_start_next_job(ud, FALSE);
                }
                else
                {
-                       ghb_uninhibit_gpm();
+                       ghb_uninhibit_gsm();
                        ud->current_job = NULL;
+                       gtk_widget_hide(GTK_WIDGET(progress));
                }
                if (js)
                        ghb_settings_set_int(js, "job_status", qstatus);
                ghb_save_queue(ud->queue);
-               ud->cancel_encode = FALSE;
+               ud->cancel_encode = GHB_CANCEL_NONE;
+#if !GTK_CHECK_VERSION(2, 16, 0)
+               GtkStatusIcon *si;
+
+               si = GTK_STATUS_ICON(GHB_OBJECT(ud->builder, "hb_status"));
+               gtk_status_icon_set_tooltip(si, "HandBrake");
+#endif
        }
        else if (status.queue.state & GHB_STATE_MUXING)
        {
-               gtk_progress_bar_set_text(progress, "Muxing: this may take awhile...");
-       }
-       if (status.queue.state & GHB_STATE_SCANNING)
-       {
-               // This needs to be in scanning and working since scanning
-               // happens fast enough that it can be missed
-               if (!work_started)
-               {
-                       work_started = TRUE;
-                       ghb_queue_buttons_grey(ud, TRUE);
-               }
+               gtk_label_set_text (work_status, "Muxing: This may take a while...");
        }
-       if (status.queue.state & GHB_STATE_WORKING)
-       {
-               // This needs to be in scanning and working since scanning
-               // happens fast enough that it can be missed
-               if (!work_started)
-               {
-                       work_started = TRUE;
-                       ghb_queue_buttons_grey(ud, TRUE);
-               }
-               index = find_queue_job(ud->queue, status.queue.unique_id, &js);
-               if (status.queue.unique_id != 0 && index >= 0)
-               {
-                       gchar working_icon[] = "hb-working0";
-                       working_icon[10] = '0' + working;
-                       working = (working+1) % 6;
-                       treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
-                       store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
-                       gchar *path = g_strdup_printf ("%d", index);
-                       if (gtk_tree_model_get_iter_from_string(
-                                       GTK_TREE_MODEL(store), &iter, path))
-                       {
-                               gtk_tree_store_set(store, &iter, 0, working_icon, -1);
-                       }
-                       g_free(path);
-               }
-               GtkLabel *label;
-               gchar *status_str;
 
-               status_str = working_status_string(ud, &status.queue);
-               label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_status"));
-               gtk_label_set_text (label, status_str);
-               g_free(status_str);
-       }
        if (status.scan.state & GHB_STATE_WORKING)
        {
                GtkProgressBar *live_progress;
@@ -1924,7 +2896,37 @@ ghb_backend_events(signal_user_data_t *ud)
        }
 }
 
-gboolean
+#if GTK_CHECK_VERSION(2, 16, 0)
+G_MODULE_EXPORT gboolean
+status_icon_query_tooltip_cb(
+       GtkStatusIcon *si,
+       gint           x,
+       gint           y,
+       gboolean       kbd_mode,
+       GtkTooltip    *tt,
+       signal_user_data_t *ud)
+{
+       ghb_status_t status;
+       gchar *status_str;
+
+       ghb_get_status(&status);
+       if (status.queue.state & GHB_STATE_WORKING)
+               status_str = working_status_string(ud, &status.queue);
+       else if (status.queue.state & GHB_STATE_SEARCHING)
+               status_str = searching_status_string(ud, &status.queue);
+       else if (status.queue.state & GHB_STATE_WORKDONE)
+               status_str = g_strdup("Encode Complete");
+       else
+               status_str = g_strdup("HandBrake");
+
+       gtk_tooltip_set_text(tt, status_str);
+       gtk_tooltip_set_icon_from_icon_name(tt, "hb-icon", GTK_ICON_SIZE_BUTTON);
+       g_free(status_str);
+       return TRUE;
+}
+#endif
+
+G_MODULE_EXPORT gboolean
 ghb_timer_cb(gpointer data)
 {
        signal_user_data_t *ud = (signal_user_data_t*)data;
@@ -1949,17 +2951,51 @@ ghb_timer_cb(gpointer data)
        }
        if (update_preview)
        {
+               g_debug("Updating preview\n");
                ghb_set_preview_image (ud);
                update_preview = FALSE;
        }
+
+#if !defined(_NO_UPDATE_CHECK)
+       if (!appcast_busy)
+       {
+               gchar *updates;
+               updates = ghb_settings_get_string(ud->settings, "check_updates");
+               gint64 duration = 0;
+               if (strcmp(updates, "daily") == 0)
+                       duration = 60 * 60 * 24;
+               else if (strcmp(updates, "weekly") == 0)
+                       duration = 60 * 60 * 24 * 7;
+               else if (strcmp(updates, "monthly") == 0)
+                       duration = 60 * 60 * 24 * 7;
+
+               g_free(updates);
+               if (duration != 0)
+               {
+                       gint64 last;
+                       time_t tt;
+
+                       last = ghb_settings_get_int64(ud->settings, "last_update_check");
+                       time(&tt);
+                       if (last + duration < tt)
+                       {
+                               ghb_settings_set_int64(ud->settings, 
+                                                                               "last_update_check", tt);
+                               ghb_pref_save(ud->settings, "last_update_check");
+                               g_thread_create((GThreadFunc)ghb_check_update, ud, 
+                                                               FALSE, NULL);
+                       }
+               }
+       }
+#endif
        return TRUE;
 }
 
-gboolean
+G_MODULE_EXPORT gboolean
 ghb_log_cb(GIOChannel *source, GIOCondition cond, gpointer data)
 {
        gchar *text = NULL;
-       gsize length;
+       gsize length, outlength;
        GtkTextView *textview;
        GtkTextBuffer *buffer;
        GtkTextIter iter;
@@ -1970,12 +3006,17 @@ ghb_log_cb(GIOChannel *source, GIOCondition cond, gpointer data)
        signal_user_data_t *ud = (signal_user_data_t*)data;
 
        status = g_io_channel_read_line (source, &text, &length, NULL, &gerror);
-       if (text != NULL)
+       // Trim nils from end of text, they cause g_io_channel_write_chars to
+       // fail with an assertion that aborts
+       while (length > 0 && text[length-1] == 0)
+               length--;
+       if (text != NULL && length > 0)
        {
                GdkWindow *window;
                gint width, height;
                gint x, y;
                gboolean bottom = FALSE;
+               gchar *utf8_text;
 
                textview = GTK_TEXT_VIEW(GHB_WIDGET (ud->builder, "activity_view"));
                buffer = gtk_text_view_get_buffer (textview);
@@ -2001,25 +3042,45 @@ ghb_log_cb(GIOChannel *source, GIOCondition cond, gpointer data)
                        bottom = TRUE;
                }
                gtk_text_buffer_get_end_iter(buffer, &iter);
-               gtk_text_buffer_insert(buffer, &iter, text, -1);
-               if (bottom)
+               utf8_text = g_convert_with_fallback(text, -1, "UTF-8", "ISO-8859-1",
+                                                                                       "?", NULL, &length, NULL);
+               if (utf8_text != NULL)
                {
-                       gtk_text_buffer_get_end_iter(buffer, &iter);
-                       mark = gtk_text_buffer_create_mark(buffer, NULL, &iter, FALSE);
-                       gtk_text_view_scroll_mark_onscreen(textview, mark);
-                       gtk_text_buffer_delete_mark(buffer, mark);
-               }
-               g_io_channel_write_chars (ud->activity_log, text, 
-                                                               length, &length, NULL);
-               g_io_channel_flush(ud->activity_log, NULL);
-               if (ud->job_activity_log)
-               {
-                       g_io_channel_write_chars (ud->job_activity_log, text, 
-                                                                       length, &length, NULL);
-                       g_io_channel_flush(ud->job_activity_log, NULL);
+                       gtk_text_buffer_insert(buffer, &iter, utf8_text, -1);
+                       if (bottom)
+                       {
+                               gtk_text_buffer_get_end_iter(buffer, &iter);
+                               mark = gtk_text_buffer_create_mark(buffer, NULL, &iter, FALSE);
+                               gtk_text_view_scroll_mark_onscreen(textview, mark);
+                               gtk_text_buffer_delete_mark(buffer, mark);
+                       }
+#if defined(_WIN32)
+                       gsize one = 1;
+                       utf8_text[length-1] = '\r';
+#endif
+                       g_io_channel_write_chars (ud->activity_log, utf8_text, 
+                                                                       length, &outlength, NULL);
+#if defined(_WIN32)
+                       g_io_channel_write_chars (ud->activity_log, "\n", 
+                                                                       one, &one, NULL);
+#endif
+                       g_io_channel_flush(ud->activity_log, NULL);
+                       if (ud->job_activity_log)
+                       {
+                               g_io_channel_write_chars (ud->job_activity_log, utf8_text, 
+                                                                               length, &outlength, NULL);
+#if defined(_WIN32)
+                               g_io_channel_write_chars (ud->activity_log, "\n", 
+                                                                               one, &outlength, NULL);
+#endif
+                               g_io_channel_flush(ud->job_activity_log, NULL);
+                       }
+                       g_free(utf8_text);
                }
-               g_free(text);
        }
+       if (text != NULL)
+               g_free(text);
+
        if (status != G_IO_STATUS_NORMAL)
        {
                // This should never happen, but if it does I would get into an
@@ -2050,7 +3111,7 @@ set_visible(GtkWidget *widget, gboolean visible)
        }
 }
 
-void
+G_MODULE_EXPORT void
 show_activity_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
 {
        GtkWidget *widget = GHB_WIDGET (ud->builder, "activity_window");
@@ -2058,7 +3119,7 @@ show_activity_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
                                                GTK_TOGGLE_TOOL_BUTTON(xwidget)));
 }
 
-void
+G_MODULE_EXPORT void
 show_activity_menu_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
 {
        GtkWidget *widget = GHB_WIDGET (ud->builder, "activity_window");
@@ -2067,7 +3128,7 @@ show_activity_menu_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
        gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), TRUE);
 }
 
-gboolean
+G_MODULE_EXPORT gboolean
 activity_window_delete_cb(GtkWidget *xwidget, GdkEvent *event, signal_user_data_t *ud)
 {
        set_visible(xwidget, FALSE);
@@ -2086,7 +3147,7 @@ ghb_log(gchar *log, ...)
 
        _now = time(NULL);
        now = localtime( &_now );
-       snprintf(fmt, 362, "[%02d:%02d:%02d] lingui: %s\n", 
+       snprintf(fmt, 362, "[%02d:%02d:%02d] gtkgui: %s\n", 
                        now->tm_hour, now->tm_min, now->tm_sec, log);
        va_start(args, log);
        vfprintf(stderr, fmt, args);
@@ -2096,6 +3157,10 @@ ghb_log(gchar *log, ...)
 static void
 browse_url(const gchar *url)
 {
+#if defined(_WIN32)
+       HINSTANCE r;
+       r = ShellExecute(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL);
+#else
        gboolean result;
        char *argv[] = 
                {"xdg-open",NULL,NULL,NULL};
@@ -2121,6 +3186,7 @@ browse_url(const gchar *url)
        argv[2] = NULL;
        result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
                                NULL, NULL, NULL);
+#endif
 }
 
 void
@@ -2129,7 +3195,7 @@ about_web_hook(GtkAboutDialog *about, const gchar *link, gpointer data)
        browse_url(link);
 }
 
-void
+G_MODULE_EXPORT void
 about_activate_cb(GtkWidget *xwidget, signal_user_data_t *ud)
 {
        GtkWidget *widget = GHB_WIDGET (ud->builder, "hb_about");
@@ -2146,19 +3212,19 @@ about_activate_cb(GtkWidget *xwidget, signal_user_data_t *ud)
        gtk_widget_show (widget);
 }
 
-void
+G_MODULE_EXPORT void
 guide_activate_cb(GtkWidget *xwidget, signal_user_data_t *ud)
 {
        browse_url("http://trac.handbrake.fr/wiki/HandBrakeGuide");
 }
 
-void
+G_MODULE_EXPORT void
 hb_about_response_cb(GtkWidget *widget, gint response, signal_user_data_t *ud)
 {
        gtk_widget_hide (widget);
 }
 
-void
+G_MODULE_EXPORT void
 show_queue_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
 {
        GtkWidget *widget = GHB_WIDGET (ud->builder, "queue_window");
@@ -2166,7 +3232,7 @@ show_queue_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
                                                GTK_TOGGLE_TOOL_BUTTON(xwidget)));
 }
 
-void
+G_MODULE_EXPORT void
 show_queue_menu_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
 {
        GtkWidget *widget = GHB_WIDGET (ud->builder, "queue_window");
@@ -2175,7 +3241,7 @@ show_queue_menu_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
        gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), TRUE);
 }
 
-gboolean
+G_MODULE_EXPORT gboolean
 queue_window_delete_cb(GtkWidget *xwidget, GdkEvent *event, signal_user_data_t *ud)
 {
        set_visible(xwidget, FALSE);
@@ -2184,7 +3250,7 @@ queue_window_delete_cb(GtkWidget *xwidget, GdkEvent *event, signal_user_data_t *
        return TRUE;
 }
 
-void
+G_MODULE_EXPORT void
 show_presets_toggled_cb(GtkWidget *action, signal_user_data_t *ud)
 {
        GtkWidget *widget;
@@ -2207,6 +3273,83 @@ show_presets_toggled_cb(GtkWidget *action, signal_user_data_t *ud)
 }
 
 static void
+reset_chapter_list(signal_user_data_t *ud, GValue *settings)
+{
+       GtkTreeView *treeview;
+       GtkTreeIter iter;
+       GtkListStore *store;
+       gboolean done;
+       GValue *chapters;
+       gint titleindex, ii;
+       gint count;
+       
+       g_debug("reset_chapter_list ()");
+       chapters = ghb_value_dup(ghb_settings_get_value(settings, "chapter_list"));
+       count = ghb_array_len(chapters);
+       ghb_settings_set_value(ud->settings, "chapter_list", chapters);
+       titleindex = ghb_settings_combo_int(ud->settings, "title");
+       
+       treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "chapters_list"));
+       store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview));
+       ii = 0;
+       if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter))
+       {
+               do
+               {
+
+                       if (ii < count)
+                       {
+                               gchar *chapter, *duration;
+                               gint hh, mm, ss;
+
+                               // Update row with settings data
+                               g_debug("Updating row");
+                               chapter = ghb_value_string(ghb_array_get_nth(chapters, ii));
+                               ghb_get_chapter_duration(titleindex, ii, &hh, &mm, &ss);
+                               duration = g_strdup_printf("%02d:%02d:%02d", hh, mm, ss);
+                               gtk_list_store_set(store, &iter, 
+                                       0, ii+1,
+                                       1, duration,
+                                       2, chapter,
+                                       3, TRUE,
+                                       -1);
+                               g_free(chapter);
+                               g_free(duration);
+                               ii++;
+                               done = !gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
+                       }
+                       else
+                       {
+                               // No more settings data, remove row
+                               g_debug("Removing row");
+                               done = !gtk_list_store_remove(store, &iter);
+                       }
+               } while (!done);
+       }
+       while (ii < count)
+       {
+               gchar *chapter, *duration;
+               gint hh, mm, ss;
+
+               // Additional settings, add row
+               g_debug("Adding row");
+               chapter = ghb_value_string(ghb_array_get_nth(chapters, ii));
+               ghb_get_chapter_duration(titleindex, ii, &hh, &mm, &ss);
+               duration = g_strdup_printf("%02d:%02d:%02d", hh, mm, ss);
+               gtk_list_store_append(store, &iter);
+               gtk_list_store_set(store, &iter, 
+                       0, ii+1,
+                       1, duration,
+                       2, chapter,
+                       3, TRUE,
+                       -1);
+               g_free(chapter);
+               g_free(duration);
+               ii++;
+       }
+}
+
+static void
 update_chapter_list(signal_user_data_t *ud)
 {
        GtkTreeView *treeview;
@@ -2286,7 +3429,7 @@ update_chapter_list(signal_user_data_t *ud)
 
 static gint chapter_edit_key = 0;
 
-gboolean
+G_MODULE_EXPORT gboolean
 chapter_keypress_cb(
        GhbCellRendererText *cell,
        GdkEventKey *event,
@@ -2296,7 +3439,7 @@ chapter_keypress_cb(
        return FALSE;
 }
 
-void
+G_MODULE_EXPORT void
 chapter_edited_cb(
        GhbCellRendererText *cell, 
        gchar *path, 
@@ -2376,13 +3519,6 @@ chapter_edited_cb(
 }
 
 void
-chapter_list_selection_changed_cb(GtkTreeSelection *selection, signal_user_data_t *ud)
-{
-       g_debug("chapter_list_selection_changed_cb ()");
-       //chapter_selection_changed = TRUE;
-}
-
-void
 debug_log_handler(const gchar *domain, GLogLevelFlags flags, const gchar *msg, gpointer data)
 {
        signal_user_data_t *ud = (signal_user_data_t*)data;
@@ -2396,7 +3532,6 @@ debug_log_handler(const gchar *domain, GLogLevelFlags flags, const gchar *msg, g
 void
 warn_log_handler(const gchar *domain, GLogLevelFlags flags, const gchar *msg, gpointer data)
 {
-       printf("mywarning\n");
        printf("%s: %s\n", domain, msg);
 }
 
@@ -2429,7 +3564,7 @@ ghb_hbfd(signal_user_data_t *ud, gboolean hbfd)
 
 }
 
-void
+G_MODULE_EXPORT void
 hbfd_toggled_cb(GtkWidget *widget, signal_user_data_t *ud)
 {
        g_debug("hbfd_toggled_cb");
@@ -2439,24 +3574,51 @@ hbfd_toggled_cb(GtkWidget *widget, signal_user_data_t *ud)
        ghb_pref_save(ud->settings, "hbfd");
 }
 
-void
+G_MODULE_EXPORT void
 pref_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
 {
        g_debug("pref_changed_cb");
        ghb_widget_to_setting (ud->settings, widget);
-       ghb_check_dependency(ud, widget);
-       const gchar *name = gtk_widget_get_name(widget);
+       ghb_check_dependency(ud, widget, NULL);
+       const gchar *name = ghb_get_setting_key(widget);
        ghb_pref_save(ud->settings, name);
 }
 
-void
+G_MODULE_EXPORT void
+use_m4v_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
+{
+       g_debug("use_m4v_changed_cb");
+       ghb_widget_to_setting (ud->settings, widget);
+       ghb_check_dependency(ud, widget, NULL);
+       const gchar *name = ghb_get_setting_key(widget);
+       ghb_pref_save(ud->settings, name);
+       ghb_update_destination_extension(ud);
+}
+
+G_MODULE_EXPORT void
+show_status_cb(GtkWidget *widget, signal_user_data_t *ud)
+{
+       g_debug("show_status_cb");
+       ghb_widget_to_setting (ud->settings, widget);
+       ghb_check_dependency(ud, widget, NULL);
+       const gchar *name = ghb_get_setting_key(widget);
+       ghb_pref_save(ud->settings, name);
+
+       GtkStatusIcon *si;
+
+       si = GTK_STATUS_ICON(GHB_OBJECT (ud->builder, "hb_status"));
+       gtk_status_icon_set_visible(si,
+                       ghb_settings_get_boolean(ud->settings, "show_status"));
+}
+
+G_MODULE_EXPORT void
 vqual_granularity_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
 {
        g_debug("vqual_granularity_changed_cb");
        ghb_widget_to_setting (ud->settings, widget);
-       ghb_check_dependency(ud, widget);
+       ghb_check_dependency(ud, widget, NULL);
 
-       const gchar *name = gtk_widget_get_name(widget);
+       const gchar *name = ghb_get_setting_key(widget);
        ghb_pref_save(ud->settings, name);
 
        gdouble vqmin, vqmax, step, page;
@@ -2468,21 +3630,21 @@ vqual_granularity_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        gtk_range_set_increments (GTK_RANGE(qp), step, page);
 }
 
-void
+G_MODULE_EXPORT void
 tweaks_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
 {
        g_debug("tweaks_changed_cb");
        ghb_widget_to_setting (ud->settings, widget);
-       const gchar *name = gtk_widget_get_name(widget);
+       const gchar *name = ghb_get_setting_key(widget);
        ghb_pref_save(ud->settings, name);
 }
 
-void
+G_MODULE_EXPORT void
 hbfd_feature_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
 {
        g_debug("hbfd_feature_changed_cb");
        ghb_widget_to_setting (ud->settings, widget);
-       const gchar *name = gtk_widget_get_name(widget);
+       const gchar *name = ghb_get_setting_key(widget);
        ghb_pref_save(ud->settings, name);
 
        gboolean hbfd = ghb_settings_get_boolean(ud->settings, "hbfd_feature");
@@ -2497,42 +3659,66 @@ hbfd_feature_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        gtk_action_set_visible(action, hbfd);
 }
 
-void
+gboolean
 ghb_file_menu_add_dvd(signal_user_data_t *ud)
 {
        GList *link, *drives;
+       static GtkActionGroup *agroup = NULL;
+       static gint merge_id;
 
-       GtkActionGroup *agroup = GTK_ACTION_GROUP(
-               gtk_builder_get_object(ud->builder, "actiongroup1"));
-       GtkUIManager *ui = GTK_UI_MANAGER(
-               gtk_builder_get_object(ud->builder, "uimanager1"));
-       guint merge_id = gtk_ui_manager_new_merge_id(ui);
-
+       g_debug("ghb_file_menu_add_dvd()");
        link = drives = dvd_device_list();
-       while (link != NULL)
+       if (drives != NULL)
        {
-               gchar *name = (gchar*)link->data;
-               // Create action for this drive
-               GtkAction *action = gtk_action_new(name, name,
-                       "Scan this DVD source", "gtk-cdrom");
-               // Add action to action group
-               gtk_action_group_add_action_with_accel(agroup, action, "");
-               // Add to ui manager
+               GtkUIManager *ui = GTK_UI_MANAGER(
+                       gtk_builder_get_object(ud->builder, "uimanager1"));
+
+               if (agroup == NULL)
+               {
+                       agroup = gtk_action_group_new("dvdgroup");
+                       gtk_ui_manager_insert_action_group(ui, agroup, 0);
+               }
+               else
+                       gtk_ui_manager_remove_ui(ui, merge_id);
+
+               merge_id = gtk_ui_manager_new_merge_id(ui);
+               // Add separator
                gtk_ui_manager_add_ui(ui, merge_id, 
-                       "ui/menubar1/menuitem1/quit1", name, name,
-                       GTK_UI_MANAGER_AUTO, TRUE);
-               // Connect signal to action (menu item)
-               g_signal_connect(action, "activate", 
-                       (GCallback)dvd_source_activate_cb, ud);
-               g_free(name);
-               link = link->next;
-       }
-       g_list_free(drives);
+                       "ui/menubar1/menuitem1/quit1", "dvdsep", NULL,
+                       GTK_UI_MANAGER_SEPARATOR, TRUE);
 
-       // Add separator
-       gtk_ui_manager_add_ui(ui, merge_id, 
-               "ui/menubar1/menuitem1/quit1", "", NULL,
-               GTK_UI_MANAGER_AUTO, TRUE);
+               while (link != NULL)
+               {
+                       GtkAction *action;
+                       gchar *drive = get_dvd_device_name(link->data);
+                       gchar *name = get_dvd_volume_name(link->data);
+               
+                       action = gtk_action_group_get_action(agroup, drive);
+                       if (action != NULL)
+                       {
+                               gtk_action_group_remove_action(agroup, action);
+                               g_object_unref(G_OBJECT(action));
+                       }
+                       // Create action for this drive
+                       action = gtk_action_new(drive, name,
+                               "Scan this DVD source", "gtk-cdrom");
+                       // Add action to action group
+                       gtk_action_group_add_action_with_accel(agroup, action, NULL);
+                       // Add to ui manager
+                       gtk_ui_manager_add_ui(ui, merge_id, 
+                               "ui/menubar1/menuitem1/dvdsep", drive, drive,
+                               GTK_UI_MANAGER_AUTO, TRUE);
+                       // Connect signal to action (menu item)
+                       g_signal_connect(action, "activate", 
+                               (GCallback)dvd_source_activate_cb, ud);
+                       g_free(name);
+                       g_free(drive);
+                       free_drive(link->data);
+                       link = link->next;
+               }
+               g_list_free(drives);
+       }
+       return FALSE;
 }
 
 gboolean ghb_is_cd(GDrive *gd);
@@ -2540,9 +3726,33 @@ gboolean ghb_is_cd(GDrive *gd);
 static GList*
 dvd_device_list()
 {
+       GList *dvd_devices = NULL;
+
+#if defined(_WIN32)
+       gint ii, drives;
+       gchar drive[5];
+
+       strcpy(drive, "A:" G_DIR_SEPARATOR_S);
+       drives = GetLogicalDrives();
+       for (ii = 0; ii < 26; ii++)
+       {
+               if (drives & 0x01)
+               {
+                       guint dtype;
+
+                       drive[0] = 'A' + ii;
+                       dtype = GetDriveType(drive);
+                       if (dtype == DRIVE_CDROM)
+                       {
+                               dvd_devices = g_list_append(dvd_devices, 
+                                               (gpointer)g_strdup(drive));
+                       }
+               }
+               drives >>= 1;
+       }
+#else
        GVolumeMonitor *gvm;
        GList *drives, *link;
-       GList *dvd_devices = NULL;
        
        gvm = g_volume_monitor_get ();
        drives = g_volume_monitor_get_connected_drives (gvm);
@@ -2554,92 +3764,411 @@ dvd_device_list()
                gd = (GDrive*)link->data;
                if (ghb_is_cd(gd))
                {
-                       gchar *device;
-                       device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
-                       dvd_devices = g_list_append(dvd_devices, (gpointer)device);
+                       dvd_devices = g_list_append(dvd_devices, gd);
                }
-               g_object_unref (gd);
+               else
+                       g_object_unref (gd);
                link = link->next;
        }
        g_list_free(drives);
+#endif
+
        return dvd_devices;
 }
 
-static LibHalContext *hal_ctx;
+#if !defined(_WIN32)
+static GUdevClient *udev_ctx = NULL;
+#endif
 
 gboolean
 ghb_is_cd(GDrive *gd)
 {
+#if !defined(_WIN32)
        gchar *device;
-       LibHalDrive *halDrive;
-       LibHalDriveType dtype;
+       GUdevDevice *udd;
+
+       if (udev_ctx == NULL)
+               return FALSE;
 
        device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
-       halDrive = libhal_drive_from_device_file (hal_ctx, device);
-       dtype = libhal_drive_get_type(halDrive);
-       libhal_drive_free(halDrive);
+       if (device == NULL)
+               return FALSE;
+
+       udd = g_udev_client_query_by_device_file(udev_ctx, device);
        g_free(device);
-       return (dtype == LIBHAL_DRIVE_TYPE_CDROM);
+
+       if (udd == NULL)
+       {
+               g_message("udev: Failed to lookup device %s", device);
+               return FALSE;
+       }
+
+       gint val;
+       val = g_udev_device_get_property_as_int(udd, "ID_CDROM_DVD");
+       if (val == 1)
+               return TRUE;
+
+       return FALSE;
+#else
+       return FALSE;
+#endif
 }
 
 void
+ghb_udev_init()
+{
+#if !defined(_WIN32)
+       udev_ctx = g_udev_client_new(NULL);
+#endif
+}
+
+#if defined(_WIN32)
+static void
+handle_media_change(const gchar *device, gboolean insert, signal_user_data_t *ud)
+{
+       guint dtype;
+       static gint ins_count = 0;
+       static gint rem_count = 0;
+
+       // The media change event in windows bounces around a bit
+       // so I debounce it here
+       // DVD insertion detected.  Scan it.
+       dtype = GetDriveType(device);
+       if (dtype != DRIVE_CDROM)
+               return;
+       if (insert)
+       {
+               rem_count = 0;
+               ins_count++;
+               if (ins_count == 2)
+               {
+                       g_thread_create((GThreadFunc)ghb_cache_volnames, ud, FALSE, NULL);
+                       if (ghb_settings_get_boolean(ud->settings, "AutoScan") &&
+                               ud->current_dvd_device != NULL &&
+                               strcmp(device, ud->current_dvd_device) == 0)
+                       {
+                               show_scan_progress(ud);
+                               update_source_label(ud, device, TRUE);
+                               gint preview_count;
+                               preview_count = ghb_settings_get_int(ud->settings, "preview_count");
+                               ghb_settings_set_string(ud->settings, "scan_source", device);
+                               start_scan(ud, device, 0, preview_count);
+                       }
+               }
+       }
+       else
+       {
+               ins_count = 0;
+               rem_count++;
+               if (rem_count == 2)
+               {
+                       g_thread_create((GThreadFunc)ghb_cache_volnames, ud, FALSE, NULL);
+                       if (ud->current_dvd_device != NULL &&
+                               strcmp(device, ud->current_dvd_device) == 0)
+                       {
+                               ghb_hb_cleanup(TRUE);
+                               prune_logs(ud);
+                               ghb_settings_set_string(ud->settings, "scan_source", "/dev/null");
+                               start_scan(ud, "/dev/null", 0, 1);
+                       }
+               }
+       }
+}
+
+static gchar
+FindDriveFromMask(ULONG unitmask)
+{
+       gchar cc;
+       for (cc = 0; cc < 26; cc++)
+       {
+               if (unitmask & 0x01)
+                       return 'A' + cc;
+               unitmask >>= 1;
+       }
+       return 0;
+}
+
+void
+wm_drive_changed(MSG *msg, signal_user_data_t *ud)
+{
+       PDEV_BROADCAST_HDR bch = (PDEV_BROADCAST_HDR)msg->lParam;
+       gchar drive[4];
+
+       g_strlcpy(drive, "A:" G_DIR_SEPARATOR_S, 4);
+       switch (msg->wParam)
+       {
+               case DBT_DEVICEARRIVAL:
+               {
+                       if (bch->dbch_devicetype == DBT_DEVTYP_VOLUME)
+                       {
+                               PDEV_BROADCAST_VOLUME bcv = (PDEV_BROADCAST_VOLUME)bch;
+
+                               if (bcv->dbcv_flags & DBTF_MEDIA)
+                               {
+                                       drive[0] = FindDriveFromMask(bcv->dbcv_unitmask);
+                                       handle_media_change(drive, TRUE, ud);
+                               }
+                       }
+               } break;
+
+               case DBT_DEVICEREMOVECOMPLETE:
+               {
+                       if (bch->dbch_devicetype == DBT_DEVTYP_VOLUME)
+                       {
+                               PDEV_BROADCAST_VOLUME bcv = (PDEV_BROADCAST_VOLUME)bch;
+
+                               if (bcv->dbcv_flags & DBTF_MEDIA)
+                               {
+                                       drive[0] = FindDriveFromMask(bcv->dbcv_unitmask);
+                                       handle_media_change(drive, FALSE, ud);
+                               }
+                       }
+               } break;
+               default: ;
+       }
+}
+
+#else
+
+G_MODULE_EXPORT void
 drive_changed_cb(GVolumeMonitor *gvm, GDrive *gd, signal_user_data_t *ud)
 {
        gchar *device;
-       gint state = ghb_get_scan_state();
-       static gboolean first_time = TRUE;
+       gint state;
+
+       g_debug("drive_changed_cb()");
+       g_thread_create((GThreadFunc)ghb_cache_volnames, ud, FALSE, NULL);
+
+       state = ghb_get_scan_state();
+       device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
+       if (ud->current_dvd_device == NULL ||
+               strcmp(device, ud->current_dvd_device) != 0 ||
+               state != GHB_STATE_IDLE )
+       {
+               return;
+       }
+       if (g_drive_has_media(gd))
+       {
+               if (ghb_settings_get_boolean(ud->settings, "AutoScan"))
+               {
+                       show_scan_progress(ud);
+                       update_source_label(ud, device, TRUE);
+                       gint preview_count;
+                       preview_count = ghb_settings_get_int(ud->settings, "preview_count");
+                       ghb_settings_set_string(ud->settings, "scan_source", device);
+                       start_scan(ud, device, 0, preview_count);
+               }
+       }
+       else
+       {
+               ghb_hb_cleanup(TRUE);
+               prune_logs(ud);
+               ghb_settings_set_string(ud->settings, "scan_source", "/dev/null");
+               start_scan(ud, "/dev/null", 0, 1);
+       }
+}
+#endif
+
+#if !defined(_WIN32)
+#define GPM_DBUS_PM_SERVICE                    "org.freedesktop.PowerManagement"
+#define GPM_DBUS_PM_PATH                       "/org/freedesktop/PowerManagement"
+#define GPM_DBUS_PM_INTERFACE          "org.freedesktop.PowerManagement"
+#define GPM_DBUS_INHIBIT_PATH          "/org/freedesktop/PowerManagement/Inhibit"
+#define GPM_DBUS_INHIBIT_INTERFACE     "org.freedesktop.PowerManagement.Inhibit" 
+static gboolean gpm_inhibited = FALSE;
+static guint gpm_cookie = -1;
+#endif
+
+static gboolean
+ghb_can_suspend_gpm()
+{
+       gboolean can_suspend = FALSE;
+#if !defined(_WIN32)
+       DBusGConnection *conn;
+       DBusGProxy      *proxy;
+       GError *error = NULL;
+       gboolean res;
+       
+
+       g_debug("ghb_can_suspend_gpm()");
+       conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+       if (error != NULL)
+       {
+               g_warning("DBUS cannot connect: %s", error->message);
+               g_error_free(error);
+               return FALSE;
+       }
+       proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE,
+                                                       GPM_DBUS_PM_PATH, GPM_DBUS_PM_INTERFACE);
+       if (proxy == NULL)
+       {
+               g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE);
+               dbus_g_connection_unref(conn);
+               return FALSE;
+       }
+       res = dbus_g_proxy_call(proxy, "CanSuspend", &error,
+                                                       G_TYPE_INVALID,
+                                                       G_TYPE_BOOLEAN, &can_suspend,
+                                                       G_TYPE_INVALID);
+       if (!res)
+       {
+               if (error != NULL)
+               {
+                       g_warning("CanSuspend failed: %s", error->message);
+                       g_error_free(error);
+               }
+               else
+                       g_warning("CanSuspend failed");
+               // Try to shutdown anyway
+               can_suspend = TRUE;
+       }
+       g_object_unref(G_OBJECT(proxy));
+       dbus_g_connection_unref(conn);
+#endif
+       return can_suspend;
+}
+
+static void
+ghb_suspend_gpm()
+{
+#if !defined(_WIN32)
+       DBusGConnection *conn;
+       DBusGProxy      *proxy;
+       GError *error = NULL;
+       gboolean res;
+       
+
+       g_debug("ghb_suspend_gpm()");
+       conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+       if (error != NULL)
+       {
+               g_warning("DBUS cannot connect: %s", error->message);
+               g_error_free(error);
+               return;
+       }
+       proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE,
+                                                       GPM_DBUS_PM_PATH, GPM_DBUS_PM_INTERFACE);
+       if (proxy == NULL)
+       {
+               g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE);
+               dbus_g_connection_unref(conn);
+               return;
+       }
+       res = dbus_g_proxy_call(proxy, "Suspend", &error,
+                                                       G_TYPE_INVALID,
+                                                       G_TYPE_INVALID);
+       if (!res)
+       {
+               if (error != NULL)
+               {
+                       g_warning("Suspend failed: %s", error->message);
+                       g_error_free(error);
+               }
+               else
+                       g_warning("Suspend failed");
+       }
+       g_object_unref(G_OBJECT(proxy));
+       dbus_g_connection_unref(conn);
+#endif
+}
+
+#if !defined(_WIN32)
+static gboolean
+ghb_can_shutdown_gpm()
+{
+       gboolean can_shutdown = FALSE;
+       DBusGConnection *conn;
+       DBusGProxy      *proxy;
+       GError *error = NULL;
+       gboolean res;
+       
+
+       g_debug("ghb_can_shutdown_gpm()");
+       conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+       if (error != NULL)
+       {
+               g_warning("DBUS cannot connect: %s", error->message);
+               g_error_free(error);
+               return FALSE;
+       }
+       proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE,
+                                                       GPM_DBUS_PM_PATH, GPM_DBUS_PM_INTERFACE);
+       if (proxy == NULL)
+       {
+               g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE);
+               dbus_g_connection_unref(conn);
+               return FALSE;
+       }
+       res = dbus_g_proxy_call(proxy, "CanShutdown", &error,
+                                                       G_TYPE_INVALID,
+                                                       G_TYPE_BOOLEAN, &can_shutdown,
+                                                       G_TYPE_INVALID);
+       if (!res)
+       {
+               if (error != NULL)
+               {
+                       g_warning("CanShutdown failed: %s", error->message);
+                       g_error_free(error);
+               }
+               else
+                       g_warning("CanShutdown failed");
+               // Try to shutdown anyway
+               can_shutdown = TRUE;
+       }
+       g_object_unref(G_OBJECT(proxy));
+       dbus_g_connection_unref(conn);
+       return can_shutdown;
+}
+#endif
+
+#if !defined(_WIN32)
+static void
+ghb_shutdown_gpm()
+{
+       DBusGConnection *conn;
+       DBusGProxy      *proxy;
+       GError *error = NULL;
+       gboolean res;
+       
 
-       if (state != GHB_STATE_IDLE) return;
-       if (ud->current_dvd_device == NULL) return;
-       // A drive change event happens when the program initially starts
-       // and I don't want to automatically scan at that time.
-       if (first_time)
+       g_debug("ghb_shutdown_gpm()");
+       conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+       if (error != NULL)
        {
-               first_time = FALSE;
+               g_warning("DBUS cannot connect: %s", error->message);
+               g_error_free(error);
                return;
        }
-       device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
-       
-       // DVD insertion detected.  Scan it.
-       if (strcmp(device, ud->current_dvd_device) == 0)
+       proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE,
+                                                       GPM_DBUS_PM_PATH, GPM_DBUS_PM_INTERFACE);
+       if (proxy == NULL)
+       {
+               g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE);
+               dbus_g_connection_unref(conn);
+               return;
+       }
+       res = dbus_g_proxy_call(proxy, "Shutdown", &error,
+                                                       G_TYPE_INVALID,
+                                                       G_TYPE_INVALID);
+       if (!res)
        {
-               if (g_drive_has_media (gd))
+               if (error != NULL)
                {
-                       GtkProgressBar *progress;
-                       progress = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "progressbar"));
-                       gtk_progress_bar_set_text (progress, "Scanning ...");
-                       gtk_progress_bar_set_fraction (progress, 0);
-                       update_source_label(ud, device);
-                       prune_logs(ud);
-                       gint preview_count;
-                       preview_count = ghb_settings_get_int(ud->settings, "preview_count");
-                       ghb_backend_scan(device, 0, preview_count);
+                       g_warning("Shutdown failed: %s", error->message);
+                       g_error_free(error);
                }
                else
-               {
-                       prune_logs(ud);
-                       ghb_backend_scan("/dev/null", 0, 1);
-               }
+                       g_warning("Shutdown failed");
        }
-       g_free(device);
-}
-
-
-static void
-dbus_init (void)
-{
-       dbus_g_thread_init();
+       g_object_unref(G_OBJECT(proxy));
+       dbus_g_connection_unref(conn);
 }
-
-#define GPM_DBUS_SERVICE                       "org.freedesktop.PowerManagement"
-#define GPM_DBUS_INHIBIT_PATH          "/org/freedesktop/PowerManagement/Inhibit"
-#define GPM_DBUS_INHIBIT_INTERFACE     "org.freedesktop.PowerManagement.Inhibit" 
-static gboolean gpm_inhibited = FALSE;
-static guint gpm_cookie = -1;
+#endif
 
 void
 ghb_inhibit_gpm()
 {
+#if !defined(_WIN32)
        DBusGConnection *conn;
        DBusGProxy      *proxy;
        GError *error = NULL;
@@ -2655,15 +4184,15 @@ ghb_inhibit_gpm()
        conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
        if (error != NULL)
        {
-               g_debug("DBUS cannot connect: %s", error->message);
+               g_warning("DBUS cannot connect: %s", error->message);
                g_error_free(error);
                return;
        }
-       proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SERVICE,
+       proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE,
                                                        GPM_DBUS_INHIBIT_PATH, GPM_DBUS_INHIBIT_INTERFACE);
        if (proxy == NULL)
        {
-               g_debug("Could not get DBUS proxy: %s", GPM_DBUS_SERVICE);
+               g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE);
                dbus_g_connection_unref(conn);
                return;
        }
@@ -2673,25 +4202,29 @@ ghb_inhibit_gpm()
                                                        G_TYPE_INVALID,
                                                        G_TYPE_UINT, &gpm_cookie,
                                                        G_TYPE_INVALID);
+       gpm_inhibited = TRUE;
        if (!res)
        {
-               g_warning("Inhibit method failed");
-               gpm_cookie = -1;
-       }
-       if (error != NULL)
-       {
-               g_warning("Inhibit problem: %s", error->message);
-               g_error_free(error);
+               if (error != NULL)
+               {
+                       g_warning("Inhibit failed: %s", error->message);
+                       g_error_free(error);
+                       gpm_cookie = -1;
+               }
+               else
+                       g_warning("Inhibit failed");
                gpm_cookie = -1;
+               gpm_inhibited = FALSE;
        }
-       gpm_inhibited = TRUE;
        g_object_unref(G_OBJECT(proxy));
        dbus_g_connection_unref(conn);
+#endif
 }
 
 void
 ghb_uninhibit_gpm()
 {
+#if !defined(_WIN32)
        DBusGConnection *conn;
        DBusGProxy      *proxy;
        GError *error = NULL;
@@ -2707,15 +4240,15 @@ ghb_uninhibit_gpm()
        conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
        if (error != NULL)
        {
-               g_debug("DBUS cannot connect: %s", error->message);
+               g_warning("DBUS cannot connect: %s", error->message);
                g_error_free(error);
                return;
        }
-       proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SERVICE,
+       proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE,
                                                        GPM_DBUS_INHIBIT_PATH, GPM_DBUS_INHIBIT_INTERFACE);
        if (proxy == NULL)
        {
-               g_debug("Could not get DBUS proxy: %s", GPM_DBUS_SERVICE);
+               g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE);
                dbus_g_connection_unref(conn);
                return;
        }
@@ -2725,74 +4258,231 @@ ghb_uninhibit_gpm()
                                                        G_TYPE_INVALID);
        if (!res)
        {
-               g_warning("UnInhibit method failed");
+               if (error != NULL)
+               {
+                       g_warning("UnInhibit failed: %s", error->message);
+                       g_error_free(error);
+               }
+               else
+                       g_warning("UnInhibit failed");
        }
+       gpm_inhibited = FALSE;
+       dbus_g_connection_unref(conn);
+       g_object_unref(G_OBJECT(proxy));
+#endif
+}
+
+#if !defined(_WIN32)
+
+// For inhibit and shutdown
+#define GPM_DBUS_SM_SERVICE                    "org.gnome.SessionManager"
+#define GPM_DBUS_SM_PATH                       "/org/gnome/SessionManager"
+#define GPM_DBUS_SM_INTERFACE          "org.gnome.SessionManager"
+
+#endif
+
+static gboolean
+ghb_can_shutdown_gsm()
+{
+       gboolean can_shutdown = FALSE;
+#if !defined(_WIN32)
+       DBusGConnection *conn;
+       DBusGProxy      *proxy;
+       GError *error = NULL;
+       gboolean res;
+       
+
+       g_debug("ghb_can_shutdown_gpm()");
+       conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
        if (error != NULL)
        {
-               g_warning("UnInhibit problem: %s", error->message);
+               g_warning("DBUS cannot connect: %s", error->message);
                g_error_free(error);
+               return FALSE;
        }
-       gpm_inhibited = FALSE;
+       proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SM_SERVICE,
+                                                       GPM_DBUS_SM_PATH, GPM_DBUS_SM_INTERFACE);
+       if (proxy == NULL)
+       {
+               g_warning("Could not get DBUS proxy: %s", GPM_DBUS_SM_SERVICE);
+               dbus_g_connection_unref(conn);
+               return FALSE;
+       }
+       res = dbus_g_proxy_call(proxy, "CanShutdown", &error,
+                                                       G_TYPE_INVALID,
+                                                       G_TYPE_BOOLEAN, &can_shutdown,
+                                                       G_TYPE_INVALID);
+       g_object_unref(G_OBJECT(proxy));
        dbus_g_connection_unref(conn);
+       if (!res)
+       {
+               if (error != NULL)
+               {
+                       g_error_free(error);
+               }
+               // Try to shutdown anyway
+               can_shutdown = TRUE;
+               // Try the gpm version
+               return ghb_can_shutdown_gpm();
+       }
+#endif
+       return can_shutdown;
+}
+
+static void
+ghb_shutdown_gsm()
+{
+#if !defined(_WIN32)
+       DBusGConnection *conn;
+       DBusGProxy      *proxy;
+       GError *error = NULL;
+       gboolean res;
+       
+
+       g_debug("ghb_shutdown_gpm()");
+       conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+       if (error != NULL)
+       {
+               g_warning("DBUS cannot connect: %s", error->message);
+               g_error_free(error);
+               return;
+       }
+       proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SM_SERVICE,
+                                                       GPM_DBUS_SM_PATH, GPM_DBUS_SM_INTERFACE);
+       if (proxy == NULL)
+       {
+               g_warning("Could not get DBUS proxy: %s", GPM_DBUS_SM_SERVICE);
+               dbus_g_connection_unref(conn);
+               return;
+       }
+       res = dbus_g_proxy_call(proxy, "Shutdown", &error,
+                                                       G_TYPE_INVALID,
+                                                       G_TYPE_INVALID);
        g_object_unref(G_OBJECT(proxy));
+       dbus_g_connection_unref(conn);
+       if (!res)
+       {
+               if (error != NULL)
+               {
+                       g_error_free(error);
+               }
+               // Try the gpm version
+               ghb_shutdown_gpm();
+       }
+#endif
 }
 
 void
-ghb_hal_init()
+ghb_inhibit_gsm(signal_user_data_t *ud)
 {
-       DBusGConnection *gconn;
-       DBusConnection *conn;
-       GError *gerror = NULL;
-       DBusError error;
-       char **devices;
-       int nr;
-
-       dbus_init ();
+#if !defined(_WIN32)
+       DBusGConnection *conn;
+       DBusGProxy      *proxy;
+       GError *error = NULL;
+       gboolean res;
+       guint xid;
+       GtkWidget *widget;
+       
 
-       if (!(hal_ctx = libhal_ctx_new ())) {
-               g_warning ("failed to create a HAL context!");
+       if (gpm_inhibited)
+       {
+               // Already inhibited
                return;
        }
-
-       gconn = dbus_g_bus_get(DBUS_BUS_SYSTEM, &gerror);
-       if (gerror != NULL)
+       g_debug("ghb_inhibit_gsm()");
+       conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+       if (error != NULL)
        {
-               g_warning("DBUS cannot connect: %s", gerror->message);
-               g_error_free(gerror);
+               g_warning("DBUS cannot connect: %s", error->message);
+               g_error_free(error);
                return;
        }
-       conn = dbus_g_connection_get_connection(gconn);
-       libhal_ctx_set_dbus_connection (hal_ctx, conn);
-       dbus_error_init (&error);
-       if (!libhal_ctx_init (hal_ctx, &error)) {
-               g_warning ("libhal_ctx_init failed: %s", error.message ? error.message : "unknown");
-               dbus_error_free (&error);
-               libhal_ctx_free (hal_ctx);
-               dbus_g_connection_unref(gconn);
+       proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SM_SERVICE,
+                                                       GPM_DBUS_SM_PATH, GPM_DBUS_SM_INTERFACE);
+       if (proxy == NULL)
+       {
+               g_warning("Could not get DBUS proxy: %s", GPM_DBUS_SM_SERVICE);
+               dbus_g_connection_unref(conn);
                return;
        }
+       widget = GHB_WIDGET(ud->builder, "hb_window");
+       xid = GDK_DRAWABLE_XID(widget->window);
+       res = dbus_g_proxy_call(proxy, "Inhibit", &error,
+                                                       G_TYPE_STRING, "ghb",
+                                                       G_TYPE_UINT, xid,
+                                                       G_TYPE_STRING, "Encoding",
+                                                       G_TYPE_UINT, 1 | 4,
+                                                       G_TYPE_INVALID,
+                                                       G_TYPE_UINT, &gpm_cookie,
+                                                       G_TYPE_INVALID);
+       gpm_inhibited = TRUE;
+       g_object_unref(G_OBJECT(proxy));
+       dbus_g_connection_unref(conn);
+       if (!res)
+       {
+               if (error != NULL)
+               {
+                       g_error_free(error);
+                       gpm_cookie = -1;
+               }
+               gpm_cookie = -1;
+               gpm_inhibited = FALSE;
+               // Try the gpm version
+               ghb_inhibit_gpm();
+       }
+#endif
+}
 
-       /*
-        * Do something to ping the HAL daemon - the above functions will
-        * succeed even if hald is not running, so long as DBUS is.  But we
-        * want to exit silently if hald is not running, to behave on
-        * pre-2.6 systems.
-        */
-       if (!(devices = libhal_get_all_devices (hal_ctx, &nr, &error))) {
-               g_warning ("seems that HAL is not running: %s", error.message ? error.message : "unknown");
-               dbus_error_free (&error);
+void
+ghb_uninhibit_gsm()
+{
+#if !defined(_WIN32)
+       DBusGConnection *conn;
+       DBusGProxy      *proxy;
+       GError *error = NULL;
+       gboolean res;
+       
+       g_debug("ghb_uninhibit_gsm() gpm_cookie %u", gpm_cookie);
 
-               libhal_ctx_shutdown (hal_ctx, NULL);
-               libhal_ctx_free (hal_ctx);
-               dbus_g_connection_unref(gconn);
+       if (!gpm_inhibited)
+       {
+               // Not inhibited
                return;
        }
-
-       libhal_free_string_array (devices);
-       dbus_g_connection_unref(gconn);
+       conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+       if (error != NULL)
+       {
+               g_warning("DBUS cannot connect: %s", error->message);
+               g_error_free(error);
+               return;
+       }
+       proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SM_SERVICE,
+                                                       GPM_DBUS_SM_PATH, GPM_DBUS_SM_INTERFACE);
+       if (proxy == NULL)
+       {
+               g_warning("Could not get DBUS proxy: %s", GPM_DBUS_SM_SERVICE);
+               dbus_g_connection_unref(conn);
+               return;
+       }
+       res = dbus_g_proxy_call(proxy, "Uninhibit", &error,
+                                                       G_TYPE_UINT, gpm_cookie,
+                                                       G_TYPE_INVALID,
+                                                       G_TYPE_INVALID);
+       dbus_g_connection_unref(conn);
+       g_object_unref(G_OBJECT(proxy));
+       if (!res)
+       {
+               if (error != NULL)
+               {
+                       g_error_free(error);
+               }
+               ghb_uninhibit_gpm();
+       }
+       gpm_inhibited = FALSE;
+#endif
 }
 
-gboolean 
+G_MODULE_EXPORT gboolean 
 tweak_setting_cb(
        GtkWidget *widget, 
        GdkEventButton *event, 
@@ -2812,7 +4502,7 @@ tweak_setting_cb(
                GtkResponseType response;
                gchar *tweak = NULL;
 
-               name = gtk_widget_get_name(widget);
+               name = ghb_get_setting_key(widget);
                if (g_str_has_prefix(name, "tweak_"))
                {
                        tweak_name = g_strdup(name);
@@ -2854,7 +4544,7 @@ tweak_setting_cb(
        return ret;
 }
 
-gboolean 
+G_MODULE_EXPORT gboolean 
 easter_egg_cb(
        GtkWidget *widget, 
        GdkEventButton *event, 
@@ -2880,7 +4570,7 @@ easter_egg_cb(
        return FALSE;
 }
 
-gchar*
+G_MODULE_EXPORT gchar*
 format_deblock_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud)
 {
        if (val < 5.0)
@@ -2893,7 +4583,7 @@ format_deblock_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud)
        }
 }
 
-gchar*
+G_MODULE_EXPORT gchar*
 format_drc_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud)
 {
        if (val <= 0.0)
@@ -2906,67 +4596,55 @@ format_drc_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud)
        }
 }
 
-gchar*
+G_MODULE_EXPORT gchar*
 format_vquality_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud)
 {
-       gdouble percent;
-
        gint vcodec = ghb_settings_combo_int(ud->settings, "VideoEncoder");
        switch (vcodec)
        {
                case HB_VCODEC_X264:
                {
-                       gboolean crf;
-                       crf = ghb_settings_get_boolean(ud->settings, "constant_rate_factor");
-                       percent = 100. * (51 - val) / 51.;
-                       if (crf)
-                               return g_strdup_printf("RF: %.4g (%.0f%%)", val, percent);
+                       if (val == 0.0)
+                       {
+                               return g_strdup_printf("RF: %.4g (Warning: lossless)", val);
+                       }
                        else
-                               return g_strdup_printf("QP: %.4g (%.0f%%)", val, percent);
+                       {
+                               return g_strdup_printf("RF: %.4g", val);
+                       }
                } break;
 
-               case HB_VCODEC_XVID:
                case HB_VCODEC_FFMPEG:
                {
-                       percent = 100. * (30 - (val - 1)) / 30.;
-                       return g_strdup_printf("QP: %d (%.0f%%)", (int)val, percent);
+                       return g_strdup_printf("QP: %d", (int)val);
                } break;
 
                case HB_VCODEC_THEORA:
                {
-                       percent = 100. * val / 63.;
-                       return g_strdup_printf("QP: %d (%.0f%%)", (int)val, percent);
+                       return g_strdup_printf("QP: %d", (int)val);
                } break;
 
                default:
                {
-                       percent = 0;
                } break;
        }
-       return g_strdup_printf("QP: %.1f / %.1f%%", val, percent);
+       return g_strdup_printf("QP: %.4g", val);
 }
 
 static void
-html_link_cb(GtkHTML *html, const gchar *url, signal_user_data_t *ud)
-{
-       browse_url(url);
-}
-
-static gpointer check_stable_update(signal_user_data_t *ud);
-static gboolean stable_update_lock = FALSE;
-
-static void
 process_appcast(signal_user_data_t *ud)
 {
        gchar *description = NULL, *build = NULL, *version = NULL, *msg;
-       GtkWidget *html, *window, *dialog, *label;
+#if !defined(_WIN32)
+       GtkWidget *window;
+       static GtkWidget *html = NULL;
+#endif
+       GtkWidget *dialog, *label;
        gint    response, ibuild = 0, skip;
 
        if (ud->appcast == NULL || ud->appcast_len < 15 || 
                strncmp(&(ud->appcast[9]), "200 OK", 6))
        {
-               if (!stable_update_lock && hb_get_build(NULL) % 100)
-                       g_idle_add((GSourceFunc)check_stable_update, ud);
                goto done;
        }
        ghb_appcast_parse(ud->appcast, &description, &build, &version);
@@ -2976,25 +4654,30 @@ process_appcast(signal_user_data_t *ud)
        if (description == NULL || build == NULL || version == NULL 
                || ibuild <= hb_get_build(NULL) || skip == ibuild)
        {
-               if (!stable_update_lock && hb_get_build(NULL) % 100)
-                       g_thread_create((GThreadFunc)check_stable_update, ud, FALSE, NULL);
                goto done;
        }
        msg = g_strdup_printf("HandBrake %s/%s is now available (you have %s/%d).",
                        version, build, hb_get_version(NULL), hb_get_build(NULL));
        label = GHB_WIDGET(ud->builder, "update_message");
        gtk_label_set_text(GTK_LABEL(label), msg);
-       html = gtk_html_new_from_string(description, -1);
-       g_signal_connect(html, "link_clicked", G_CALLBACK(html_link_cb), ud);
-       window = GHB_WIDGET(ud->builder, "update_scroll");
-       gtk_container_add(GTK_CONTAINER(window), html);
-       // Show it
+
+#if !defined(_WIN32)
+#if !defined(_NO_UPDATE_CHECK)
+       if (html == NULL)
+       {
+               html = webkit_web_view_new();
+               window = GHB_WIDGET(ud->builder, "update_scroll");
+               gtk_container_add(GTK_CONTAINER(window), html);
+               // Show it
+               gtk_widget_set_size_request(html, 420, 240);
+               gtk_widget_show(html);
+       }
+       webkit_web_view_open(WEBKIT_WEB_VIEW(html), description);
+#endif
+#endif
        dialog = GHB_WIDGET(ud->builder, "update_dialog");
-       gtk_widget_set_size_request(html, 420, 240);
-       gtk_widget_show(html);
        response = gtk_dialog_run(GTK_DIALOG(dialog));
        gtk_widget_hide(dialog);
-       gtk_widget_destroy(html);
        if (response == GTK_RESPONSE_OK)
        {
                // Skip
@@ -3010,6 +4693,7 @@ done:
        g_free(ud->appcast);
        ud->appcast_len = 0;
        ud->appcast = NULL;
+       appcast_busy = FALSE;
 }
 
 void
@@ -3024,7 +4708,7 @@ ghb_net_close(GIOChannel *ioc)
        g_io_channel_unref(ioc);
 }
 
-gboolean
+G_MODULE_EXPORT gboolean
 ghb_net_recv_cb(GIOChannel *ioc, GIOCondition cond, gpointer data)
 {
        gchar buf[2048];
@@ -3046,7 +4730,10 @@ ghb_net_recv_cb(GIOChannel *ioc, GIOCondition cond, gpointer data)
        }
        if (status == G_IO_STATUS_EOF)
        {
-               ud->appcast[ud->appcast_len] = 0;
+               if ( ud->appcast != NULL )
+               {
+                       ud->appcast[ud->appcast_len] = 0;
+               }
                ghb_net_close(ioc);
                process_appcast(ud);
                return FALSE;
@@ -3067,6 +4754,7 @@ ghb_net_open(signal_user_data_t *ud, gchar *address, gint port)
        if( !( host = gethostbyname( address ) ) )
        {
                g_warning( "gethostbyname failed (%s)", address );
+               appcast_busy = FALSE;
                return NULL;
        }
 
@@ -3079,12 +4767,14 @@ ghb_net_open(signal_user_data_t *ud, gchar *address, gint port)
        if( fd < 0 )
        {
                g_debug( "socket failed" );
+               appcast_busy = FALSE;
                return NULL;
        }
 
        if(connect(fd, (struct sockaddr*)&sock, sizeof(struct sockaddr_in )) < 0 )
        {
                g_debug( "connect failed" );
+               appcast_busy = FALSE;
                return NULL;
        }
        ioc = g_io_channel_unix_new(fd);
@@ -3102,48 +4792,127 @@ ghb_check_update(signal_user_data_t *ud)
        gsize len;
        GIOChannel *ioc;
        GError *gerror = NULL;
+       GRegex *regex;
+       GMatchInfo *mi;
+       gchar *host, *appcast;
 
        g_debug("ghb_check_update");
-       if (hb_get_build(NULL) % 100)
-       {
-       query = 
-               "GET /appcast_unstable.xml HTTP/1.0\r\nHost: handbrake.fr\r\n\r\n";
-       }
-       else
+       appcast_busy = TRUE;
+       regex = g_regex_new("^http://(.+)/(.+)$", 0, 0, NULL);
+       if (!g_regex_match(regex, HB_PROJECT_URL_APPCAST, 0, &mi))
        {
-               stable_update_lock = TRUE;
-       query = "GET /appcast.xml HTTP/1.0\r\nHost: handbrake.fr\r\n\r\n";
+               return NULL;
        }
-       ioc = ghb_net_open(ud, "handbrake.fr", 80);
+
+       host = g_match_info_fetch(mi, 1);
+       appcast = g_match_info_fetch(mi, 2);
+
+       if (host == NULL || appcast == NULL)
+               return NULL;
+
+       query = g_strdup_printf( "GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n",
+                                                       appcast, host);
+
+       ioc = ghb_net_open(ud, host, 80);
        if (ioc == NULL)
                return NULL;
 
        g_io_channel_write_chars(ioc, query, strlen(query), &len, &gerror);
        g_io_channel_flush(ioc, &gerror);
-       // This function is initiated by g_idle_add.  Must return false
-       // so that it is not called again
+       g_free(query);
+       g_free(host);
+       g_free(appcast);
+       g_match_info_free(mi);
+       g_regex_unref(regex);
        return NULL;
 }
 
-static gpointer
-check_stable_update(signal_user_data_t *ud)
+G_MODULE_EXPORT gboolean
+hb_visibility_event_cb(
+       GtkWidget *widget, 
+       GdkEventVisibility *vs, 
+       signal_user_data_t *ud)
 {
-       gchar *query;
-       gsize len;
-       GIOChannel *ioc;
-       GError *gerror = NULL;
+       ud->hb_visibility = vs->state;
+       return FALSE;
+}
 
-       g_debug("check_stable_update");
-       stable_update_lock = TRUE;
-       query = "GET /appcast.xml HTTP/1.0\r\nHost: handbrake.fr\r\n\r\n";
-       ioc = ghb_net_open(ud, "handbrake.fr", 80);
-       if (ioc == NULL)
-               return NULL;
+G_MODULE_EXPORT void
+status_activate_cb(GtkStatusIcon *si, signal_user_data_t *ud)
+{
+       GtkWindow *window;
+       GdkWindowState state;
 
-       g_io_channel_write_chars(ioc, query, strlen(query), &len, &gerror);
-       g_io_channel_flush(ioc, &gerror);
-       // This function is initiated by g_idle_add.  Must return false
-       // so that it is not called again
-       return NULL;
+       window = GTK_WINDOW(GHB_WIDGET(ud->builder, "hb_window"));
+       state = gdk_window_get_state(GTK_WIDGET(window)->window);
+       if ((state & GDK_WINDOW_STATE_ICONIFIED) ||
+               (ud->hb_visibility != GDK_VISIBILITY_UNOBSCURED))
+       {
+               gtk_window_present(window);
+               gtk_window_set_skip_taskbar_hint(window, FALSE);
+       }
+       else
+       {
+               gtk_window_set_skip_taskbar_hint(window, TRUE);
+               gtk_window_iconify(window);
+       }
+}
+
+#if !defined(_WIN32)
+G_MODULE_EXPORT void
+notify_closed_cb(NotifyNotification *notification, signal_user_data_t *ud)
+{
+       g_object_unref(G_OBJECT(notification));
 }
+#endif
+
+void
+ghb_notify_done(signal_user_data_t *ud)
+{
+       GtkStatusIcon *si;
+
+       if (ghb_settings_combo_int(ud->settings, "WhenComplete") == 0)
+               return;
+
+       si = GTK_STATUS_ICON(GHB_OBJECT(ud->builder, "hb_status"));
+
+#if !defined(_WIN32)
+       NotifyNotification *notification;
+       notification = notify_notification_new(
+               "Encode Complete",
+               "Put down that cocktail, Your HandBrake queue is done!",
+               "hb-icon",
+               NULL);
+       notify_notification_attach_to_status_icon(notification, si);
+       g_signal_connect(notification, "closed", (GCallback)notify_closed_cb, ud);
+       notify_notification_show(notification, NULL);
+#endif
 
+       if (ghb_settings_combo_int(ud->settings, "WhenComplete") == 3)
+       {
+               if (ghb_can_shutdown_gsm())
+               {
+                       ghb_countdown_dialog(GTK_MESSAGE_WARNING, 
+                               "Your encode is complete.",
+                               "Shutting down the computer", 
+                               "Cancel", (GSourceFunc)shutdown_cb, ud, 60);
+               }
+       }
+       if (ghb_settings_combo_int(ud->settings, "WhenComplete") == 2)
+       {
+               if (ghb_can_suspend_gpm())
+               {
+                       ghb_countdown_dialog(GTK_MESSAGE_WARNING, 
+                               "Your encode is complete.",
+                               "Putting computer to sleep", 
+                               "Cancel", (GSourceFunc)suspend_cb, ud, 60);
+               }
+       }
+       if (ghb_settings_combo_int(ud->settings, "WhenComplete") == 4)
+       {
+               ghb_countdown_dialog(GTK_MESSAGE_WARNING, 
+                                                       "Your encode is complete.",
+                                                       "Quiting Handbrake", 
+                                                       "Cancel", (GSourceFunc)quit_cb, ud, 60);
+       }
+}