OSDN Git Service

LinGui: first cut at anamorphic picture settings enhancements
[handbrake-jp/handbrake-jp-git.git] / gtk / src / callbacks.c
index c8d5e9c..61b6bf8 100644 (file)
 #include <poll.h>
 #include <fcntl.h>
 #include <sys/stat.h>
+#include <netinet/in.h>
+#include <netdb.h>
 #include <libhal-storage.h>
 #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 <libnotify/notify.h>
 
 #include "hb.h"
 #include "callbacks.h"
 #include "resources.h"
 #include "settings.h"
 #include "presets.h"
+#include "preview.h"
 #include "values.h"
 #include "plist.h"
+#include "appcast.h"
 #include "hb-backend.h"
 #include "ghb-dvd.h"
 #include "ghbcellrenderertext.h"
 
 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);
 
 // This is a dependency map used for greying widgets
 // that are dependent on the state of another widget.
@@ -59,7 +69,7 @@ ghb_init_dep_map()
 }
 
 static gboolean
-dep_check(signal_user_data_t *ud, const gchar *name)
+dep_check(signal_user_data_t *ud, const gchar *name, gboolean *out_hide)
 {
        GtkWidget *widget;
        GObject *dep_object;
@@ -71,8 +81,10 @@ dep_check(signal_user_data_t *ud, const gchar *name)
        
        g_debug("dep_check () %s", name);
 
+       if (rev_map == NULL) return TRUE;
        array = ghb_dict_lookup(rev_map, name);
        count = ghb_array_len(array);
+       *out_hide = FALSE;
        for (ii = 0; ii < count; ii++)
        {
                data = ghb_array_get_nth(array, ii);
@@ -80,6 +92,8 @@ dep_check(signal_user_data_t *ud, const gchar *name)
                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))
+                       continue;
                if (dep_object == NULL)
                {
                        g_message("Failed to find widget");
@@ -90,9 +104,10 @@ dep_check(signal_user_data_t *ud, const gchar *name)
                        gint jj = 0;
                        gchar **values;
                        gboolean sensitive = FALSE;
-                       gboolean die;
+                       gboolean die, hide;
 
                        die = ghb_value_boolean(ghb_array_get_nth(data, 2));
+                       hide = ghb_value_boolean(ghb_array_get_nth(data, 3));
                        value = ghb_value_string(ghb_array_get_nth(data, 1));
                        values = g_strsplit(value, "|", 10);
                        g_free(value);
@@ -131,7 +146,11 @@ dep_check(signal_user_data_t *ud, const gchar *name)
                                jj++;
                        }
                        sensitive = die ^ sensitive;
-                       if (!sensitive) result = FALSE;
+                       if (!sensitive)
+                       {
+                               result = FALSE;
+                               *out_hide |= hide;
+                       }
                        g_strfreev (values);
                        g_free(value);
                }
@@ -156,11 +175,13 @@ ghb_check_dependency(signal_user_data_t *ud, GtkWidget *widget)
        name = gtk_widget_get_name(widget);
        g_debug("ghb_check_dependency () %s", name);
 
+       if (dep_map == NULL) return;
        array = ghb_dict_lookup(dep_map, name);
        count = ghb_array_len(array);
        for (ii = 0; ii < count; ii++)
        {
                gboolean sensitive;
+               gboolean hide;
 
                data = ghb_array_get_nth(array, ii);
                dep_name = ghb_value_string(data);
@@ -171,12 +192,25 @@ ghb_check_dependency(signal_user_data_t *ud, GtkWidget *widget)
                        g_free(dep_name);
                        continue;
                }
-               sensitive = dep_check(ud, dep_name);
+               sensitive = dep_check(ud, dep_name, &hide);
                g_free(dep_name);
                if (GTK_IS_ACTION(dep_object))
+               {
                        gtk_action_set_sensitive(GTK_ACTION(dep_object), sensitive);
+                       gtk_action_set_visible(GTK_ACTION(dep_object), sensitive || !hide);
+               }
                else
+               {
                        gtk_widget_set_sensitive(GTK_WIDGET(dep_object), sensitive);
+                       if (!sensitive && hide)
+                       {
+                               gtk_widget_hide(GTK_WIDGET(dep_object));
+                       }
+                       else
+                       {
+                               gtk_widget_show_now(GTK_WIDGET(dep_object));
+                       }
+               }
        }
 }
 
@@ -189,6 +223,7 @@ ghb_check_all_depencencies(signal_user_data_t *ud)
        GObject *dep_object;
 
        g_debug("ghb_check_all_depencencies ()");
+       if (rev_map == NULL) return;
        ghb_dict_iter_init(&iter, rev_map);
        // middle (void*) cast prevents gcc warning "defreferencing type-punned
        // pointer will break strict-aliasing rules"
@@ -196,52 +231,33 @@ ghb_check_all_depencencies(signal_user_data_t *ud)
                        &iter, (gpointer*)(void*)&dep_name, (gpointer*)(void*)&value))
        {
                gboolean sensitive;
+               gboolean hide;
+
                dep_object = gtk_builder_get_object (ud->builder, dep_name);
                if (dep_object == NULL)
                {
                        g_message("Failed to find dependent widget %s", dep_name);
                        continue;
                }
-               sensitive = dep_check(ud, dep_name);
+               sensitive = dep_check(ud, dep_name, &hide);
                if (GTK_IS_ACTION(dep_object))
-                       gtk_action_set_sensitive(GTK_ACTION(dep_object), sensitive);
-               else
-                       gtk_widget_set_sensitive(GTK_WIDGET(dep_object), sensitive);
-       }
-}
-
-static gchar*
-expand_tilde(const gchar *path)
-{
-       const gchar *user_home;
-       gchar *home;
-       const gchar *suffix;
-       gchar *expanded_path = NULL;
-       
-       g_debug("expand_tilde ()");
-       if (path[0] == '~')
-       {
-               user_home = g_get_home_dir();
-               home = NULL; // squash warning about home uninitialized
-               if (path[1] == 0)
-               {
-                       home = g_strdup(user_home);
-                       suffix = "";
-               }
-               else if (path[1] == '/')
                {
-                       home = g_strdup(user_home);
-                       suffix = &path[2];
+                       gtk_action_set_sensitive(GTK_ACTION(dep_object), sensitive);
+                       gtk_action_set_visible(GTK_ACTION(dep_object), sensitive || !hide);
                }
                else
                {
-                       home = g_path_get_dirname(user_home);
-                       suffix = &path[1];
+                       gtk_widget_set_sensitive(GTK_WIDGET(dep_object), sensitive);
+                       if (!sensitive && hide)
+                       {
+                               gtk_widget_hide(GTK_WIDGET(dep_object));
+                       }
+                       else
+                       {
+                               gtk_widget_show_now(GTK_WIDGET(dep_object));
+                       }
                }
-               expanded_path = g_strdup_printf("%s/%s", home, suffix);
-               g_free(home);
        }
-       return expanded_path;
 }
 
 void
@@ -254,12 +270,14 @@ on_quit1_activate(GtkMenuItem *quit, signal_user_data_t *ud)
                if (ghb_cancel_encode("Closing HandBrake will terminate encoding.\n"))
                {
                        ghb_hb_cleanup(FALSE);
+                       prune_logs(ud);
                        gtk_main_quit();
                        return;
                }
                return;
        }
        ghb_hb_cleanup(FALSE);
+       prune_logs(ud);
        gtk_main_quit();
 }
 
@@ -269,39 +287,49 @@ 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 *dir, *new_name;
+               gchar *new_name;
+               gint title;
                
-               filename = ghb_settings_get_string(ud->settings, "destination");
-               extension = ghb_settings_get_string(ud->settings, "container");
-               dir = g_path_get_dirname (filename);
+               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");
-               if (ghb_settings_get_boolean(ud->settings, "chapters_in_destination"))
+               g_string_append_printf(str, "%s", vol_name);
+               title = ghb_settings_combo_int(ud->settings, "title");
+               if (title >= 0)
                {
-                       gint start, end;
-
-                       start = ghb_settings_get_int(ud->settings, "start_chapter");
-                       end = ghb_settings_get_int(ud->settings, "end_chapter");
-                       if (start == end)
+                       if (ghb_settings_get_boolean(
+                                       ud->settings, "title_no_in_destination"))
                        {
-                               new_name = g_strdup_printf("%s/%s-%d.%s", 
-                                       dir, vol_name, start, extension);
+
+                               title = ghb_settings_combo_int(ud->settings, "title");
+                               g_string_append_printf(str, " - %d", title+1);
                        }
-                       else
+                       if (ghb_settings_get_boolean(
+                                       ud->settings, "chapters_in_destination"))
                        {
-                               new_name = g_strdup_printf("%s/%s-%d-%d.%s", 
-                                       dir, vol_name, start, end, extension);
+                               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);
                        }
                }
-               else
-               {
-                       new_name = g_strdup_printf("%s/%s.%s", dir, vol_name, extension);
-               }
-               ghb_ui_update(ud, "destination", ghb_string_value(new_name));
+               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(dir);
                g_free(new_name);
        }
 }
@@ -362,6 +390,28 @@ camel_convert(gchar *str)
        }
 }
 
