OSDN Git Service

LinGui: free a mark thats used in activity log scrolling
[handbrake-jp/handbrake-jp-git.git] / gtk / src / callbacks.c
index df8d829..01b8365 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 "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);
 
 // This is a dependency map used for greying widgets
 // that are dependent on the state of another widget.
@@ -210,40 +216,6 @@ ghb_check_all_depencencies(signal_user_data_t *ud)
        }
 }
 
-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];
-               }
-               else
-               {
-                       home = g_path_get_dirname(user_home);
-                       suffix = &path[1];
-               }
-               expanded_path = g_strdup_printf("%s/%s", home, suffix);
-               g_free(home);
-       }
-       return expanded_path;
-}
-
 void
 on_quit1_activate(GtkMenuItem *quit, signal_user_data_t *ud)
 {
@@ -254,12 +226,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();
 }
 
@@ -270,11 +244,10 @@ set_destination(signal_user_data_t *ud)
        if (ghb_settings_get_boolean(ud->settings, "use_source_name"))
        {
                gchar *vol_name, *filename, *extension;
-               gchar *dir, *new_name;
+               gchar *new_name;
                
-               filename = ghb_settings_get_string(ud->settings, "destination");
+               filename = ghb_settings_get_string(ud->settings, "dest_file");
                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"))
                {
@@ -284,24 +257,23 @@ set_destination(signal_user_data_t *ud)
                        end = ghb_settings_get_int(ud->settings, "end_chapter");
                        if (start == end)
                        {
-                               new_name = g_strdup_printf("%s/%s-%d.%s", 
-                                       dir, vol_name, start, extension);
+                               new_name = g_strdup_printf("%s-%d.%s", 
+                                       vol_name, start, extension);
                        }
                        else
                        {
-                               new_name = g_strdup_printf("%s/%s-%d-%d.%s", 
-                                       dir, vol_name, start, end, extension);
+                               new_name = g_strdup_printf("%s-%d-%d.%s", 
+                                       vol_name, start, end, extension);
                        }
                }
                else
                {
-                       new_name = g_strdup_printf("%s/%s.%s", dir, vol_name, extension);
+                       new_name = g_strdup_printf("%s.%s", vol_name, extension);
                }
-               ghb_ui_update(ud, "destination", ghb_string_value(new_name));
+               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);
        }
 }
@@ -603,7 +575,10 @@ 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);
-                       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, 0, preview_count);
                        g_free(path);
                }
                else
@@ -696,7 +671,7 @@ update_destination_extension(signal_user_data_t *ud)
 
        g_debug("update_destination_extension ()");
        extension = ghb_settings_get_string(ud->settings, "FileFormat");
-       entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "destination"));
+       entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "dest_file"));
        filename = g_strdup(gtk_entry_get_text(entry));
        for (ii = 0; containers[ii] != NULL; ii++)
        {
@@ -718,7 +693,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;
                }
@@ -749,6 +724,7 @@ destination_select_title(GtkEntry *entry)
                        break;
                }
        }
+       if (start < 0) start = 0;
        if (start < end)
        {
                gtk_editable_select_region(GTK_EDITABLE(entry), start, end);
@@ -767,20 +743,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;
 }
 
@@ -807,18 +802,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);
@@ -829,6 +824,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 +839,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;
 }
@@ -869,6 +867,7 @@ 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))
@@ -995,57 +994,11 @@ show_title_info(signal_user_data_t *ud, ghb_title_info_t *tinfo)
        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)
 {
@@ -1075,12 +1028,17 @@ title_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
        }
 
        // 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_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, preview_count);
+       ghb_ui_update(ud, "preview_frame", ghb_int64_value(2));
 
-       set_preview_image (ud);
+       ghb_set_preview_image (ud);
 }
 
 void
@@ -1089,6 +1047,7 @@ 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
@@ -1150,6 +1109,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);
 }
@@ -1164,6 +1124,7 @@ vcodec_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_vquality_range(ud, &vqmin, &vqmax, &step, &page, &digits);
        GtkWidget *qp = GHB_WIDGET(ud->builder, "VideoQualitySlider");
        gtk_range_set_range (GTK_RANGE(qp), vqmin, vqmax);
@@ -1184,6 +1145,7 @@ target_size_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);
        if (ghb_settings_get_boolean(ud->settings, "vquality_type_target"))
        {
                gint titleindex;
@@ -1246,6 +1208,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
@@ -1263,6 +1226,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
@@ -1299,6 +1263,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
@@ -1308,6 +1273,7 @@ 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;
        
@@ -1362,7 +1328,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);
@@ -1401,9 +1367,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);
@@ -1412,10 +1389,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_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");
@@ -1444,7 +1499,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;
                        }
                }
@@ -1465,7 +1520,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;
                                }
                        }
@@ -1480,7 +1535,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;
                }
        }
@@ -1512,7 +1567,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;
@@ -1580,26 +1635,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);
@@ -1620,7 +1675,7 @@ 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)
                {
@@ -1636,43 +1691,43 @@ ghb_backend_events(signal_user_data_t *ud)
                        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);
                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!" );
@@ -1726,12 +1781,14 @@ 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)
+       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
@@ -1741,7 +1798,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
@@ -1750,8 +1807,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;
@@ -1769,11 +1826,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
@@ -1781,6 +1863,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)
        {
@@ -1800,7 +1883,7 @@ ghb_timer_cb(gpointer data)
        }
        if (update_preview)
        {
-               set_preview_image (ud);
+               ghb_set_preview_image (ud);
                update_preview = FALSE;
        }
        return TRUE;
@@ -1832,7 +1915,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)
@@ -1855,11 +1938,16 @@ 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_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)
@@ -1880,6 +1968,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");
@@ -1887,12 +1999,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;
@@ -1914,20 +2027,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
@@ -1938,14 +2049,15 @@ show_queue_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
 }
 
 void
-show_presets_toggled_cb(GtkToggleButton *button, signal_user_data_t *ud)
+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);
        }
@@ -1955,7 +2067,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");
 }
 
@@ -2126,42 +2237,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;
@@ -2383,9 +2458,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.
@@ -2394,7 +2470,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.
@@ -2408,12 +2483,16 @@ 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);
-                       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);
@@ -2607,3 +2686,205 @@ format_vquality_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud)
                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 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_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_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);
+       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_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 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;
+}
+