OSDN Git Service

LinGui: add update checking. shows a dialog similar to the macui when
[handbrake-jp/handbrake-jp-git.git] / gtk / src / callbacks.c
index 516f33a..f7e5518 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 <gio/gio.h>
 #include "presets.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);
 
 // This is a dependency map used for greying widgets
 // that are dependent on the state of another widget.
@@ -254,12 +259,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();
 }
 
@@ -273,7 +280,7 @@ set_destination(signal_user_data_t *ud)
                gchar *dir, *new_name;
                
                filename = ghb_settings_get_string(ud->settings, "destination");
-               extension = ghb_settings_get_string(ud->settings, "container");
+               extension = ghb_settings_get_string(ud->settings, "FileFormat");
                dir = g_path_get_dirname (filename);
                vol_name = ghb_settings_get_string(ud->settings, "volume_label");
                if (ghb_settings_get_boolean(ud->settings, "chapters_in_destination"))
@@ -603,6 +610,7 @@ ghb_do_scan(signal_user_data_t *ud, const gchar *filename, gboolean force)
                        gtk_progress_bar_set_fraction (progress, 0);
                        gtk_progress_bar_set_text (progress, "Scanning ...");
                        ghb_hb_cleanup(TRUE);
+                       prune_logs(ud);
                        ghb_backend_scan (path, 0);
                        g_free(path);
                }
@@ -695,7 +703,7 @@ update_destination_extension(signal_user_data_t *ud)
        GtkEntry *entry;
 
        g_debug("update_destination_extension ()");
-       extension = ghb_settings_get_string(ud->settings, "container");
+       extension = ghb_settings_get_string(ud->settings, "FileFormat");
        entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "destination"));
        filename = g_strdup(gtk_entry_get_text(entry));
        for (ii = 0; containers[ii] != NULL; ii++)
@@ -829,6 +837,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;
 }
@@ -843,12 +852,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;
 }
@@ -875,10 +886,10 @@ container_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        {
                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);
        }
@@ -948,7 +959,7 @@ show_title_info(signal_user_data_t *ud, ghb_title_info_t *tinfo)
                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");
+       keep_aspect = ghb_settings_get_boolean(ud->settings, "PictureKeepRatio");
        anamorphic = ghb_settings_get_boolean(ud->settings, "anamorphic");
        if (!(keep_aspect || anamorphic))
        {
@@ -960,35 +971,42 @@ 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]));
-       }
-       else
-       {
-               ghb_ui_update(ud, "crop_top", ghb_int64_value(0));
-               ghb_ui_update(ud, "crop_bottom", ghb_int64_value(0));
-               ghb_ui_update(ud, "crop_left", ghb_int64_value(0));
-               ghb_ui_update(ud, "crop_right", ghb_int64_value(0));
+               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;
@@ -1045,18 +1063,16 @@ title_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
 {
        ghb_title_info_t tinfo;
        gint titleindex;
-       GValue *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_value (ud->settings, "preset");
-       ghb_update_from_preset(ud, preset, "subtitle_lang");
+       ghb_update_from_preset(ud, "Subtitles");
        if (ghb_get_title_info (&tinfo, titleindex))
        {
                show_title_info(ud, &tinfo);
@@ -1067,7 +1083,7 @@ 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
@@ -1127,7 +1143,7 @@ deint_tweak_focus_out_cb(GtkWidget *widget, GdkEventFocus *event,
        signal_user_data_t *ud)
 {
        g_debug("deint_tweak_focus_out_cb ()");
-       validate_filter_widget(ud, "tweak_deinterlace");
+       validate_filter_widget(ud, "tweak_PictureDeinterlace");
        return FALSE;
 }
 
@@ -1136,7 +1152,7 @@ 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");
+       validate_filter_widget(ud, "tweak_PictureDenoise");
        return FALSE;
 }
 
@@ -1153,14 +1169,23 @@ 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;
+       gint digits;
+       gint vcodec;
 
        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_vquality_range(ud, &vqmin, &vqmax, &step, &page, &digits);
+       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));
+       }
 }
 
 void