+static gchar*
+get_file_label(const gchar *filename)
+{
+       static gchar *containers[] = 
+               {".vob", ".mpg", ".m2ts", ".mkv", ".mp4", ".m4v", ".avi", ".ogm", NULL};
+       gchar *base;
+       gint ii;
+
+       base = g_path_get_basename(filename);
+       for (ii = 0; containers[ii] != NULL; ii++)
+       {
+               if (g_str_has_suffix(base, containers[ii]))
+               {
+                       gchar *pos;
+                       pos = strrchr(base, '.');
+                       *pos = 0;
+                       break;
+               }
+       }
+       return base;
+}
+
 static gboolean
 update_source_label(signal_user_data_t *ud, const gchar *source)
 {
@@ -393,11 +443,7 @@ update_source_label(signal_user_data_t *ud, const gchar *source)
                label = ghb_dvd_volname (filename);
                if (label == NULL)
                {
-                       path = g_strsplit(filename, "/", -1);
-                       len = g_strv_length (path);
-                       // Just use the last combonent of the path
-                       label = g_strdup(path[len-1]);
-                       g_strfreev (path);
+                       label = get_file_label(filename);
                }
                else
                {
@@ -426,18 +472,18 @@ update_source_label(signal_user_data_t *ud, const gchar *source)
        return TRUE;
 }
 
-static GtkWidget *dvd_device_combo = NULL;
-
 void
-chooser_file_selected_cb(GtkFileChooser *dialog, GtkComboBox *combo)
+chooser_file_selected_cb(GtkFileChooser *dialog, signal_user_data_t *ud)
 {
        const gchar *name = gtk_file_chooser_get_filename (dialog);
        GtkTreeModel *store;
        GtkTreeIter iter;
        const gchar *device;
        gboolean foundit = FALSE;
+       GtkComboBox *combo;
        
        if (name == NULL) return;
+       combo = GTK_COMBO_BOX(GHB_WIDGET(ud->builder, "source_device"));
        store = gtk_combo_box_get_model(combo);
        if (gtk_tree_model_get_iter_first(store, &iter))
        {
@@ -458,28 +504,39 @@ chooser_file_selected_cb(GtkFileChooser *dialog, GtkComboBox *combo)
 }
 
 void
-dvd_device_changed_cb(GtkComboBox *combo, GtkWidget *dialog)
+dvd_device_changed_cb(GtkComboBox *combo, signal_user_data_t *ud)
 {
-       gint ii = gtk_combo_box_get_active (combo);
-       if (ii != 0)
+       GtkWidget *dialog;
+       gint ii;
+
+       ii = gtk_combo_box_get_active (combo);
+       if (ii > 0)
        {
-               const gchar *device = gtk_combo_box_get_active_text (combo);
-               const gchar *name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog));
+               const gchar *device, *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, GtkFileChooser *chooser)
+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_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);
        }
@@ -495,61 +552,69 @@ source_type_changed_cb(GtkToggleButton *toggle, GtkFileChooser *chooser)
        }
 }
 
-static GtkWidget*
-source_dialog_extra_widgets(GtkWidget *dialog, gboolean checkbutton_active)
+static void
+source_dialog_extra_widgets(
+       signal_user_data_t *ud,
+       GtkWidget *dialog, 
+       gboolean checkbutton_active)
 {
-       GtkBox *vbox;
-       GtkWidget *checkbutton;
-       
-       vbox = GTK_BOX(gtk_vbox_new (FALSE, 2));
-       checkbutton = gtk_check_button_new_with_label ("Open VIDEO_TS folder");
-       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(checkbutton), checkbutton_active);
-       gtk_box_pack_start (vbox, checkbutton, FALSE, FALSE, 1);
-       gtk_widget_show(checkbutton);
-
-       GtkWidget *combo;
-       GtkBox *hbox;
+       GtkToggleButton *checkbutton;
+       GtkComboBox *combo;
        GList *drives, *link;
-       GtkWidget *label, *blank;
+       
+       checkbutton = GTK_TOGGLE_BUTTON(
+               GHB_WIDGET(ud->builder, "source_folder_flag"));
+       gtk_toggle_button_set_active(checkbutton, checkbutton_active);
+       combo = GTK_COMBO_BOX(GHB_WIDGET(ud->builder, "source_device"));
+       gtk_list_store_clear(GTK_LIST_STORE(
+                                               gtk_combo_box_get_model(combo)));
 
-       hbox = GTK_BOX(gtk_hbox_new (FALSE, 2));
-       combo = gtk_combo_box_new_text();
-       label = gtk_label_new("Detected DVD devices:");
-       blank = gtk_label_new("");
        link = drives = dvd_device_list();
-       gtk_combo_box_append_text (GTK_COMBO_BOX(combo), "Not Selected");
+       gtk_combo_box_append_text (combo, "Not Selected");
        while (link != NULL)
        {
                gchar *name = (gchar*)link->data;
-               gtk_combo_box_append_text (GTK_COMBO_BOX(combo), name);
+               gtk_combo_box_append_text(combo, name);
                g_free(name);
                link = link->next;
        }
        g_list_free(drives);
-       gtk_combo_box_set_active (GTK_COMBO_BOX(combo), 0);
-       gtk_box_pack_start (vbox, GTK_WIDGET(hbox), FALSE, FALSE, 1);
-       gtk_widget_show(GTK_WIDGET(hbox));
-       gtk_box_pack_start (hbox, label, FALSE, FALSE, 1);
-       gtk_widget_show(label);
-       gtk_box_pack_start (hbox, combo, FALSE, FALSE, 2);
-       gtk_widget_show(combo);
-       gtk_box_pack_start (hbox, blank, TRUE, TRUE, 1);
-       gtk_widget_show(blank);
-       // Ugly hackish global alert
-       dvd_device_combo = combo;
-       g_signal_connect(combo, "changed", (GCallback)dvd_device_changed_cb, dialog);
-       g_signal_connect(dialog, "selection-changed", (GCallback)chooser_file_selected_cb, combo);
-
-       g_signal_connect(checkbutton, "toggled", (GCallback)source_type_changed_cb, dialog);
-       return GTK_WIDGET(vbox);
 }
 
-static void
-do_scan(signal_user_data_t *ud, const gchar *filename)
+extern GValue *ghb_queue_edit_settings;
+static gchar *last_scan_file = NULL;
+
+void
+ghb_do_scan(
+       signal_user_data_t *ud, 
+       const gchar *filename, 
+       gint titlenum, 
+       gboolean force)
 {
+       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_queue_edit_settings = NULL;
+               }
+               return;
+       }
+       if (last_scan_file != NULL)
+               g_free(last_scan_file);
+       last_scan_file = NULL;
        if (filename != NULL)
        {
+               last_scan_file = g_strdup(filename);
                ghb_settings_set_string(ud->settings, "source", filename);
                if (update_source_label(ud, filename))
                {
@@ -559,8 +624,10 @@ do_scan(signal_user_data_t *ud, const gchar *filename)
                        path = ghb_settings_get_string( ud->settings, "source");
                        gtk_progress_bar_set_fraction (progress, 0);
                        gtk_progress_bar_set_text (progress, "Scanning ...");
-                       ghb_hb_cleanup(TRUE);
-                       ghb_backend_scan (path, 0);
+                       prune_logs(ud);
+                       gint preview_count;
+                       preview_count = ghb_settings_get_int(ud->settings, "preview_count");
+                       ghb_backend_scan(path, titlenum, preview_count);
                        g_free(path);
                }
                else
@@ -570,11 +637,24 @@ do_scan(signal_user_data_t *ud, const gchar *filename)
        }
 }
 
-void
-source_button_clicked_cb(GtkButton *button, signal_user_data_t *ud)
+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;
-       GtkWidget *widget;
        gchar *sourcename;
        gint    response;
        GtkFileChooserAction action;
@@ -592,15 +672,20 @@ source_button_clicked_cb(GtkButton *button, signal_user_data_t *ud)
        {
                action = GTK_FILE_CHOOSER_ACTION_OPEN;
        }
-       dialog = gtk_file_chooser_dialog_new ("Select Source",
-                                                               NULL,
-                                                               action,
-                                                               GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
-                                                               GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
-                                                               NULL);
-       widget = source_dialog_extra_widgets(dialog, checkbutton_active);
-       gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), widget);
-       gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), sourcename);
+       GtkWidget *widget;
+       widget = GHB_WIDGET(ud->builder, "single_title_box");
+       if (single)
+               gtk_widget_show(widget);
+       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);
        response = gtk_dialog_run(GTK_DIALOG (dialog));
        gtk_widget_hide(dialog);
        if (response == GTK_RESPONSE_ACCEPT)
@@ -610,10 +695,17 @@ source_button_clicked_cb(GtkButton *button, signal_user_data_t *ud)
                filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
                if (filename != NULL)
                {
-                       do_scan(ud, filename);
+                       gint titlenum;
+
+                       if (single)
+                               titlenum = ghb_settings_get_int(ud->settings, "single_title");
+                       else
+                               titlenum = 0;
+                       ghb_do_scan(ud, filename, titlenum, TRUE);
                        if (strcmp(sourcename, filename) != 0)
                        {
-                               ghb_settings_set_string (ud->settings, "default_source", filename);
+                               ghb_settings_set_string (ud->settings, 
+                                                                               "default_source", filename);
                                ghb_pref_save (ud->settings, "default_source");
                                ghb_dvd_set_current (filename, ud);
                        }
@@ -621,7 +713,18 @@ source_button_clicked_cb(GtkButton *button, signal_user_data_t *ud)
                }
        }
        g_free(sourcename);
-       gtk_widget_destroy(dialog);
+}
+
+void
+source_button_clicked_cb(GtkButton *button, signal_user_data_t *ud)
+{
+       do_source_dialog(button, FALSE, ud);
+}
+
+void
+single_title_source_cb(GtkButton *button, signal_user_data_t *ud)
+{
+       do_source_dialog(button, TRUE, ud);
 }
 
 void
