OSDN Git Service

LinGui: first cut at anamorphic picture settings enhancements
[handbrake-jp/handbrake-jp-git.git] / gtk / src / callbacks.c
index 6d745ed..61b6bf8 100644 (file)
@@ -29,6 +29,7 @@
 #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"
@@ -48,6 +49,7 @@
 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.
@@ -67,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;
@@ -79,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);
@@ -88,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");
@@ -98,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);
@@ -139,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);
                }
@@ -164,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);
@@ -179,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));
+                       }
+               }
        }
 }
 
@@ -197,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"
@@ -204,17 +231,32 @@ 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);
+                       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));
+                       }
+               }
        }
 }
 
@@ -430,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))
        {
@@ -462,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);
        }
@@ -499,61 +552,44 @@ 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);
 }
 
 extern GValue *ghb_queue_edit_settings;
 static gchar *last_scan_file = NULL;
 
 void
-ghb_do_scan(signal_user_data_t *ud, const gchar *filename, gboolean force)
+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)
@@ -588,11 +624,10 @@ ghb_do_scan(signal_user_data_t *ud, const gchar *filename, gboolean force)
                        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);
                        prune_logs(ud);
                        gint preview_count;
                        preview_count = ghb_settings_get_int(ud->settings, "preview_count");
-                       ghb_backend_scan(path, 0, preview_count);
+                       ghb_backend_scan(path, titlenum, preview_count);
                        g_free(path);
                }
                else
@@ -602,11 +637,24 @@ ghb_do_scan(signal_user_data_t *ud, const gchar *filename, gboolean force)
        }
 }
 
-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;
@@ -624,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)
@@ -642,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)
                {
-                       ghb_do_scan(ud, filename, TRUE);
+                       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);
                        }
@@ -653,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
@@ -664,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);
-       ghb_do_scan(ud, filename, TRUE);
+       ghb_do_scan(ud, filename, 0, TRUE);
        if (strcmp(sourcename, filename) != 0)
        {
                ghb_settings_set_string (ud->settings, "default_source", filename);
@@ -960,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;
+       gboolean keep_aspect;
+       gint pic_par;
        keep_aspect = ghb_settings_get_boolean(ud->settings, "PictureKeepRatio");
-       anamorphic = ghb_settings_get_boolean(ud->settings, "anamorphic");
-       if (!(keep_aspect || anamorphic))
+       pic_par = ghb_settings_combo_int(ud->settings, "PicturePAR");
+       if (!(keep_aspect || pic_par) || pic_par == 3)
        {
                ghb_ui_update(ud, "scale_height", 
                        ghb_int64_value(tinfo->height - tinfo->crop[0] - tinfo->crop[1]));
@@ -1049,7 +1121,7 @@ title_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        gint preview_count;
        preview_count = ghb_settings_get_int(ud->settings, "preview_count");
        widget = GHB_WIDGET(ud->builder, "preview_frame");
-       gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, preview_count);
+       gtk_range_set_range (GTK_RANGE(widget), 1, preview_count);
        ghb_ui_update(ud, "preview_frame", ghb_int64_value(2));
 
        ghb_set_preview_image (ud);
@@ -1068,57 +1140,28 @@ setting_widget_changed_cb(GtkWidget *widget, signal_user_data_t *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_PictureDeinterlace");
-       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_PictureDenoise");
-       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
@@ -1136,30 +1179,26 @@ void
 vcodec_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
 {
        gdouble vqmin, vqmax, step, page;
+       gboolean inverted;
        gint digits;
-       gint vcodec;
 
        ghb_widget_to_setting(ud->settings, widget);
        ghb_check_dependency(ud, widget);
        ghb_clear_presets_selection(ud);
        ghb_live_reset(ud);
-       ghb_vquality_range(ud, &vqmin, &vqmax, &step, &page, &digits);
+       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);
-       vcodec = ghb_settings_combo_int(ud->settings, "VideoEncoder");
-       if (vcodec != HB_VCODEC_X264 && vcodec != HB_VCODEC_FFMPEG)
-       {
-               ghb_ui_update(ud, "directqp", ghb_boolean_value(FALSE));
-       }
+       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);
@@ -1303,7 +1342,24 @@ scale_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        text = ghb_settings_get_boolean(ud->settings, "autoscale") ? "On" : "Off";
        widget = GHB_WIDGET (ud->builder, "scale_auto");
        gtk_label_set_text (GTK_LABEL(widget), text);
-       text = ghb_settings_get_boolean(ud->settings, "anamorphic") ? "On" : "Off";
+       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);
 }
@@ -1483,7 +1539,7 @@ queue_scan(signal_user_data_t *ud, GValue *js)
                gchar *ver_str;
 
                ver_str = g_strdup_printf("Handbrake Version: %s (%d)\n", 
-                                                                       HB_VERSION, HB_BUILD);
+                                                                       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);
@@ -1524,6 +1580,7 @@ ghb_start_next_job(signal_user_data_t *ud, gboolean find_first)
                }
                // Nothing pending
                ghb_uninhibit_gpm();
+               ghb_notify_done(ud);
                return NULL;
        }
        // Find the next pending item after the current running item
@@ -1563,6 +1620,7 @@ ghb_start_next_job(signal_user_data_t *ud, gboolean find_first)
        }
        // Nothing found
        ghb_uninhibit_gpm();
+       ghb_notify_done(ud);
        return NULL;
 }
 
@@ -2057,14 +2115,6 @@ ghb_log(gchar *log, ...)
        va_end(args);
 }
 