@@ -1176,7 +1201,7 @@ target_size_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
                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));
        }
 }
 
@@ -1263,10 +1288,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))
        {
@@ -1300,7 +1325,7 @@ scale_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        
        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";
@@ -1349,7 +1374,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);
@@ -1388,9 +1413,20 @@ static void
 submit_job(GValue *settings)
 {
        static gint unique_id = 1;
+       gchar *type, *modified, *preset;
+       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);
@@ -1399,10 +1435,87 @@ 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);
+}
+
+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_VERSION, HB_BUILD);
+               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");
@@ -1431,7 +1544,7 @@ ghb_start_next_job(signal_user_data_t *ud, gboolean find_first)
                        if (status == GHB_QUEUE_PENDING)
                        {
                                current = ii;
-                               queue_scan(js);
+                               queue_scan(ud, js);
                                return js;
                        }
                }
@@ -1452,7 +1565,7 @@ ghb_start_next_job(signal_user_data_t *ud, gboolean find_first)
                                if (status == GHB_QUEUE_PENDING)
                                {
                                        current = jj;
-                                       queue_scan(js);
+                                       queue_scan(ud, js);
                                        return js;
                                }
                        }
@@ -1467,7 +1580,7 @@ ghb_start_next_job(signal_user_data_t *ud, gboolean find_first)
                if (status == GHB_QUEUE_PENDING)
                {
                        current = ii;
-                       queue_scan(js);
+                       queue_scan(ud, js);
                        return js;
                }
        }
@@ -1713,6 +1826,8 @@ ghb_backend_events(signal_user_data_t *ud)
                        ghb_settings_set_int(js, "job_status", qstatus);
                ghb_save_queue(ud->queue);
                ud->cancel_encode = FALSE;
+               g_io_channel_unref(ud->job_activity_log);
+               ud->job_activity_log = NULL;
        }
        else if (status.queue_state & GHB_STATE_MUXING)
        {
@@ -1846,7 +1961,11 @@ ghb_log_cb(GIOChannel *source, GIOCondition cond, gpointer data)
                        mark = gtk_text_buffer_get_insert (buffer);
                        gtk_text_view_scroll_mark_onscreen(textview, mark);
                }
-               g_io_channel_write_chars (ud->activity_log, text, length, &length, NULL);
+               g_io_channel_write_chars (ud->activity_log, text, 
+                                                               length, &length, NULL);
+               if (ud->job_activity_log)
+                       g_io_channel_write_chars (ud->job_activity_log, text, 
+                                                                       length, &length, NULL);
                g_free(text);
        }
        if (status != G_IO_STATUS_NORMAL)
@@ -1867,6 +1986,30 @@ ghb_log_cb(GIOChannel *source, GIOCondition cond, gpointer data)
 }
 
 void
+show_activity_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
+{
+       GtkWidget *widget = GHB_WIDGET (ud->builder, "activity_window");
+       gtk_widget_show (widget);
+}
+
+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);
+}
+
+void
 about_activate_cb(GtkWidget *xwidget, signal_user_data_t *ud)
 {
        GtkWidget *widget = GHB_WIDGET (ud->builder, "hb_about");
@@ -1874,12 +2017,13 @@ about_activate_cb(GtkWidget *xwidget, signal_user_data_t *ud)
        gtk_widget_show (widget);
 }
 
-void
-guide_activate_cb(GtkWidget *xwidget, signal_user_data_t *ud)
+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;
@@ -1901,20 +2045,18 @@ 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)
+guide_activate_cb(GtkWidget *xwidget, signal_user_data_t *ud)
 {
-       gtk_widget_hide (widget);
+       browse_url("http://trac.handbrake.fr/wiki/HandBrakeGuide");
 }
 
 void
-show_activity_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
+hb_about_response_cb(GtkWidget *widget, gint response, signal_user_data_t *ud)
 {
-       GtkWidget *widget = GHB_WIDGET (ud->builder, "activity_window");
-       gtk_widget_show (widget);
+       gtk_widget_hide (widget);
 }
 
 void