@@ -632,7 +735,7 @@ dvd_source_activate_cb(GtkAction *action, signal_user_data_t *ud)
 
        sourcename = ghb_settings_get_string(ud->settings, "source");
        filename = gtk_action_get_name(action);
-       do_scan(ud, filename);
+       ghb_do_scan(ud, filename, 0, TRUE);
        if (strcmp(sourcename, filename) != 0)
        {
                ghb_settings_set_string (ud->settings, "default_source", filename);
@@ -652,8 +755,8 @@ update_destination_extension(signal_user_data_t *ud)
        GtkEntry *entry;
 
        g_debug("update_destination_extension ()");
-       extension = ghb_settings_get_string(ud->settings, "container");
-       entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "destination"));
+       extension = ghb_settings_get_string(ud->settings, "FileFormat");
+       entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "dest_file"));
        filename = g_strdup(gtk_entry_get_text(entry));
        for (ii = 0; containers[ii] != NULL; ii++)
        {
@@ -675,7 +778,7 @@ update_destination_extension(signal_user_data_t *ud)
                                break;
                        }
                        new_name = g_strjoin(".", filename, extension, NULL); 
-                       ghb_ui_update(ud, "destination", ghb_string_value(new_name));
+                       ghb_ui_update(ud, "dest_file", ghb_string_value(new_name));
                        g_free(new_name);
                        break;
                }
@@ -706,6 +809,7 @@ destination_select_title(GtkEntry *entry)
                        break;
                }
        }
+       if (start < 0) start = 0;
        if (start < end)
        {
                gtk_editable_select_region(GTK_EDITABLE(entry), start, end);
@@ -724,20 +828,39 @@ destination_grab_cb(
 static gboolean update_default_destination = FALSE;
 
 void
-destination_entry_changed_cb(GtkEntry *entry, signal_user_data_t *ud)
+dest_dir_set_cb(GtkFileChooserButton *dest_chooser, signal_user_data_t *ud)
 {
-       gchar *dest;
+       gchar *dest_file, *dest_dir, *dest;
        
-       g_debug("destination_entry_changed_cb ()");
-       if ((dest = expand_tilde(gtk_entry_get_text(entry))) != NULL)
-       {
-               gtk_entry_set_text(entry, dest);
-               g_free(dest);
-       }
+       g_debug("dest_dir_set_cb ()");
+       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);
+       ghb_settings_set_string(ud->settings, "destination", dest);
+       g_free(dest_file);
+       g_free(dest_dir);
+       g_free(dest);
+       update_default_destination = TRUE;
+}
+
+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_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);
+       ghb_settings_set_string(ud->settings, "destination", dest);
+       g_free(dest_file);
+       g_free(dest_dir);
+       g_free(dest);
        update_default_destination = TRUE;
 }
 
@@ -764,18 +887,18 @@ destination_browse_clicked_cb(GtkButton *button, signal_user_data_t *ud)
        g_free(basename);
        if (gtk_dialog_run(GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
        {
-               char *filename;
+               char *filename, *dirname;
+               GtkFileChooser *dest_chooser;
                
                filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
-               entry = (GtkEntry*)GHB_WIDGET(ud->builder, "destination");
-               if (entry == NULL)
-               {
-                       g_debug("Failed to find widget: %s", "destination");
-               }
-               else
-               {
-                       gtk_entry_set_text(entry, filename);
-               }
+               basename = g_path_get_basename(filename);
+               dirname = g_path_get_dirname(filename);
+               entry = (GtkEntry*)GHB_WIDGET(ud->builder, "dest_file");
+               gtk_entry_set_text(entry, basename);
+               dest_chooser = GTK_FILE_CHOOSER(GHB_WIDGET(ud->builder, "dest_dir"));
+               gtk_file_chooser_set_filename(dest_chooser, dirname);
+               g_free (dirname);
+               g_free (basename);
                g_free (filename);
        }
        gtk_widget_destroy(dialog);
@@ -786,6 +909,7 @@ window_destroy_event_cb(GtkWidget *widget, GdkEvent *event, signal_user_data_t *
 {
        g_debug("window_destroy_event_cb ()");
        ghb_hb_cleanup(FALSE);
+       prune_logs(ud);
        gtk_main_quit();
        return FALSE;
 }
@@ -800,12 +924,14 @@ window_delete_event_cb(GtkWidget *widget, GdkEvent *event, signal_user_data_t *u
                if (ghb_cancel_encode("Closing HandBrake will terminate encoding.\n"))
                {
                        ghb_hb_cleanup(FALSE);
+                       prune_logs(ud);
                        gtk_main_quit();
                        return FALSE;
                }
                return TRUE;
        }
        ghb_hb_cleanup(FALSE);
+       prune_logs(ud);
        gtk_main_quit();
        return FALSE;
 }
@@ -826,16 +952,17 @@ container_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        ghb_check_dependency(ud, widget);
        update_acodec_combo(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, "container");
+               container = ghb_settings_get_string(ud->settings, "FileFormat");
                if (strcmp(container, "mp4") == 0)
                {
-                       ghb_ui_update(ud, "container", ghb_string_value("m4v"));
+                       ghb_ui_update(ud, "FileFormat", ghb_string_value("m4v"));
                }
                g_free(container);
        }
@@ -904,10 +1031,11 @@ show_title_info(signal_user_data_t *ud, ghb_title_info_t *tinfo)
        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;
-       keep_aspect = ghb_settings_get_boolean(ud->settings, "keep_aspect");
-       anamorphic = ghb_settings_get_boolean(ud->settings, "anamorphic");
-       if (!(keep_aspect || anamorphic))
+       gboolean keep_aspect;
+       gint pic_par;
+       keep_aspect = ghb_settings_get_boolean(ud->settings, "PictureKeepRatio");
+       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]));
@@ -917,97 +1045,61 @@ show_title_info(signal_user_data_t *ud, ghb_title_info_t *tinfo)
        // you pass it a cropped width or height == 0.
        gint bound;
        bound = tinfo->height / 2 - 2;
-       widget = GHB_WIDGET (ud->builder, "crop_top");
+       widget = GHB_WIDGET (ud->builder, "PictureTopCrop");
        gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
-       widget = GHB_WIDGET (ud->builder, "crop_bottom");
+       widget = GHB_WIDGET (ud->builder, "PictureBottomCrop");
        gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
        bound = tinfo->width / 2 - 2;
-       widget = GHB_WIDGET (ud->builder, "crop_left");
+       widget = GHB_WIDGET (ud->builder, "PictureLeftCrop");
        gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
-       widget = GHB_WIDGET (ud->builder, "crop_right");
+       widget = GHB_WIDGET (ud->builder, "PictureRightCrop");
        gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
-       if (ghb_settings_get_boolean(ud->settings, "autocrop"))
+       if (ghb_settings_get_boolean(ud->settings, "PictureAutoCrop"))
        {
-               ghb_ui_update(ud, "crop_top", ghb_int64_value(tinfo->crop[0]));
-               ghb_ui_update(ud, "crop_bottom", ghb_int64_value(tinfo->crop[1]));
-               ghb_ui_update(ud, "crop_left", ghb_int64_value(tinfo->crop[2]));
-               ghb_ui_update(ud, "crop_right", ghb_int64_value(tinfo->crop[3]));
+               ghb_ui_update(ud, "PictureTopCrop", ghb_int64_value(tinfo->crop[0]));
+               ghb_ui_update(ud, "PictureBottomCrop", ghb_int64_value(tinfo->crop[1]));
+               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);
+       gint width, height, crop[4];
+       crop[0] = ghb_settings_get_int(ud->settings, "PictureTopCrop");
+       crop[1] = ghb_settings_get_int(ud->settings, "PictureBottomCrop");
+       crop[2] = ghb_settings_get_int(ud->settings, "PictureLeftCrop");
+       crop[3] = ghb_settings_get_int(ud->settings, "PictureRightCrop");
+       width = tinfo->width - crop[2] - crop[3];
+       height = tinfo->height - crop[0] - crop[1];
+       widget = GHB_WIDGET (ud->builder, "crop_dimensions");
+       text = g_strdup_printf ("%d x %d", width, height);
+       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");
        gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), 1);
+       gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, tinfo->num_chapters);
 }
 
-static gint preview_button_width;
-static gint preview_button_height;
 static gboolean update_preview = FALSE;
 
-static void
-set_preview_image(signal_user_data_t *ud)
-{
-       GtkWidget *widget;
-       gint preview_width, preview_height, target_height, width, height;
-
-       g_debug("set_preview_button_image ()");
-       gint titleindex;
-
-       titleindex = ghb_settings_combo_int(ud->settings, "title");
-       if (titleindex < 0) return;
-       widget = GHB_WIDGET (ud->builder, "preview_frame");
-       gint frame = ghb_widget_int(widget) - 1;
-       GdkPixbuf *preview = ghb_get_preview_image (titleindex, frame, ud->settings, TRUE);
-       if (preview == NULL) return;
-       widget = GHB_WIDGET (ud->builder, "preview_image");
-       gtk_image_set_from_pixbuf(GTK_IMAGE(widget), preview);
-
-       preview_width = gdk_pixbuf_get_width(preview);
-       preview_height = gdk_pixbuf_get_height(preview);
-       gchar *text = g_strdup_printf("%d x %d", preview_width, preview_height);
-       widget = GHB_WIDGET (ud->builder, "preview_dims");
-       gtk_label_set_text(GTK_LABEL(widget), text);
-       g_free(text);
-       
-       g_debug("preview %d x %d", preview_width, preview_height);
-       target_height = MIN(preview_button_height - 12, 128);
-       height = target_height;
-       width = preview_width * height / preview_height;
-
-       if ((height >= 16) && (width >= 16))
-       {
-               GdkPixbuf *scaled_preview;
-               scaled_preview = gdk_pixbuf_scale_simple (preview, width, height, GDK_INTERP_NEAREST);
-               if (scaled_preview != NULL)
-               {
-                       g_object_unref (preview);
-                       
-                       widget = GHB_WIDGET (ud->builder, "preview_button_image");
-                       gtk_image_set_from_pixbuf(GTK_IMAGE(widget), scaled_preview);
-                       g_object_unref (scaled_preview);
-               }
-       }
-}
-
 void
 title_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
 {
        ghb_title_info_t tinfo;
        gint titleindex;
-       gchar *preset;
        
        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, "audio_track", titleindex, FALSE);