-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);
-}
-
 static void
 browse_url(const gchar *url)
 {
@@ -2096,6 +2146,29 @@ browse_url(const gchar *url)
 }
 
 void
+about_web_hook(GtkAboutDialog *about, const gchar *link, gpointer data)
+{
+       browse_url(link);
+}
+
+void
+about_activate_cb(GtkWidget *xwidget, signal_user_data_t *ud)
+{
+       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");
@@ -2183,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);
                        }
@@ -2207,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++;
        }
 }
@@ -2260,8 +2343,8 @@ 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);
 
@@ -2301,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);
@@ -2333,6 +2416,13 @@ debug_log_handler(const gchar *domain, GLogLevelFlags flags, const gchar *msg, g
 }
 
 void
+warn_log_handler(const gchar *domain, GLogLevelFlags flags, const gchar *msg, gpointer data)
+{
+       printf("mywarning\n");
+       printf("%s: %s\n", domain, msg);
+}
+
+void
 ghb_hbfd(signal_user_data_t *ud, gboolean hbfd)
 {
        GtkWidget *widget;
@@ -2382,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, "PictureDeinterlace");
-       tweaks ? gtk_widget_hide(widget) : gtk_widget_show(widget);
-       widget = GHB_WIDGET(ud->builder, "tweak_PictureDeinterlace");
-       !tweaks ? gtk_widget_hide(widget) : gtk_widget_show(widget);
+       gdouble vqmin, vqmax, step, page;
+       gboolean inverted;
+       gint digits;
 
-       widget = GHB_WIDGET(ud->builder, "PictureDenoise");
-       tweaks ? gtk_widget_hide(widget) : gtk_widget_show(widget);
-       widget = GHB_WIDGET(ud->builder, "tweak_PictureDenoise");
-       !tweaks ? gtk_widget_hide(widget) : gtk_widget_show(widget);
-       if (tweaks)
-       {
-               const GValue *value;
-               value = ghb_settings_get_value(ud->settings, "PictureDeinterlace");
-               ghb_ui_update(ud, "tweak_PictureDeinterlace", value);
-               value = ghb_settings_get_value(ud->settings, "PictureDenoise");
-               ghb_ui_update(ud, "tweak_PictureDenoise", value);
-       }
-       else
-       {
-               const GValue *value;
-               value = ghb_settings_get_value(ud->settings, "tweak_PictureDeinterlace");
-               ghb_ui_update(ud, "PictureDeinterlace", value);
-               value = ghb_settings_get_value(ud->settings, "tweak_PictureDenoise");
-               ghb_ui_update(ud, "PictureDenoise", 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
@@ -2550,7 +2632,6 @@ 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);
                        prune_logs(ud);
                        gint preview_count;
                        preview_count = ghb_settings_get_int(ud->settings, "preview_count");
@@ -2558,7 +2639,6 @@ drive_changed_cb(GVolumeMonitor *gvm, GDrive *gd, signal_user_data_t *ud)
                }
                else
                {
-                       ghb_hb_cleanup(TRUE);
                        prune_logs(ud);
                        ghb_backend_scan("/dev/null", 0, 1);
                }
@@ -2836,26 +2916,58 @@ format_deblock_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud)
 }
 
 gchar*
-format_vquality_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud)
+format_drc_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud)
 {
-       if (ghb_settings_get_boolean(ud->settings, "directqp"))
+       if (val <= 0.0)
        {
-               gint vcodec = ghb_settings_combo_int(ud->settings, "VideoEncoder");
-               // Only x264 and ffmpeg currently support direct qp/crf entry
-               if (vcodec != HB_VCODEC_X264 && vcodec != HB_VCODEC_FFMPEG)
-               {
-                       val *= 100;
-                       return g_strdup_printf("%.1f", val);
-               }
-               return g_strdup_printf("%d", (gint)val);
+               return g_strdup_printf("Off");
        }
        else
        {
-               val *= 100;
                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)
 {
@@ -2875,7 +2987,7 @@ process_appcast(signal_user_data_t *ud)
        if (ud->appcast == NULL || ud->appcast_len < 15 || 
                strncmp(&(ud->appcast[9]), "200 OK", 6))
        {
-               if (!stable_update_lock && HB_BUILD % 100)
+               if (!stable_update_lock && hb_get_build(NULL) % 100)
                        g_idle_add((GSourceFunc)check_stable_update, ud);
                goto done;
        }
@@ -2884,14 +2996,14 @@ process_appcast(signal_user_data_t *ud)
                ibuild = g_strtod(build, NULL);
        skip = ghb_settings_get_int(ud->settings, "update_skip_version");
        if (description == NULL || build == NULL || version == NULL 
-               || ibuild <= HB_BUILD || skip == ibuild)
+               || ibuild <= hb_get_build(NULL) || skip == ibuild)
        {
-               if (!stable_update_lock && HB_BUILD % 100)
+               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_VERSION, HB_BUILD);
+                       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);
@@ -3014,7 +3126,7 @@ ghb_check_update(signal_user_data_t *ud)
        GError *gerror = NULL;
 
        g_debug("ghb_check_update");
-       if (HB_BUILD % 100)
+       if (hb_get_build(NULL) % 100)
        {
        query = 
                "GET /appcast_unstable.xml HTTP/1.0\r\nHost: handbrake.fr\r\n\r\n";
@@ -3057,3 +3169,35 @@ check_stable_update(signal_user_data_t *ud)
        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);
+}