@@ -2232,30 +2374,30 @@ tweaks_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        ghb_pref_save(ud->settings, name);
 
        gboolean tweaks = ghb_settings_get_boolean(ud->settings, "allow_tweaks");
-       widget = GHB_WIDGET(ud->builder, "deinterlace");
+       widget = GHB_WIDGET(ud->builder, "PictureDeinterlace");
        tweaks ? gtk_widget_hide(widget) : gtk_widget_show(widget);
-       widget = GHB_WIDGET(ud->builder, "tweak_deinterlace");
+       widget = GHB_WIDGET(ud->builder, "tweak_PictureDeinterlace");
        !tweaks ? gtk_widget_hide(widget) : gtk_widget_show(widget);
 
-       widget = GHB_WIDGET(ud->builder, "denoise");
+       widget = GHB_WIDGET(ud->builder, "PictureDenoise");
        tweaks ? gtk_widget_hide(widget) : gtk_widget_show(widget);
-       widget = GHB_WIDGET(ud->builder, "tweak_denoise");
+       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, "deinterlace");
-               ghb_ui_update(ud, "tweak_deinterlace", value);
-               value = ghb_settings_get_value(ud->settings, "denoise");
-               ghb_ui_update(ud, "tweak_denoise", 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_deinterlace");
-               ghb_ui_update(ud, "deinterlace", value);
-               value = ghb_settings_get_value(ud->settings, "tweak_denoise");
-               ghb_ui_update(ud, "denoise", 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);
        }
 }
 
@@ -2395,11 +2537,13 @@ drive_changed_cb(GVolumeMonitor *gvm, GDrive *gd, signal_user_data_t *ud)
                        gtk_progress_bar_set_fraction (progress, 0);
                        update_source_label(ud, device);
                        ghb_hb_cleanup(TRUE);
+                       prune_logs(ud);
                        ghb_backend_scan(device, 0);
                }
                else
                {
                        ghb_hb_cleanup(TRUE);
+                       prune_logs(ud);
                        ghb_backend_scan("/dev/null", 0);
                }
        }
@@ -2573,3 +2717,234 @@ format_deblock_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud)
                return g_strdup_printf("%d", (gint)val);
        }
 }
+
+gchar*
+format_vquality_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud)
+{
+       if (ghb_settings_get_boolean(ud->settings, "directqp"))
+       {
+               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);
+       }
+       else
+       {
+               val *= 100;
+               return g_strdup_printf("%.1f", val);
+       }
+}
+
+static void
+html_link_cb(GtkHTML *html, const gchar *url, signal_user_data_t *ud)
+{
+       browse_url(url);
+}
+
+static gboolean 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_BUILD % 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_BUILD || skip == ibuild)
+       {
+               if (!stable_update_lock && HB_BUILD % 100)
+                       g_idle_add((GSourceFunc)check_stable_update, ud);
+               goto done;
+       }
+       msg = g_strdup_printf("HandBrake %s/%s is now available (you have %s/%d).",
+                       version, build, HB_VERSION, HB_BUILD);
+       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");
+       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;
+               process_appcast(ud);
+               ghb_net_close(ioc);
+               return FALSE;
+       }
+       return TRUE;
+}
+
+static gboolean
+appcast_timeout_cb(GIOChannel *ioc)
+{
+       g_debug("appcast_timeout_cb");
+       ghb_net_close(ioc);
+       return FALSE;
+}
+
+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 );
+       g_timeout_add_seconds(20, (GSourceFunc)appcast_timeout_cb, ioc);
+
+       return ioc;
+}
+
+gboolean
+ghb_check_update(signal_user_data_t *ud)
+{
+       gchar *query;
+       gsize len;
+       GIOChannel *ioc;
+       GError *gerror = NULL;
+
+       g_debug("ghb_check_update");
+       if (HB_BUILD % 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 FALSE;
+
+       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 FALSE;
+}
+
+static gboolean
+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 FALSE;
+
+       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 FALSE;
+}
+