-       ghb_update_ui_combo_box (ud->builder, "subtitle_lang", titleindex, FALSE);
+       ghb_update_ui_combo_box (ud->builder, "AudioTrack", titleindex, FALSE);
+       ghb_update_ui_combo_box (ud->builder, "Subtitles", titleindex, FALSE);
 
-       preset = ghb_settings_get_string (ud->settings, "preset");
-       ghb_update_from_preset(ud, preset, "subtitle_lang");
-       g_free(preset);
+       ghb_update_from_preset(ud, "Subtitles");
        if (ghb_get_title_info (&tinfo, titleindex))
        {
                show_title_info(ud, &tinfo);
@@ -1018,16 +1110,25 @@ title_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        if (ghb_settings_get_boolean(ud->settings, "vquality_type_target"))
        {
                gint bitrate = ghb_calculate_target_bitrate (ud->settings, titleindex);
-               ghb_ui_update(ud, "video_bitrate", ghb_int64_value(bitrate));
+               ghb_ui_update(ud, "VideoAvgBitrate", ghb_int64_value(bitrate));
        }
 
        // Unfortunately, there is no way to query how many frames were
-       // actually generated during the scan.  It attempts to make 10.
+       // actually generated during the scan.
        // If I knew how many were generated, I would adjust the spin
        // control range here.
-       ghb_ui_update(ud, "preview_frame", ghb_int64_value(1));
+       // I do know how many were asked for.
+       gint preview_count;
+       preview_count = ghb_settings_get_int(ud->settings, "preview_count");
+       widget = GHB_WIDGET(ud->builder, "preview_frame");
+       gtk_range_set_range (GTK_RANGE(widget), 1, preview_count);
+       ghb_ui_update(ud, "preview_frame", ghb_int64_value(2));
 
-       set_preview_image (ud);
+       ghb_set_preview_image (ud);
+       if (ghb_settings_get_boolean(ud->settings, "title_no_in_destination"))
+       {
+               set_destination(ud);
+       }
 }
 
 void
@@ -1036,59 +1137,31 @@ setting_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        ghb_widget_to_setting(ud->settings, widget);
        ghb_check_dependency(ud, widget);
        ghb_clear_presets_selection(ud);
+       ghb_live_reset(ud);
 }
 
-static void
-validate_filter_widget(signal_user_data_t *ud, const gchar *name)
-{
-       GtkTreeModel *store;
-       GtkTreeIter iter;
-       const gchar *str;
-       gboolean foundit = FALSE;
-       GtkComboBox *combo = GTK_COMBO_BOX(GHB_WIDGET(ud->builder, name));
-       if (gtk_combo_box_get_active(combo) < 0)
-       { // Validate user input
-               gchar *val = ghb_settings_get_string(ud->settings, name);
-               store = gtk_combo_box_get_model(combo);
-               // Check to see if user manually entered one of the combo options
-               if (gtk_tree_model_get_iter_first(store, &iter))
-               {
-                       do
-                       {
-                               gtk_tree_model_get(store, &iter, 0, &str, -1);
-                               if (strcasecmp(val, str) == 0)
-                               {
-                                       gtk_combo_box_set_active_iter(combo, &iter);
-                                       foundit = TRUE;
-                                       break;
-                               }
-                       } while (gtk_tree_model_iter_next(store, &iter));
-               }
-               if (!foundit)
-               { // validate format of filter string
-                       if (!ghb_validate_filter_string(val, -1))
-                               gtk_combo_box_set_active(combo, 0);
-               }
-               g_free(val);
-       }
-}
-
-gboolean
-deint_tweak_focus_out_cb(GtkWidget *widget, GdkEventFocus *event, 
-       signal_user_data_t *ud)
+void
+vquality_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
 {
-       g_debug("deint_tweak_focus_out_cb ()");
-       validate_filter_widget(ud, "tweak_deinterlace");
-       return FALSE;
-}
+       ghb_widget_to_setting(ud->settings, widget);
+       ghb_check_dependency(ud, widget);
+       ghb_clear_presets_selection(ud);
+       ghb_live_reset(ud);
 
-gboolean
-denoise_tweak_focus_out_cb(GtkWidget *widget, GdkEventFocus *event, 
-       signal_user_data_t *ud)
-{
-       g_debug("denoise_tweak_focus_out_cb ()");
-       validate_filter_widget(ud, "tweak_noise");
-       return FALSE;
+       gint vcodec = ghb_settings_combo_int(ud->settings, "VideoEncoder");
+       gdouble step;
+       if (vcodec == HB_VCODEC_X264)
+       {
+               step = ghb_settings_combo_double(ud->settings, 
+                                                                                       "VideoQualityGranularity");
+       }
+       else
+       {
+               step = 1;
+       }
+       gdouble val = gtk_range_get_value(GTK_RANGE(widget));
+       val = ((int)((val + step / 2) / step)) * step;
+       gtk_range_set_value(GTK_RANGE(widget), val);
 }
 
 void
@@ -1097,6 +1170,7 @@ http_opt_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        ghb_widget_to_setting(ud->settings, widget);
        ghb_check_dependency(ud, widget);
        ghb_clear_presets_selection(ud);
+       ghb_live_reset(ud);
        // AC3 is not allowed when Web optimized
        ghb_grey_combo_options (ud->builder);
 }
@@ -1104,30 +1178,37 @@ http_opt_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
 void
 vcodec_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
 {
-       gint vqmin, vqmax;
+       gdouble vqmin, vqmax, step, page;
+       gboolean inverted;
+       gint digits;
 
        ghb_widget_to_setting(ud->settings, widget);
        ghb_check_dependency(ud, widget);
        ghb_clear_presets_selection(ud);
-       ghb_vquality_range(ud, &vqmin, &vqmax);
-       GtkWidget *qp = GHB_WIDGET(ud->builder, "video_quality");
+       ghb_live_reset(ud);
+       ghb_vquality_range(ud, &vqmin, &vqmax, &step, &page, &digits, &inverted);
+       GtkWidget *qp = GHB_WIDGET(ud->builder, "VideoQualitySlider");
        gtk_range_set_range (GTK_RANGE(qp), vqmin, vqmax);
+       gtk_range_set_increments (GTK_RANGE(qp), step, page);
+       gtk_scale_set_digits(GTK_SCALE(qp), digits);
+       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("setting_widget_changed_cb () %s", name);
+       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, "video_bitrate", ghb_int64_value(bitrate));
+               ghb_ui_update(ud, "VideoAvgBitrate", ghb_int64_value(bitrate));
        }
 }
 
@@ -1184,6 +1265,7 @@ scale_width_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        text = g_strdup_printf ("%d x %d", width, height);
        gtk_label_set_text (GTK_LABEL(widget), text);
        g_free(text);
+       ghb_live_reset(ud);
 }
 
 void
@@ -1201,6 +1283,7 @@ scale_height_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        text = g_strdup_printf ("%d x %d", width, height);
        gtk_label_set_text (GTK_LABEL(widget), text);
        g_free(text);
+       ghb_live_reset(ud);
 }
 
 void
@@ -1214,10 +1297,10 @@ crop_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        ghb_check_dependency(ud, widget);
        ghb_set_scale (ud, GHB_SCALE_KEEP_NONE);
 
-       crop[0] = ghb_settings_get_int(ud->settings, "crop_top");
-       crop[1] = ghb_settings_get_int(ud->settings, "crop_bottom");
-       crop[2] = ghb_settings_get_int(ud->settings, "crop_left");
-       crop[3] = ghb_settings_get_int(ud->settings, "crop_right");
+       crop[0] = ghb_settings_get_int(ud->settings, "PictureTopCrop");
+       crop[1] = ghb_settings_get_int(ud->settings, "PictureBottomCrop");
+       crop[2] = ghb_settings_get_int(ud->settings, "PictureLeftCrop");
+       crop[3] = ghb_settings_get_int(ud->settings, "PictureRightCrop");
        titleindex = ghb_settings_combo_int(ud->settings, "title");
        if (ghb_get_title_info (&tinfo, titleindex))
        {
@@ -1237,6 +1320,7 @@ crop_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        gtk_label_set_text (GTK_LABEL(widget), text);
        g_free(text);
        update_preview = TRUE;
+       ghb_live_reset(ud);
 }
 
 void
@@ -1246,18 +1330,36 @@ scale_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        ghb_widget_to_setting(ud->settings, widget);
        ghb_check_dependency(ud, widget);
        ghb_clear_presets_selection(ud);
+       ghb_live_reset(ud);
        ghb_set_scale (ud, GHB_SCALE_KEEP_NONE);
        update_preview = TRUE;
        
        gchar *text;
        
-       text = ghb_settings_get_boolean(ud->settings, "autocrop") ? "On" : "Off";
+       text = ghb_settings_get_boolean(ud->settings, "PictureAutoCrop") ? "On" : "Off";
        widget = GHB_WIDGET (ud->builder, "crop_auto");
        gtk_label_set_text (GTK_LABEL(widget), text);
        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";
+       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);
 }
@@ -1300,7 +1402,7 @@ ghb_message_dialog(GtkMessageType type, const gchar *message, const gchar *no, c
        // Toss up a warning dialog
        dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
                                                        type, GTK_BUTTONS_NONE,
-                                                       message);
+                                                       "%s", message);
        gtk_dialog_add_buttons( GTK_DIALOG(dialog), 
                                                   no, GTK_RESPONSE_NO,
                                                   yes, GTK_RESPONSE_YES, NULL);
@@ -1339,9 +1441,20 @@ static void
 submit_job(GValue *settings)
 {
        static gint unique_id = 1;
+       gchar *type, *modified, *preset;
+       const GValue *path;
+       gboolean preset_modified;
 
        g_debug("submit_job");
        if (settings == NULL) return;
+       preset_modified = ghb_settings_get_boolean(settings, "preset_modified");
+       path = ghb_settings_get_value(settings, "preset");
+       preset = ghb_preset_path_string(path);
+       type = ghb_preset_is_custom() ? "Custom " : "";
+       modified = preset_modified ? "Modified " : "";
+       ghb_log("%s%sPreset: %s", modified, type, preset);
+       g_free(preset);
+
        ghb_settings_set_int(settings, "job_unique_id", unique_id);
        ghb_settings_set_int(settings, "job_status", GHB_QUEUE_RUNNING);
        ghb_add_job (settings, unique_id);
@@ -1350,10 +1463,88 @@ submit_job(GValue *settings)
 }
 
 static void
-queue_scan(GValue *js)
+prune_logs(signal_user_data_t *ud)
+{
+       gchar *dest_dir;
+
+       // Only prune logs stored in the default config dir location
+       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;
+               GDir *gdir = g_dir_open(dest_dir, 0, NULL);
+               time_t now;
+
+               now = time(NULL);
+               file = g_dir_read_name(gdir);
+               while (file)
+               {
+                       gchar *path;
+                       struct stat stbuf;
+
+                       path = g_strdup_printf("%s/%s", dest_dir, file);
+                       g_stat(path, &stbuf);
+                       if (now - stbuf.st_mtime > week)
+                       {
+                               g_unlink(path);
+                       }
+                       g_free(path);
+                       file = g_dir_read_name(gdir);
+               }
+               g_dir_close(gdir);
+       }
+       g_free(dest_dir);
+       ghb_preview_cleanup(ud);
+}
+
+static void
+queue_scan(signal_user_data_t *ud, GValue *js)
 {
        gchar *path;
        gint titlenum;
+       time_t  _now;
+       struct tm *now;
+       gchar *log_path, *pos, *destname, *basename, *dest_dir;
+
+       _now = time(NULL);
+       now = localtime(&_now);
+       destname = ghb_settings_get_string(js, "destination");
+       basename = g_path_get_basename(destname);
+       if (ghb_settings_get_boolean(ud->settings, "EncodeLogLocation"))
+       {
+               dest_dir = g_path_get_dirname (destname);
+       }
+       else
+       {
+               dest_dir = ghb_get_user_config_dir("EncodeLogs");
+       }
+       g_free(destname);
+       pos = g_strrstr( basename, "." );
+       if (pos != NULL)
+       {
+               *pos = 0;
+       }
+       log_path = g_strdup_printf("%s/%d-%02d-%02d %02d-%02d-%02d %s.log",
+               dest_dir,
+               now->tm_year + 1900, now->tm_mon + 1, now->tm_mday,
+               now->tm_hour, now->tm_min, now->tm_sec, basename);
+       g_free(basename);
+       g_free(dest_dir);
+       if (ud->job_activity_log)
+               g_io_channel_unref(ud->job_activity_log);
+       ud->job_activity_log = g_io_channel_new_file (log_path, "w", NULL);
+       if (ud->job_activity_log)
+       {
+               gchar *ver_str;
+
+               ver_str = g_strdup_printf("Handbrake Version: %s (%d)\n", 
+                                                                       hb_get_version(NULL), hb_get_build(NULL));
+               g_io_channel_write_chars (ud->job_activity_log, ver_str, 
+                                                                       -1, NULL, NULL);
+               g_free(ver_str);
+       }
+       g_free(log_path);
 
        path = ghb_settings_get_string( js, "source");
        titlenum = ghb_settings_get_int(js, "titlenum");
@@ -1382,11 +1573,14 @@ ghb_start_next_job(signal_user_data_t *ud, gboolean find_first)
                        if (status == GHB_QUEUE_PENDING)
                        {
                                current = ii;
-                               queue_scan(js);
+                               ghb_inhibit_gpm();
+                               queue_scan(ud, js);
                                return js;
                        }
                }
                // Nothing pending
+               ghb_uninhibit_gpm();
+               ghb_notify_done(ud);
                return NULL;
        }
        // Find the next pending item after the current running item
@@ -1403,7 +1597,8 @@ ghb_start_next_job(signal_user_data_t *ud, gboolean find_first)
                                if (status == GHB_QUEUE_PENDING)
                                {
                                        current = jj;
-                                       queue_scan(js);
+                                       ghb_inhibit_gpm();
+                                       queue_scan(ud, js);
                                        return js;
                                }
                        }
@@ -1418,11 +1613,14 @@ ghb_start_next_job(signal_user_data_t *ud, gboolean find_first)
                if (status == GHB_QUEUE_PENDING)
                {
                        current = ii;
-                       queue_scan(js);
+                       ghb_inhibit_gpm();
+                       queue_scan(ud, js);
                        return js;
                }
        }
        // Nothing found
+       ghb_uninhibit_gpm();
+       ghb_notify_done(ud);
        return NULL;
 }
 
@@ -1450,7 +1648,7 @@ find_queue_job(GValue *queue, gint unique_id, GValue **job)
 }
 
 gchar*
-working_status_string(signal_user_data_t *ud, ghb_status_t *status)
+working_status_string(signal_user_data_t *ud, ghb_instance_status_t *status)
 {
        gchar *task_str, *job_str, *status_str;
        gint qcount;
@@ -1518,26 +1716,26 @@ ghb_backend_events(signal_user_data_t *ud)
        progress = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "progressbar"));
        // First handle the status of title scans
        // Then handle the status of the queue
-       if (status.state & GHB_STATE_SCANNING)
+       if (status.scan.state & GHB_STATE_SCANNING)
        {
-               if (status.title_cur == 0)
+               if (status.scan.title_cur == 0)
                {
                        status_str = g_strdup ("Scanning...");
                }
                else
                {
                        status_str = g_strdup_printf ("Scanning title %d of %d...", 
-                                                                 status.title_cur, status.title_count );
+                                                         status.scan.title_cur, status.scan.title_count );
                }
                gtk_progress_bar_set_text (progress, status_str);
                g_free(status_str);
-               if (status.title_count > 0)
+               if (status.scan.title_count > 0)
                {
                        gtk_progress_bar_set_fraction (progress, 
-                               (gdouble)status.title_cur / status.title_count);
+                               (gdouble)status.scan.title_cur / status.scan.title_count);
                }
        }
-       else if (status.state & GHB_STATE_SCANDONE)
+       else if (status.scan.state & GHB_STATE_SCANDONE)
        {
                status_str = g_strdup_printf ("Scan done"); 
                gtk_progress_bar_set_text (progress, status_str);
@@ -1558,46 +1756,60 @@ ghb_backend_events(signal_user_data_t *ud)
                        gtk_progress_bar_set_fraction (progress, 0);
                        gtk_progress_bar_set_text (progress, "No Source");
                }
-               ghb_clear_state(GHB_STATE_SCANDONE);
+               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_queue_edit_settings = NULL;
+               }
        }
-       else if (status.queue_state & GHB_STATE_SCANNING)
+       else 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);
                gtk_progress_bar_set_fraction (progress, 0);
        }
-       else if (status.queue_state & GHB_STATE_SCANDONE)
+       else if (status.queue.state & GHB_STATE_SCANDONE)
        {
                ghb_clear_queue_state(GHB_STATE_SCANDONE);
+               usleep(2000000);
                submit_job(ud->current_job);
        }
-       else if (status.queue_state & GHB_STATE_PAUSED)
+       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);
        }
-       else if (status.queue_state & GHB_STATE_WORKING)
+       else if (status.queue.state & GHB_STATE_WORKING)
        {
-               status_str = working_status_string(ud, &status);
+               status_str = working_status_string(ud, &status.queue);
                gtk_progress_bar_set_text (progress, status_str);
-               gtk_progress_bar_set_fraction (progress, status.progress);
+               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_WORKDONE)
        {
                gint qstatus;
 
                work_started = FALSE;
                ghb_queue_buttons_grey(ud, FALSE);
-               index = find_queue_job(ud->queue, status.unique_id, &js);
+               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.error = GHB_ERROR_CANCELED;
-               switch( status.error )
+                       status.queue.error = GHB_ERROR_CANCELED;
+               switch( status.queue.error )
                {
                        case GHB_ERROR_NONE:
                                gtk_progress_bar_set_text( progress, "Rip done!" );
@@ -1643,20 +1855,28 @@ ghb_backend_events(signal_user_data_t *ud)
                }
                gtk_progress_bar_set_fraction (progress, 1.0);
                ghb_clear_queue_state(GHB_STATE_WORKDONE);
+               if (ud->job_activity_log)
+                       g_io_channel_unref(ud->job_activity_log);
+               ud->job_activity_log = NULL;
                if (!ud->cancel_encode)
+               {
                        ud->current_job = ghb_start_next_job(ud, FALSE);
+               }
                else
+               {
+                       ghb_uninhibit_gpm();
                        ud->current_job = NULL;
+               }
                if (js)
                        ghb_settings_set_int(js, "job_status", qstatus);
                ghb_save_queue(ud->queue);
                ud->cancel_encode = FALSE;
        }
-       else if (status.queue_state & GHB_STATE_MUXING)
+       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)
+       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
@@ -1666,7 +1886,7 @@ ghb_backend_events(signal_user_data_t *ud)
                        ghb_queue_buttons_grey(ud, TRUE);
                }
        }
-       if (status.queue_state & GHB_STATE_WORKING)
+       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
@@ -1675,8 +1895,8 @@ ghb_backend_events(signal_user_data_t *ud)
                        work_started = TRUE;
                        ghb_queue_buttons_grey(ud, TRUE);
                }
-               index = find_queue_job(ud->queue, status.unique_id, &js);
-               if (status.unique_id != 0 && index >= 0)
+               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;
@@ -1694,11 +1914,36 @@ ghb_backend_events(signal_user_data_t *ud)
                GtkLabel *label;
                gchar *status_str;
 
-               status_str = working_status_string(ud, &status);
+               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;
+               live_progress = GTK_PROGRESS_BAR(
+                       GHB_WIDGET(ud->builder, "live_encode_progress"));
+               status_str = working_status_string(ud, &status.scan);
+               gtk_progress_bar_set_text (live_progress, status_str);
+               gtk_progress_bar_set_fraction (live_progress, status.scan.progress);
+               g_free(status_str);
+       }
+       if (status.scan.state & GHB_STATE_WORKDONE)
+       {
+               switch( status.scan.error )
+               {
+                       case GHB_ERROR_NONE:
+                       {
+                               ghb_live_encode_done(ud, TRUE);
+                       } break;
+                       default:
+                       {
+                               ghb_live_encode_done(ud, FALSE);
+                       } break;
+               }
+               ghb_clear_scan_state(GHB_STATE_WORKDONE);
+       }
 }
 
 gboolean
@@ -1706,6 +1951,7 @@ ghb_timer_cb(gpointer data)
 {
        signal_user_data_t *ud = (signal_user_data_t*)data;
 
+       ghb_live_preview_progress(ud);
        ghb_backend_events(ud);
        if (update_default_destination)
        {
@@ -1725,7 +1971,7 @@ ghb_timer_cb(gpointer data)
        }
        if (update_preview)
        {
-               set_preview_image (ud);
+               ghb_set_preview_image (ud);
                update_preview = FALSE;
        }
        return TRUE;
@@ -1757,7 +2003,7 @@ ghb_log_cb(GIOChannel *source, GIOCondition cond, gpointer data)
                buffer = gtk_text_view_get_buffer (textview);
                // I would like to auto-scroll the window when the scrollbar
                // is at the bottom, 
-               // must determining whether the insert point is at
+               // must determine whether the insert point is at
                // the bottom of the window 
                window = gtk_text_view_get_window(textview, GTK_TEXT_WINDOW_TEXT);
                if (window != NULL)
@@ -1780,11 +2026,20 @@ ghb_log_cb(GIOChannel *source, GIOCondition cond, gpointer data)
                gtk_text_buffer_insert(buffer, &iter, text, -1);
                if (bottom)
                {
-                       //gtk_text_view_scroll_to_iter(textview, &iter, 0, FALSE, 0, 0);
-                       mark = gtk_text_buffer_get_insert (buffer);
+                       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);
                }
-               g_io_channel_write_chars (ud->activity_log, text, length, &length, NULL);
                g_free(text);
        }
        if (status != G_IO_STATUS_NORMAL)
@@ -1804,20 +2059,69 @@ ghb_log_cb(GIOChannel *source, GIOCondition cond, gpointer data)
        return TRUE;
 }
 
-void
-about_activate_cb(GtkWidget *xwidget, signal_user_data_t *ud)
-{
-       GtkWidget *widget = GHB_WIDGET (ud->builder, "hb_about");
-       gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(widget), ghb_version());
-       gtk_widget_show (widget);
-}
-
-void
-guide_activate_cb(GtkWidget *xwidget, signal_user_data_t *ud)
+static void
+set_visible(GtkWidget *widget, gboolean visible)
 {
-       gboolean result;
+       if (visible)
+       {
+               gtk_widget_show_now(widget);
+       }
+       else
+       {
+               gtk_widget_hide(widget);
+       }
+}
+
+void
+show_activity_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
+{
+       GtkWidget *widget = GHB_WIDGET (ud->builder, "activity_window");
+       set_visible(widget, gtk_toggle_tool_button_get_active(
+                                               GTK_TOGGLE_TOOL_BUTTON(xwidget)));
+}
+
+void
+show_activity_menu_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
+{
+       GtkWidget *widget = GHB_WIDGET (ud->builder, "activity_window");
+       set_visible(widget, TRUE);
+       widget = GHB_WIDGET (ud->builder, "show_activity");
+       gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), TRUE);
+}
+
+gboolean
+activity_window_delete_cb(GtkWidget *xwidget, GdkEvent *event, signal_user_data_t *ud)
+{
+       set_visible(xwidget, FALSE);
+       GtkWidget *widget = GHB_WIDGET (ud->builder, "show_activity");
+       gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), FALSE);
+       return TRUE;
+}
+
+void
+ghb_log(gchar *log, ...)
+{
+       va_list args;
+       time_t _now;
+    struct tm *now;
+       gchar fmt[362];
+
+       _now = time(NULL);
+       now = localtime( &_now );
+       snprintf(fmt, 362, "[%02d:%02d:%02d] lingui: %s\n", 
+                       now->tm_hour, now->tm_min, now->tm_sec, log);
+       va_start(args, log);
+       vfprintf(stderr, fmt, args);
+       va_end(args);
+}
+
+static void
+browse_url(const gchar *url)
+{
+       gboolean result;
        char *argv[] = 
-               {"xdg-open","http://trac.handbrake.fr/wiki/HandBrakeGuide",NULL,NULL};
+               {"xdg-open",NULL,NULL,NULL};
+       argv[1] = (gchar*)url;
        result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
                                NULL, NULL, NULL);
        if (result) return;
@@ -1839,38 +2143,79 @@ guide_activate_cb(GtkWidget *xwidget, signal_user_data_t *ud)
        argv[2] = NULL;
        result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
                                NULL, NULL, NULL);
-       if (result) return;
 }
 
 void
-hb_about_response_cb(GtkWidget *widget, gint response, signal_user_data_t *ud)
+about_web_hook(GtkAboutDialog *about, const gchar *link, gpointer data)
 {
-       gtk_widget_hide (widget);
+       browse_url(link);
 }
 
 void
-show_activity_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
+about_activate_cb(GtkWidget *xwidget, signal_user_data_t *ud)
 {
-       GtkWidget *widget = GHB_WIDGET (ud->builder, "activity_window");
+       GtkWidget *widget = GHB_WIDGET (ud->builder, "hb_about");
+       gchar *ver;
+
+       ver = g_strdup_printf("%s (%s)", HB_PROJECT_VERSION, HB_PROJECT_BUILD_ARCH);
+       gtk_about_dialog_set_url_hook(about_web_hook, NULL, NULL);
+       gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(widget), ver);
+       g_free(ver);
+       gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(widget), 
+                                                               HB_PROJECT_URL_WEBSITE);
+       gtk_about_dialog_set_website_label(GTK_ABOUT_DIALOG(widget), 
+                                                                               HB_PROJECT_URL_WEBSITE);
        gtk_widget_show (widget);
 }
 
 void
+guide_activate_cb(GtkWidget *xwidget, signal_user_data_t *ud)
+{
+       browse_url("http://trac.handbrake.fr/wiki/HandBrakeGuide");
+}
+
+void
+hb_about_response_cb(GtkWidget *widget, gint response, signal_user_data_t *ud)
+{
+       gtk_widget_hide (widget);
+}
+
+void
 show_queue_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
 {
        GtkWidget *widget = GHB_WIDGET (ud->builder, "queue_window");
-       gtk_widget_show (widget);
+       set_visible(widget, gtk_toggle_tool_button_get_active(
+                                               GTK_TOGGLE_TOOL_BUTTON(xwidget)));
 }
 
 void
-show_presets_toggled_cb(GtkToggleButton *button, signal_user_data_t *ud)
+show_queue_menu_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
+{
+       GtkWidget *widget = GHB_WIDGET (ud->builder, "queue_window");
+       set_visible(widget, TRUE);
+       widget = GHB_WIDGET (ud->builder, "show_queue");
+       gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), TRUE);
+}
+
+gboolean
+queue_window_delete_cb(GtkWidget *xwidget, GdkEvent *event, signal_user_data_t *ud)
+{
+       set_visible(xwidget, FALSE);
+       GtkWidget *widget = GHB_WIDGET (ud->builder, "show_queue");
+       gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), FALSE);
+       return TRUE;
+}
+
+void
+show_presets_toggled_cb(GtkWidget *action, signal_user_data_t *ud)
 {
        GtkWidget *widget;
        GtkWindow *hb_window;
        
        g_debug("show_presets_clicked_cb ()");
        widget = GHB_WIDGET (ud->builder, "presets_frame");
-       if (gtk_toggle_button_get_active(button))
+       ghb_widget_to_setting(ud->settings, action);
+       if (ghb_settings_get_boolean(ud->settings, "show_presets"))
        {
                gtk_widget_show_now(widget);
        }
@@ -1880,7 +2225,6 @@ show_presets_toggled_cb(GtkToggleButton *button, signal_user_data_t *ud)
                hb_window = GTK_WINDOW(GHB_WIDGET (ud->builder, "hb_window"));
                gtk_window_resize(hb_window, 16, 16);
        }
-       ghb_widget_to_setting(ud->settings, GTK_WIDGET(button));
        ghb_pref_save(ud->settings, "show_presets");
 }
 
@@ -1912,17 +2256,22 @@ update_chapter_list(signal_user_data_t *ud)
 
                        if (ii < count)
                        {
-                               gchar *chapter;
+                               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, chapter,
-                                       2, TRUE,
+                                       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);
                        }
@@ -1936,18 +2285,23 @@ update_chapter_list(signal_user_data_t *ud)
        }
        while (ii < count)
        {
-               gchar *chapter;
+               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, chapter,
-                       2, TRUE,
+                       1, duration,
+                       2, chapter,
+                       3, TRUE,
                        -1);
                g_free(chapter);
+               g_free(duration);
                ii++;
        }
 }
@@ -1989,12 +2343,12 @@ chapter_edited_cb(
        row = pi[0];
        gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, treepath);
        gtk_list_store_set(store, &iter, 
-               1, text,
-               2, TRUE,
+               2, text,
+               3, TRUE,
                -1);
        gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 0, &index, -1);
 
-       GValue *chapters;
+       const GValue *chapters;
        GValue *chapter;
 
        chapters = ghb_settings_get_value(ud->settings, "chapter_list");
@@ -2030,14 +2384,14 @@ chapter_edited_cb(
                // I got industrious and made my own CellTextRendererText that
                // passes on the key-press-event. So now I have much better
                // control of this.
-               column = gtk_tree_view_get_column(treeview, 1);
+               column = gtk_tree_view_get_column(treeview, 2);
                gtk_tree_view_set_cursor(treeview, treepath, column, TRUE);
        }
        else if (chapter_edit_key == GDK_Up && row > 0)
        {
                GtkTreeViewColumn *column;
                gtk_tree_path_prev(treepath);
-               column = gtk_tree_view_get_column(treeview, 1);
+               column = gtk_tree_view_get_column(treeview, 2);
                gtk_tree_view_set_cursor(treeview, treepath, column, TRUE);
        }
        gtk_tree_path_free (treepath);
@@ -2051,42 +2405,6 @@ chapter_list_selection_changed_cb(GtkTreeSelection *selection, signal_user_data_
 }
 
 void
-preview_button_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
-{
-       gint titleindex;
-
-       titleindex = ghb_settings_combo_int(ud->settings, "title");
-       if (titleindex < 0) return;
-       g_debug("titleindex %d", titleindex);
-
-       GtkWidget *widget = GHB_WIDGET (ud->builder, "preview_window");
-       gtk_widget_show (widget);
-}
-
-void
-preview_frame_value_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
-{
-       set_preview_image(ud);
-}
-
-void
-preview_button_size_allocate_cb(GtkWidget *widget, GtkAllocation *allocation, signal_user_data_t *ud)
-{
-       g_debug("-------------------------------allocate %d x %d", allocation->width, allocation->height);
-       if (preview_button_width == allocation->width &&
-               preview_button_height == allocation->height)
-       {
-               // Nothing to do. Bug out.
-               g_debug("nothing to do");
-               return;
-       }
-       g_debug("-------------------------------prev allocate %d x %d", preview_button_width, preview_button_height);
-       preview_button_width = allocation->width;
-       preview_button_height = allocation->height;
-       set_preview_image(ud);
-}
-
-void
 debug_log_handler(const gchar *domain, GLogLevelFlags flags, const gchar *msg, gpointer data)
 {
        signal_user_data_t *ud = (signal_user_data_t*)data;
@@ -2097,17 +2415,11 @@ debug_log_handler(const gchar *domain, GLogLevelFlags flags, const gchar *msg, g
        }
 }
 
-static void
-set_visible(GtkWidget *widget, gboolean visible)
+void
+warn_log_handler(const gchar *domain, GLogLevelFlags flags, const gchar *msg, gpointer data)
 {
-       if (visible)
-       {
-               gtk_widget_show_now(widget);
-       }
-       else
-       {
-               gtk_widget_hide(widget);
-       }
+       printf("mywarning\n");
+       printf("%s: %s\n", domain, msg);
 }
 
 void
@@ -2134,8 +2446,6 @@ ghb_hbfd(signal_user_data_t *ud, gboolean hbfd)
        set_visible(widget, !hbfd);
        widget = GHB_WIDGET(ud->builder, "presets_remove");
        set_visible(widget, !hbfd);
-       widget = GHB_WIDGET(ud->builder, "presets_default");
-       set_visible(widget, !hbfd);
        widget = GHB_WIDGET (ud->builder, "hb_window");
        gtk_window_resize(GTK_WINDOW(widget), 16, 16);
 
@@ -2162,39 +2472,31 @@ pref_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
 }
 
 void
-tweaks_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
+vqual_granularity_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
 {
-       g_debug("tweaks_changed_cb");
+       g_debug("vqual_granularity_changed_cb");
        ghb_widget_to_setting (ud->settings, widget);
+       ghb_check_dependency(ud, widget);
+
        const gchar *name = gtk_widget_get_name(widget);
        ghb_pref_save(ud->settings, name);
 
-       gboolean tweaks = ghb_settings_get_boolean(ud->settings, "allow_tweaks");
-       widget = GHB_WIDGET(ud->builder, "deinterlace");
-       tweaks ? gtk_widget_hide(widget) : gtk_widget_show(widget);
-       widget = GHB_WIDGET(ud->builder, "tweak_deinterlace");
-       !tweaks ? gtk_widget_hide(widget) : gtk_widget_show(widget);
+       gdouble vqmin, vqmax, step, page;
+       gboolean inverted;
+       gint digits;
 
-       widget = GHB_WIDGET(ud->builder, "denoise");
-       tweaks ? gtk_widget_hide(widget) : gtk_widget_show(widget);
-       widget = GHB_WIDGET(ud->builder, "tweak_denoise");
-       !tweaks ? gtk_widget_hide(widget) : gtk_widget_show(widget);
-       if (tweaks)
-       {
-               const GValue *value;
-               value = ghb_settings_get_value(ud->settings, "deinterlace");
-               ghb_ui_update(ud, "tweak_deinterlace", value);
-               value = ghb_settings_get_value(ud->settings, "denoise");
-               ghb_ui_update(ud, "tweak_denoise", value);
-       }
-       else
-       {
-               const GValue *value;
-               value = ghb_settings_get_value(ud->settings, "tweak_deinterlace");
-               ghb_ui_update(ud, "deinterlace", value);
-               value = ghb_settings_get_value(ud->settings, "tweak_denoise");
-               ghb_ui_update(ud, "denoise", value);
-       }
+       ghb_vquality_range(ud, &vqmin, &vqmax, &step, &page, &digits, &inverted);
+       GtkWidget *qp = GHB_WIDGET(ud->builder, "VideoQualitySlider");
+       gtk_range_set_increments (GTK_RANGE(qp), step, page);
+}
+
+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);
+       ghb_pref_save(ud->settings, name);
 }
 
 void
@@ -2285,8 +2587,6 @@ dvd_device_list()
        return dvd_devices;
 }
 
-
-static DBusConnection *dbus_connection = NULL;
 static LibHalContext *hal_ctx;
 
 gboolean
@@ -2308,9 +2608,10 @@ void
 drive_changed_cb(GVolumeMonitor *gvm, GDrive *gd, signal_user_data_t *ud)
 {
        gchar *device;
-       gint state = ghb_get_state();
+       gint state = ghb_get_scan_state();
        static gboolean first_time = TRUE;
 
+       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.
@@ -2319,7 +2620,6 @@ drive_changed_cb(GVolumeMonitor *gvm, GDrive *gd, signal_user_data_t *ud)
                first_time = FALSE;
                return;
        }
-       if (state != GHB_STATE_IDLE) return;
        device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
        
        // DVD insertion detected.  Scan it.
@@ -2332,62 +2632,165 @@ drive_changed_cb(GVolumeMonitor *gvm, GDrive *gd, signal_user_data_t *ud)
                        gtk_progress_bar_set_text (progress, "Scanning ...");
                        gtk_progress_bar_set_fraction (progress, 0);
                        update_source_label(ud, device);
-                       ghb_hb_cleanup(TRUE);
-                       ghb_backend_scan(device, 0);
+                       prune_logs(ud);
+                       gint preview_count;
+                       preview_count = ghb_settings_get_int(ud->settings, "preview_count");
+                       ghb_backend_scan(device, 0, preview_count);
                }
                else
                {
-                       ghb_hb_cleanup(TRUE);
-                       ghb_backend_scan("/dev/null", 0);
+                       prune_logs(ud);
+                       ghb_backend_scan("/dev/null", 0, 1);
                }
        }
        g_free(device);
 }
 
 
-static gboolean
+static void
 dbus_init (void)
 {
-       DBusError error;
+       dbus_g_thread_init();
+}
 
-       if (dbus_connection != NULL)
-               return TRUE;
+#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;
 
-       dbus_error_init (&error);
-       if (!(dbus_connection = dbus_bus_get (DBUS_BUS_SYSTEM, &error))) {
-               g_debug ("could not get system bus: %s", error.message);
-               dbus_error_free (&error);
-               return FALSE;
+void
+ghb_inhibit_gpm()
+{
+       DBusGConnection *conn;
+       DBusGProxy      *proxy;
+       GError *error = NULL;
+       gboolean res;
+       
+
+       if (gpm_inhibited)
+       {
+               // Already inhibited
+               return;
+       }
+       g_debug("ghb_inhibit_gpm()");
+       conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+       if (error != NULL)
+       {
+               g_debug("DBUS cannot connect: %s", error->message);
+               g_error_free(error);
+               return;
        }
+       proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SERVICE,
+                                                       GPM_DBUS_INHIBIT_PATH, GPM_DBUS_INHIBIT_INTERFACE);
+       if (proxy == NULL)
+       {
+               g_debug("Could not get DBUS proxy: %s", GPM_DBUS_SERVICE);
+               dbus_g_connection_unref(conn);
+               return;
+       }
+       res = dbus_g_proxy_call(proxy, "Inhibit", &error,
+                                                       G_TYPE_STRING, "ghb",
+                                                       G_TYPE_STRING, "Encoding",
+                                                       G_TYPE_INVALID,
+                                                       G_TYPE_UINT, &gpm_cookie,
+                                                       G_TYPE_INVALID);
+       if (!res)
+       {
+               g_warning("Inhibit method failed");
+               gpm_cookie = -1;
+       }
+       if (error != NULL)
+       {
+               g_warning("Inhibit problem: %s", error->message);
+               g_error_free(error);
+               gpm_cookie = -1;
+       }
+       gpm_inhibited = TRUE;
+       g_object_unref(G_OBJECT(proxy));
+       dbus_g_connection_unref(conn);
+}
 
-       //dbus_connection_setup_with_g_main (dbus_connection, NULL);
-       //dbus_connection_set_exit_on_disconnect (dbus_connection, FALSE);
-       //dbus_connection_add_filter (dbus_connection, gvm_dbus_filter_function, NULL, NULL);
+void
+ghb_uninhibit_gpm()
+{
+       DBusGConnection *conn;
+       DBusGProxy      *proxy;
+       GError *error = NULL;
+       gboolean res;
+       
+       g_debug("ghb_uninhibit_gpm() gpm_cookie %u", gpm_cookie);
 
-       return TRUE;
+       if (!gpm_inhibited)
+       {
+               // Not inhibited
+               return;
+       }
+       conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+       if (error != NULL)
+       {
+               g_debug("DBUS cannot connect: %s", error->message);
+               g_error_free(error);
+               return;
+       }
+       proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SERVICE,
+                                                       GPM_DBUS_INHIBIT_PATH, GPM_DBUS_INHIBIT_INTERFACE);
+       if (proxy == NULL)
+       {
+               g_debug("Could not get DBUS proxy: %s", GPM_DBUS_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);
+       if (!res)
+       {
+               g_warning("UnInhibit method failed");
+       }
+       if (error != NULL)
+       {
+               g_warning("UnInhibit problem: %s", error->message);
+               g_error_free(error);
+       }
+       gpm_inhibited = FALSE;
+       dbus_g_connection_unref(conn);
+       g_object_unref(G_OBJECT(proxy));
 }
 
 void
 ghb_hal_init()
 {
+       DBusGConnection *gconn;
+       DBusConnection *conn;
+       GError *gerror = NULL;
        DBusError error;
        char **devices;
        int nr;
 
-       if (!dbus_init ())
-               return;
+       dbus_init ();
 
        if (!(hal_ctx = libhal_ctx_new ())) {
                g_warning ("failed to create a HAL context!");
                return;
        }
 
-       libhal_ctx_set_dbus_connection (hal_ctx, dbus_connection);
+       gconn = dbus_g_bus_get(DBUS_BUS_SYSTEM, &gerror);
+       if (gerror != NULL)
+       {
+               g_warning("DBUS cannot connect: %s", gerror->message);
+               g_error_free(gerror);
+               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);
                return;
        }
 
@@ -2403,12 +2806,12 @@ ghb_hal_init()
 
                libhal_ctx_shutdown (hal_ctx, NULL);
                libhal_ctx_free (hal_ctx);
+               dbus_g_connection_unref(gconn);
                return;
        }
 
        libhal_free_string_array (devices);
-
-       //gvm_hal_claim_branch ("/org/freedesktop/Hal/devices/local");
+       dbus_g_connection_unref(gconn);
 }
 
 gboolean 
@@ -2511,3 +2914,290 @@ format_deblock_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud)
                return g_strdup_printf("%d", (gint)val);
        }
 }
+
+gchar*
+format_drc_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud)
+{
+       if (val <= 0.0)
+       {
+               return g_strdup_printf("Off");
+       }
+       else
+       {
+               return g_strdup_printf("%.1f", val);
+       }
+}
+
+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);
+                       else
+                               return g_strdup_printf("QP: %.4g (%.0f%%)", val, percent);
+               } 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);
+               } break;
+
+               case HB_VCODEC_THEORA:
+               {
+                       percent = 100. * val / 63.;
+                       return g_strdup_printf("QP: %d (%.0f%%)", (int)val, percent);
+               } break;
+
+               default:
+               {
+                       percent = 0;
+               } break;
+       }
+       return g_strdup_printf("QP: %.1f / %.1f%%", val, percent);
+}
+
+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;
+       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);
+       if (build)
+               ibuild = g_strtod(build, NULL);
+       skip = ghb_settings_get_int(ud->settings, "update_skip_version");
+       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
+       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
+               ghb_settings_set_int(ud->settings, "update_skip_version", ibuild);
+               ghb_pref_save(ud->settings, "update_skip_version");
+       }
+       g_free(msg);
+
+done:
+       if (description) g_free(description);
+       if (build) g_free(build);
+       if (version) g_free(version);
+       g_free(ud->appcast);
+       ud->appcast_len = 0;
+       ud->appcast = NULL;
+}
+
+void
+ghb_net_close(GIOChannel *ioc)
+{
+       gint fd;
+
+       g_debug("ghb_net_close");
+       if (ioc == NULL) return;
+       fd = g_io_channel_unix_get_fd(ioc);
+       close(fd);
+       g_io_channel_unref(ioc);
+}
+
+gboolean
+ghb_net_recv_cb(GIOChannel *ioc, GIOCondition cond, gpointer data)
+{
+       gchar buf[2048];
+       gsize len;
+       GError *gerror = NULL;
+       GIOStatus status;
+       
+       g_debug("ghb_net_recv_cb");
+       signal_user_data_t *ud = (signal_user_data_t*)data;
+
+       status = g_io_channel_read_chars (ioc, buf, 2048, &len, &gerror);
+       if ((status == G_IO_STATUS_NORMAL || status == G_IO_STATUS_EOF) &&
+               len > 0)
+       {
+               gint new_len = ud->appcast_len + len;
+               ud->appcast = g_realloc(ud->appcast, new_len + 1);
+               memcpy(&(ud->appcast[ud->appcast_len]), buf, len);
+               ud->appcast_len = new_len;
+       }
+       if (status == G_IO_STATUS_EOF)
+       {
+               ud->appcast[ud->appcast_len] = 0;
+               ghb_net_close(ioc);
+               process_appcast(ud);
+               return FALSE;
+       }
+       return TRUE;
+}
+
+GIOChannel*
+ghb_net_open(signal_user_data_t *ud, gchar *address, gint port)
+{
+       GIOChannel *ioc;
+       gint fd;
+
+       struct sockaddr_in   sock;
+       struct hostent     * host;
+
+       g_debug("ghb_net_open");
+       if( !( host = gethostbyname( address ) ) )
+       {
+               g_warning( "gethostbyname failed (%s)", address );
+               return NULL;
+       }
+
+       memset( &sock, 0, sizeof( struct sockaddr_in ) );
+       sock.sin_family = host->h_addrtype;
+       sock.sin_port   = htons( port );
+       memcpy( &sock.sin_addr, host->h_addr, host->h_length );
+
+       fd = socket(host->h_addrtype, SOCK_STREAM, 0);
+       if( fd < 0 )
+       {
+               g_debug( "socket failed" );
+               return NULL;
+       }
+
+       if(connect(fd, (struct sockaddr*)&sock, sizeof(struct sockaddr_in )) < 0 )
+       {
+               g_debug( "connect failed" );
+               return NULL;
+       }
+       ioc = g_io_channel_unix_new(fd);
+       g_io_channel_set_encoding (ioc, NULL, NULL);
+       g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
+       g_io_add_watch (ioc, G_IO_IN, ghb_net_recv_cb, (gpointer)ud );
+
+       return ioc;
+}
+
+gpointer
+ghb_check_update(signal_user_data_t *ud)
+{
+       gchar *query;
+       gsize len;
+       GIOChannel *ioc;
+       GError *gerror = NULL;
+
+       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
+       {
+               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_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;
+}
+
+static gpointer
+check_stable_update(signal_user_data_t *ud)
+{
+       gchar *query;
+       gsize len;
+       GIOChannel *ioc;
+       GError *gerror = NULL;
+
+       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_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;
+}
+
+void
+status_activate_cb(GtkStatusIcon *si, signal_user_data_t *ud)
+{
+       GtkWindow *window;
+
+       window = GTK_WINDOW(GHB_WIDGET(ud->builder, "hb_window"));
+       gtk_window_present(window);
+}
+
+static void
+notify_closed_cb(NotifyNotification *notification, signal_user_data_t *ud)
+{
+       g_object_unref(G_OBJECT(notification));
+}
+
+void
+ghb_notify_done(signal_user_data_t *ud)
+{
+       NotifyNotification *notification;
+       GtkStatusIcon *si;
+
+       si = GTK_STATUS_ICON(GHB_OBJECT(ud->builder, "hb_status"));
+       gtk_status_icon_set_from_icon_name(si, "hb-status-empty");
+       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);
+}