OSDN Git Service

LinGui: ui support for new anamorphic parameters in libhb
[handbrake-jp/handbrake-jp-git.git] / gtk / src / callbacks.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * callbacks.c
4  * Copyright (C) John Stebbins 2008 <stebbins@stebbins>
5  * 
6  * callbacks.c is free software.
7  * 
8  * You may redistribute it and/or modify it under the terms of the
9  * GNU General Public License, as published by the Free Software
10  * Foundation; either version 2 of the License, or (at your option)
11  * any later version.
12  */
13
14 #ifdef HAVE_CONFIG_H
15 #  include <config.h>
16 #endif
17
18 #include <string.h>
19 #include <poll.h>
20 #include <fcntl.h>
21 #include <sys/stat.h>
22 #include <netinet/in.h>
23 #include <netdb.h>
24 #include <libhal-storage.h>
25 #include <gtk/gtk.h>
26 #include <gtkhtml/gtkhtml.h>
27 #include <gdk/gdkkeysyms.h>
28 #include <glib/gstdio.h>
29 #include <dbus/dbus-glib.h>
30 #include <dbus/dbus-glib-lowlevel.h>
31 #include <gio/gio.h>
32
33 #include "hb.h"
34 #include "callbacks.h"
35 #include "queuehandler.h"
36 #include "audiohandler.h"
37 #include "resources.h"
38 #include "settings.h"
39 #include "presets.h"
40 #include "preview.h"
41 #include "values.h"
42 #include "plist.h"
43 #include "appcast.h"
44 #include "hb-backend.h"
45 #include "ghb-dvd.h"
46 #include "ghbcellrenderertext.h"
47
48 static void update_chapter_list(signal_user_data_t *ud);
49 static GList* dvd_device_list();
50 static void prune_logs(signal_user_data_t *ud);
51
52 // This is a dependency map used for greying widgets
53 // that are dependent on the state of another widget.
54 // The enable_value comes from the values that are
55 // obtained from ghb_widget_value().  For combo boxes
56 // you will have to look further to combo box options
57 // maps in hb-backend.c
58
59 GValue *dep_map;
60 GValue *rev_map;
61
62 void
63 ghb_init_dep_map()
64 {
65         dep_map = ghb_resource_get("widget-deps");
66         rev_map = ghb_resource_get("widget-reverse-deps");
67 }
68
69 static gboolean
70 dep_check(signal_user_data_t *ud, const gchar *name)
71 {
72         GtkWidget *widget;
73         GObject *dep_object;
74         gint ii;
75         gint count;
76         gboolean result = TRUE;
77         GValue *array, *data;
78         gchar *widget_name;
79         
80         g_debug("dep_check () %s", name);
81
82         array = ghb_dict_lookup(rev_map, name);
83         count = ghb_array_len(array);
84         for (ii = 0; ii < count; ii++)
85         {
86                 data = ghb_array_get_nth(array, ii);
87                 widget_name = ghb_value_string(ghb_array_get_nth(data, 0));
88                 widget = GHB_WIDGET(ud->builder, widget_name);
89                 dep_object = gtk_builder_get_object(ud->builder, name);
90                 g_free(widget_name);
91                 if (!GTK_WIDGET_SENSITIVE(widget))
92                         continue;
93                 if (dep_object == NULL)
94                 {
95                         g_message("Failed to find widget");
96                 }
97                 else
98                 {
99                         gchar *value;
100                         gint jj = 0;
101                         gchar **values;
102                         gboolean sensitive = FALSE;
103                         gboolean die;
104
105                         die = ghb_value_boolean(ghb_array_get_nth(data, 2));
106                         value = ghb_value_string(ghb_array_get_nth(data, 1));
107                         values = g_strsplit(value, "|", 10);
108                         g_free(value);
109
110                         if (widget)
111                                 value = ghb_widget_string(widget);
112                         else
113                                 value = ghb_settings_get_string(ud->settings, name);
114                         while (values && values[jj])
115                         {
116                                 if (values[jj][0] == '>')
117                                 {
118                                         gdouble dbl = g_strtod (&values[jj][1], NULL);
119                                         gdouble dvalue = ghb_widget_double(widget);
120                                         if (dvalue > dbl)
121                                         {
122                                                 sensitive = TRUE;
123                                                 break;
124                                         }
125                                 }
126                                 else if (values[jj][0] == '<')
127                                 {
128                                         gdouble dbl = g_strtod (&values[jj][1], NULL);
129                                         gdouble dvalue = ghb_widget_double(widget);
130                                         if (dvalue < dbl)
131                                         {
132                                                 sensitive = TRUE;
133                                                 break;
134                                         }
135                                 }
136                                 if (strcmp(values[jj], value) == 0)
137                                 {
138                                         sensitive = TRUE;
139                                         break;
140                                 }
141                                 jj++;
142                         }
143                         sensitive = die ^ sensitive;
144                         if (!sensitive) result = FALSE;
145                         g_strfreev (values);
146                         g_free(value);
147                 }
148         }
149         return result;
150 }
151
152 void
153 ghb_check_dependency(signal_user_data_t *ud, GtkWidget *widget)
154 {
155         GObject *dep_object;
156         const gchar *name;
157         GValue *array, *data;
158         gint count, ii;
159         gchar *dep_name;
160         GType type;
161
162         type = GTK_WIDGET_TYPE(widget);
163         if (type == GTK_TYPE_COMBO_BOX || type == GTK_TYPE_COMBO_BOX_ENTRY)
164                 if (gtk_combo_box_get_active(GTK_COMBO_BOX(widget)) < 0) return;
165
166         name = gtk_widget_get_name(widget);
167         g_debug("ghb_check_dependency () %s", name);
168
169         array = ghb_dict_lookup(dep_map, name);
170         count = ghb_array_len(array);
171         for (ii = 0; ii < count; ii++)
172         {
173                 gboolean sensitive;
174
175                 data = ghb_array_get_nth(array, ii);
176                 dep_name = ghb_value_string(data);
177                 dep_object = gtk_builder_get_object(ud->builder, dep_name);
178                 if (dep_object == NULL)
179                 {
180                         g_message("Failed to find dependent widget %s", dep_name);
181                         g_free(dep_name);
182                         continue;
183                 }
184                 sensitive = dep_check(ud, dep_name);
185                 g_free(dep_name);
186                 if (GTK_IS_ACTION(dep_object))
187                         gtk_action_set_sensitive(GTK_ACTION(dep_object), sensitive);
188                 else
189                         gtk_widget_set_sensitive(GTK_WIDGET(dep_object), sensitive);
190         }
191 }
192
193 void
194 ghb_check_all_depencencies(signal_user_data_t *ud)
195 {
196         GHashTableIter iter;
197         gchar *dep_name;
198         GValue *value;
199         GObject *dep_object;
200
201         g_debug("ghb_check_all_depencencies ()");
202         ghb_dict_iter_init(&iter, rev_map);
203         // middle (void*) cast prevents gcc warning "defreferencing type-punned
204         // pointer will break strict-aliasing rules"
205         while (g_hash_table_iter_next(
206                         &iter, (gpointer*)(void*)&dep_name, (gpointer*)(void*)&value))
207         {
208                 gboolean sensitive;
209                 dep_object = gtk_builder_get_object (ud->builder, dep_name);
210                 if (dep_object == NULL)
211                 {
212                         g_message("Failed to find dependent widget %s", dep_name);
213                         continue;
214                 }
215                 sensitive = dep_check(ud, dep_name);
216                 if (GTK_IS_ACTION(dep_object))
217                         gtk_action_set_sensitive(GTK_ACTION(dep_object), sensitive);
218                 else
219                         gtk_widget_set_sensitive(GTK_WIDGET(dep_object), sensitive);
220         }
221 }
222
223 void
224 on_quit1_activate(GtkMenuItem *quit, signal_user_data_t *ud)
225 {
226         gint state = ghb_get_queue_state();
227         g_debug("on_quit1_activate ()");
228         if (state & GHB_STATE_WORKING)
229         {
230                 if (ghb_cancel_encode("Closing HandBrake will terminate encoding.\n"))
231                 {
232                         ghb_hb_cleanup(FALSE);
233                         prune_logs(ud);
234                         gtk_main_quit();
235                         return;
236                 }
237                 return;
238         }
239         ghb_hb_cleanup(FALSE);
240         prune_logs(ud);
241         gtk_main_quit();
242 }
243
244 static void
245 set_destination(signal_user_data_t *ud)
246 {
247         g_debug("set_destination");
248         if (ghb_settings_get_boolean(ud->settings, "use_source_name"))
249         {
250                 GString *str = g_string_new("");
251                 gchar *vol_name, *filename, *extension;
252                 gchar *new_name;
253                 gint title;
254                 
255                 filename = ghb_settings_get_string(ud->settings, "dest_file");
256                 extension = ghb_settings_get_string(ud->settings, "FileFormat");
257                 vol_name = ghb_settings_get_string(ud->settings, "volume_label");
258                 g_string_append_printf(str, "%s", vol_name);
259                 title = ghb_settings_combo_int(ud->settings, "title");
260                 if (title >= 0)
261                 {
262                         if (ghb_settings_get_boolean(
263                                         ud->settings, "title_no_in_destination"))
264                         {
265
266                                 title = ghb_settings_combo_int(ud->settings, "title");
267                                 g_string_append_printf(str, " - %d", title+1);
268                         }
269                         if (ghb_settings_get_boolean(
270                                         ud->settings, "chapters_in_destination"))
271                         {
272                                 gint start, end;
273
274                                 if (!ghb_settings_get_boolean(
275                                                 ud->settings, "title_no_in_destination"))
276                                 {
277                                         g_string_append_printf(str, " -");
278                                 }
279                                 start = ghb_settings_get_int(ud->settings, "start_chapter");
280                                 end = ghb_settings_get_int(ud->settings, "end_chapter");
281                                 if (start == end)
282                                         g_string_append_printf(str, " Ch %d", start);
283                                 else
284                                         g_string_append_printf(str, " Ch %d-%d", start, end);
285                         }
286                 }
287                 g_string_append_printf(str, ".%s", extension);
288                 new_name = g_string_free(str, FALSE);
289                 ghb_ui_update(ud, "dest_file", ghb_string_value(new_name));
290                 g_free(filename);
291                 g_free(extension);
292                 g_free(vol_name);
293                 g_free(new_name);
294         }
295 }
296
297 gboolean
298 uppers_and_unders(const gchar *str)
299 {
300         if (str == NULL) return FALSE;
301         while (*str)
302         {
303                 if (*str == ' ')
304                 {
305                         return FALSE;
306                 }
307                 if (*str >= 'a' && *str <= 'z')
308                 {
309                         return FALSE;
310                 }
311                 str++;
312         }
313         return TRUE;
314 }
315
316 enum
317 {
318         CAMEL_FIRST_UPPER,
319         CAMEL_OTHER
320 };
321
322 void
323 camel_convert(gchar *str)
324 {
325         gint state = CAMEL_OTHER;
326         
327         if (str == NULL) return;
328         while (*str)
329         {
330                 if (*str == '_') *str = ' ';
331                 switch (state)
332                 {
333                         case CAMEL_OTHER:
334                         {
335                                 if (*str >= 'A' && *str <= 'Z')
336                                         state = CAMEL_FIRST_UPPER;
337                                 else
338                                         state = CAMEL_OTHER;
339                                 
340                         } break;
341                         case CAMEL_FIRST_UPPER:
342                         {
343                                 if (*str >= 'A' && *str <= 'Z')
344                                         *str = *str - 'A' + 'a';
345                                 else
346                                         state = CAMEL_OTHER;
347                         } break;
348                 }
349                 str++;
350         }
351 }
352
353 static gchar*
354 get_file_label(const gchar *filename)
355 {
356         static gchar *containers[] = 
357                 {".vob", ".mpg", ".m2ts", ".mkv", ".mp4", ".m4v", ".avi", ".ogm", NULL};
358         gchar *base;
359         gint ii;
360
361         base = g_path_get_basename(filename);
362         for (ii = 0; containers[ii] != NULL; ii++)
363         {
364                 if (g_str_has_suffix(base, containers[ii]))
365                 {
366                         gchar *pos;
367                         pos = strrchr(base, '.');
368                         *pos = 0;
369                         break;
370                 }
371         }
372         return base;
373 }
374
375 static gboolean
376 update_source_label(signal_user_data_t *ud, const gchar *source)
377 {
378         gchar *label = NULL;
379         gint len;
380         gchar **path;
381         gchar *filename = g_strdup(source);
382         
383         len = strlen(filename);
384         if (filename[len-1] == '/') filename[len-1] = 0;
385         if (g_file_test(filename, G_FILE_TEST_IS_DIR))
386         {
387                 path = g_strsplit(filename, "/", -1);
388                 len = g_strv_length (path);
389                 if ((len > 1) && (strcmp("VIDEO_TS", path[len-1]) == 0))
390                 {
391                         label = g_strdup(path[len-2]);
392                 }
393                 else
394                 {
395                         label = g_strdup(path[len-1]);
396                 }
397                 g_strfreev (path);
398         }
399         else
400         {
401                 // Is regular file or block dev.
402                 // Check to see if it is a dvd image
403                 label = ghb_dvd_volname (filename);
404                 if (label == NULL)
405                 {
406                         label = get_file_label(filename);
407                 }
408                 else
409                 {
410                         if (uppers_and_unders(label))
411                         {
412                                 camel_convert(label);
413                         }
414                 }
415         }
416         g_free(filename);
417         GtkWidget *widget = GHB_WIDGET (ud->builder, "source_title");
418         if (label != NULL)
419         {
420                 gtk_label_set_text (GTK_LABEL(widget), label);
421                 ghb_settings_set_string(ud->settings, "volume_label", label);
422                 g_free(label);
423                 set_destination(ud);
424         }
425         else
426         {
427                 label = "No Title Found";
428                 gtk_label_set_text (GTK_LABEL(widget), label);
429                 ghb_settings_set_string(ud->settings, "volume_label", label);
430                 return FALSE;
431         }
432         return TRUE;
433 }
434
435 static GtkWidget *dvd_device_combo = NULL;
436
437 void
438 chooser_file_selected_cb(GtkFileChooser *dialog, GtkComboBox *combo)
439 {
440         const gchar *name = gtk_file_chooser_get_filename (dialog);
441         GtkTreeModel *store;
442         GtkTreeIter iter;
443         const gchar *device;
444         gboolean foundit = FALSE;
445         
446         if (name == NULL) return;
447         store = gtk_combo_box_get_model(combo);
448         if (gtk_tree_model_get_iter_first(store, &iter))
449         {
450                 do
451                 {
452                         gtk_tree_model_get(store, &iter, 0, &device, -1);
453                         if (strcmp(name, device) == 0)
454                         {
455                                 foundit = TRUE;
456                                 break;
457                         }
458                 } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter));
459         }
460         if (foundit)
461                 gtk_combo_box_set_active_iter (combo, &iter);
462         else
463                 gtk_combo_box_set_active (combo, 0);
464 }
465
466 void
467 dvd_device_changed_cb(GtkComboBox *combo, GtkWidget *dialog)
468 {
469         gint ii = gtk_combo_box_get_active (combo);
470         if (ii != 0)
471         {
472                 const gchar *device = gtk_combo_box_get_active_text (combo);
473                 const gchar *name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog));
474                 if (name == NULL || strcmp(name, device) != 0)
475                         gtk_file_chooser_select_filename (GTK_FILE_CHOOSER(dialog), device);
476         }
477 }
478
479 void
480 source_type_changed_cb(GtkToggleButton *toggle, GtkFileChooser *chooser)
481 {
482         gchar *folder;
483         
484         g_debug("source_type_changed_cb ()");
485         folder = gtk_file_chooser_get_current_folder (chooser);
486         if (gtk_toggle_button_get_active (toggle))
487         {
488                 gtk_file_chooser_set_action (chooser, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
489                 gtk_widget_set_sensitive (dvd_device_combo, FALSE);
490                 gtk_combo_box_set_active (GTK_COMBO_BOX(dvd_device_combo), 0);
491         }
492         else
493         {
494                 gtk_file_chooser_set_action (chooser, GTK_FILE_CHOOSER_ACTION_OPEN);
495                 gtk_widget_set_sensitive (dvd_device_combo, TRUE);
496         }
497         if (folder != NULL)
498         {
499                 gtk_file_chooser_set_current_folder(chooser, folder);
500                 g_free(folder);
501         }
502 }
503
504 static GtkWidget*
505 source_dialog_extra_widgets(GtkWidget *dialog, gboolean checkbutton_active)
506 {
507         GtkBox *vbox;
508         GtkWidget *checkbutton;
509         
510         vbox = GTK_BOX(gtk_vbox_new (FALSE, 2));
511         checkbutton = gtk_check_button_new_with_label ("Open VIDEO_TS folder");
512         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(checkbutton), checkbutton_active);
513         gtk_box_pack_start (vbox, checkbutton, FALSE, FALSE, 1);
514         gtk_widget_show(checkbutton);
515
516         GtkWidget *combo;
517         GtkBox *hbox;
518         GList *drives, *link;
519         GtkWidget *label, *blank;
520
521         hbox = GTK_BOX(gtk_hbox_new (FALSE, 2));
522         combo = gtk_combo_box_new_text();
523         label = gtk_label_new("Detected DVD devices:");
524         blank = gtk_label_new("");
525         link = drives = dvd_device_list();
526         gtk_combo_box_append_text (GTK_COMBO_BOX(combo), "Not Selected");
527         while (link != NULL)
528         {
529                 gchar *name = (gchar*)link->data;
530                 gtk_combo_box_append_text (GTK_COMBO_BOX(combo), name);
531                 g_free(name);
532                 link = link->next;
533         }
534         g_list_free(drives);
535         gtk_combo_box_set_active (GTK_COMBO_BOX(combo), 0);
536         gtk_box_pack_start (vbox, GTK_WIDGET(hbox), FALSE, FALSE, 1);
537         gtk_widget_show(GTK_WIDGET(hbox));
538         gtk_box_pack_start (hbox, label, FALSE, FALSE, 1);
539         gtk_widget_show(label);
540         gtk_box_pack_start (hbox, combo, FALSE, FALSE, 2);
541         gtk_widget_show(combo);
542         gtk_box_pack_start (hbox, blank, TRUE, TRUE, 1);
543         gtk_widget_show(blank);
544  
545         // Ugly hackish global alert
546         dvd_device_combo = combo;
547         g_signal_connect(combo, "changed", (GCallback)dvd_device_changed_cb, dialog);
548         g_signal_connect(dialog, "selection-changed", (GCallback)chooser_file_selected_cb, combo);
549
550         g_signal_connect(checkbutton, "toggled", (GCallback)source_type_changed_cb, dialog);
551         return GTK_WIDGET(vbox);
552 }
553
554 extern GValue *ghb_queue_edit_settings;
555 static gchar *last_scan_file = NULL;
556
557 void
558 ghb_do_scan(signal_user_data_t *ud, const gchar *filename, gboolean force)
559 {
560         if (!force && last_scan_file != NULL &&
561                 strcmp(last_scan_file, filename) == 0)
562         {
563                 if (ghb_queue_edit_settings)
564                 {
565                         gint jstatus;
566
567                         jstatus = ghb_settings_get_int(ghb_queue_edit_settings, "job_status");
568                         ghb_settings_to_ui(ud, ghb_queue_edit_settings);
569                         ghb_set_audio(ud, ghb_queue_edit_settings);
570                         if (jstatus == GHB_QUEUE_PENDING)
571                         {
572                                 ghb_value_free(ghb_queue_edit_settings);
573                         }
574                         ghb_queue_edit_settings = NULL;
575                 }
576                 return;
577         }
578         if (last_scan_file != NULL)
579                 g_free(last_scan_file);
580         last_scan_file = NULL;
581         if (filename != NULL)
582         {
583                 last_scan_file = g_strdup(filename);
584                 ghb_settings_set_string(ud->settings, "source", filename);
585                 if (update_source_label(ud, filename))
586                 {
587                         GtkProgressBar *progress;
588                         progress = GTK_PROGRESS_BAR(GHB_WIDGET(ud->builder, "progressbar"));
589                         gchar *path;
590                         path = ghb_settings_get_string( ud->settings, "source");
591                         gtk_progress_bar_set_fraction (progress, 0);
592                         gtk_progress_bar_set_text (progress, "Scanning ...");
593                         ghb_hb_cleanup(TRUE);
594                         prune_logs(ud);
595                         gint preview_count;
596                         preview_count = ghb_settings_get_int(ud->settings, "preview_count");
597                         ghb_backend_scan(path, 0, preview_count);
598                         g_free(path);
599                 }
600                 else
601                 {
602                         // TODO: error dialog
603                 }
604         }
605 }
606
607 void
608 source_button_clicked_cb(GtkButton *button, signal_user_data_t *ud)
609 {
610         GtkWidget *dialog;
611         GtkWidget *widget;
612         gchar *sourcename;
613         gint    response;
614         GtkFileChooserAction action;
615         gboolean checkbutton_active;
616
617         g_debug("source_browse_clicked_cb ()");
618         sourcename = ghb_settings_get_string(ud->settings, "source");
619         checkbutton_active = FALSE;
620         if (g_file_test(sourcename, G_FILE_TEST_IS_DIR))
621         {
622                 action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
623                 checkbutton_active = TRUE;
624         }
625         else
626         {
627                 action = GTK_FILE_CHOOSER_ACTION_OPEN;
628         }
629         dialog = gtk_file_chooser_dialog_new ("Select Source",
630                                                                 NULL,
631                                                                 action,
632                                                                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
633                                                                 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
634                                                                 NULL);
635         widget = source_dialog_extra_widgets(dialog, checkbutton_active);
636         gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), widget);
637         gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), sourcename);
638         response = gtk_dialog_run(GTK_DIALOG (dialog));
639         gtk_widget_hide(dialog);
640         if (response == GTK_RESPONSE_ACCEPT)
641         {
642                 char *filename;
643
644                 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
645                 if (filename != NULL)
646                 {
647                         ghb_do_scan(ud, filename, TRUE);
648                         if (strcmp(sourcename, filename) != 0)
649                         {
650                                 ghb_settings_set_string (ud->settings, "default_source", filename);
651                                 ghb_pref_save (ud->settings, "default_source");
652                                 ghb_dvd_set_current (filename, ud);
653                         }
654                         g_free(filename);
655                 }
656         }
657         g_free(sourcename);
658         gtk_widget_destroy(dialog);
659 }
660
661 void
662 dvd_source_activate_cb(GtkAction *action, signal_user_data_t *ud)
663 {
664         const gchar *filename;
665         gchar *sourcename;
666
667         sourcename = ghb_settings_get_string(ud->settings, "source");
668         filename = gtk_action_get_name(action);
669         ghb_do_scan(ud, filename, TRUE);
670         if (strcmp(sourcename, filename) != 0)
671         {
672                 ghb_settings_set_string (ud->settings, "default_source", filename);
673                 ghb_pref_save (ud->settings, "default_source");
674                 ghb_dvd_set_current (filename, ud);
675         }
676         g_free(sourcename);
677 }
678
679 static void
680 update_destination_extension(signal_user_data_t *ud)
681 {
682         static gchar *containers[] = {".mkv", ".mp4", ".m4v", ".avi", ".ogm", NULL};
683         gchar *filename;
684         gchar *extension;
685         gint ii;
686         GtkEntry *entry;
687
688         g_debug("update_destination_extension ()");
689         extension = ghb_settings_get_string(ud->settings, "FileFormat");
690         entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "dest_file"));
691         filename = g_strdup(gtk_entry_get_text(entry));
692         for (ii = 0; containers[ii] != NULL; ii++)
693         {
694                 if (g_str_has_suffix(filename, containers[ii]))
695                 {
696                         gchar *pos;
697                         gchar *new_name;
698                         
699                         pos = g_strrstr( filename, "." );
700                         if (pos == NULL)
701                         {
702                                 // No period? shouldn't happen
703                                 break;
704                         }
705                         *pos = 0;
706                         if (strcmp(extension, &pos[1]) == 0)
707                         {
708                                 // Extension is already correct
709                                 break;
710                         }
711                         new_name = g_strjoin(".", filename, extension, NULL); 
712                         ghb_ui_update(ud, "dest_file", ghb_string_value(new_name));
713                         g_free(new_name);
714                         break;
715                 }
716         }
717         g_free(extension);
718         g_free(filename);
719 }
720
721 static void
722 destination_select_title(GtkEntry *entry)
723 {
724         const gchar *dest;
725         gint start, end;
726
727         dest = gtk_entry_get_text(entry);
728         for (end = strlen(dest)-1; end > 0; end--)
729         {
730                 if (dest[end] == '.')
731                 {
732                         break;
733                 }
734         }
735         for (start = end; start >= 0; start--)
736         {
737                 if (dest[start] == '/')
738                 {
739                         start++;
740                         break;
741                 }
742         }
743         if (start < 0) start = 0;
744         if (start < end)
745         {
746                 gtk_editable_select_region(GTK_EDITABLE(entry), start, end);
747         }
748 }
749
750 gboolean
751 destination_grab_cb(
752         GtkEntry *entry, 
753         signal_user_data_t *ud)
754 {
755         destination_select_title(entry);
756         return FALSE;
757 }
758
759 static gboolean update_default_destination = FALSE;
760
761 void
762 dest_dir_set_cb(GtkFileChooserButton *dest_chooser, signal_user_data_t *ud)
763 {
764         gchar *dest_file, *dest_dir, *dest;
765         
766         g_debug("dest_dir_set_cb ()");
767         ghb_widget_to_setting(ud->settings, (GtkWidget*)dest_chooser);
768         dest_file = ghb_settings_get_string(ud->settings, "dest_file");
769         dest_dir = ghb_settings_get_string(ud->settings, "dest_dir");
770         dest = g_strdup_printf("%s/%s", dest_dir, dest_file);
771         ghb_settings_set_string(ud->settings, "destination", dest);
772         g_free(dest_file);
773         g_free(dest_dir);
774         g_free(dest);
775         update_default_destination = TRUE;
776 }
777
778 void
779 dest_file_changed_cb(GtkEntry *entry, signal_user_data_t *ud)
780 {
781         gchar *dest_file, *dest_dir, *dest;
782         
783         g_debug("dest_file_changed_cb ()");
784         update_destination_extension(ud);
785         ghb_widget_to_setting(ud->settings, (GtkWidget*)entry);
786         // This signal goes off with ever keystroke, so I'm putting this
787         // update on the timer.
788         dest_file = ghb_settings_get_string(ud->settings, "dest_file");
789         dest_dir = ghb_settings_get_string(ud->settings, "dest_dir");
790         dest = g_strdup_printf("%s/%s", dest_dir, dest_file);
791         ghb_settings_set_string(ud->settings, "destination", dest);
792         g_free(dest_file);
793         g_free(dest_dir);
794         g_free(dest);
795         update_default_destination = TRUE;
796 }
797
798 void
799 destination_browse_clicked_cb(GtkButton *button, signal_user_data_t *ud)
800 {
801         GtkWidget *dialog;
802         GtkEntry *entry;
803         gchar *destname;
804         gchar *basename;
805
806         g_debug("destination_browse_clicked_cb ()");
807         destname = ghb_settings_get_string(ud->settings, "destination");
808         dialog = gtk_file_chooser_dialog_new ("Choose Destination",
809                                           NULL,
810                                           GTK_FILE_CHOOSER_ACTION_SAVE,
811                                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
812                                           GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
813                                           NULL);
814         gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), destname);
815         basename = g_path_get_basename(destname);
816         g_free(destname);
817         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), basename);
818         g_free(basename);
819         if (gtk_dialog_run(GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
820         {
821                 char *filename, *dirname;
822                 GtkFileChooser *dest_chooser;
823                 
824                 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
825                 basename = g_path_get_basename(filename);
826                 dirname = g_path_get_dirname(filename);
827                 entry = (GtkEntry*)GHB_WIDGET(ud->builder, "dest_file");
828                 gtk_entry_set_text(entry, basename);
829                 dest_chooser = GTK_FILE_CHOOSER(GHB_WIDGET(ud->builder, "dest_dir"));
830                 gtk_file_chooser_set_filename(dest_chooser, dirname);
831                 g_free (dirname);
832                 g_free (basename);
833                 g_free (filename);
834         }
835         gtk_widget_destroy(dialog);
836 }
837
838 gboolean
839 window_destroy_event_cb(GtkWidget *widget, GdkEvent *event, signal_user_data_t *ud)
840 {
841         g_debug("window_destroy_event_cb ()");
842         ghb_hb_cleanup(FALSE);
843         prune_logs(ud);
844         gtk_main_quit();
845         return FALSE;
846 }
847
848 gboolean
849 window_delete_event_cb(GtkWidget *widget, GdkEvent *event, signal_user_data_t *ud)
850 {
851         gint state = ghb_get_queue_state();
852         g_debug("window_delete_event_cb ()");
853         if (state & GHB_STATE_WORKING)
854         {
855                 if (ghb_cancel_encode("Closing HandBrake will terminate encoding.\n"))
856                 {
857                         ghb_hb_cleanup(FALSE);
858                         prune_logs(ud);
859                         gtk_main_quit();
860                         return FALSE;
861                 }
862                 return TRUE;
863         }
864         ghb_hb_cleanup(FALSE);
865         prune_logs(ud);
866         gtk_main_quit();
867         return FALSE;
868 }
869
870 static void
871 update_acodec_combo(signal_user_data_t *ud)
872 {
873         ghb_grey_combo_options (ud->builder);
874 }
875
876 void
877 container_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
878 {
879         const GValue *audio_list;
880         g_debug("container_changed_cb ()");
881         ghb_widget_to_setting(ud->settings, widget);
882         update_destination_extension(ud);
883         ghb_check_dependency(ud, widget);
884         update_acodec_combo(ud);
885         ghb_clear_presets_selection(ud);
886         ghb_live_reset(ud);
887
888         audio_list = ghb_settings_get_value(ud->settings, "audio_list");
889         if (ghb_ac3_in_audio_list (audio_list))
890         {
891                 gchar *container;
892
893                 container = ghb_settings_get_string(ud->settings, "FileFormat");
894                 if (strcmp(container, "mp4") == 0)
895                 {
896                         ghb_ui_update(ud, "FileFormat", ghb_string_value("m4v"));
897                 }
898                 g_free(container);
899         }
900 }
901
902 static gchar*
903 get_aspect_string(gint aspect_n, gint aspect_d)
904 {
905         gchar *aspect;
906
907         if (aspect_d < 10)
908         {
909                 aspect = g_strdup_printf("%d:%d", aspect_n, aspect_d);
910         }
911         else
912         {
913                 gdouble aspect_nf = (gdouble)aspect_n / aspect_d;
914                 aspect = g_strdup_printf("%.2f:1", aspect_nf);
915         }
916         return aspect;
917 }
918
919 static gchar*
920 get_rate_string(gint rate_base, gint rate)
921 {
922         gdouble rate_f = (gdouble)rate / rate_base;
923         gchar *rate_s;
924
925         rate_s = g_strdup_printf("%.6g", rate_f);
926         return rate_s;
927 }
928 static void
929 show_title_info(signal_user_data_t *ud, ghb_title_info_t *tinfo)
930 {
931         GtkWidget *widget;
932         gchar *text;
933
934         widget = GHB_WIDGET (ud->builder, "title_duration");
935         if (tinfo->duration != 0)
936         {
937                 text = g_strdup_printf ("%02d:%02d:%02d", tinfo->hours, 
938                                 tinfo->minutes, tinfo->seconds);
939         }
940         else
941         {
942                 text = g_strdup_printf ("Unknown");
943         }
944         gtk_label_set_text (GTK_LABEL(widget), text);
945         g_free(text);
946         widget = GHB_WIDGET (ud->builder, "source_dimensions");
947         text = g_strdup_printf ("%d x %d", tinfo->width, tinfo->height);
948         gtk_label_set_text (GTK_LABEL(widget), text);
949         ghb_settings_set_int(ud->settings, "source_width", tinfo->width);
950         ghb_settings_set_int(ud->settings, "source_height", tinfo->height);
951         g_free(text);
952         widget = GHB_WIDGET (ud->builder, "source_aspect");
953         text = get_aspect_string(tinfo->aspect_n, tinfo->aspect_d);
954         gtk_label_set_text (GTK_LABEL(widget), text);
955         g_free(text);
956
957         widget = GHB_WIDGET (ud->builder, "source_frame_rate");
958         text = (gchar*)get_rate_string(tinfo->rate_base, tinfo->rate);
959         gtk_label_set_text (GTK_LABEL(widget), text);
960         g_free(text);
961
962         ghb_ui_update(ud, "scale_width", 
963                 ghb_int64_value(tinfo->width - tinfo->crop[2] - tinfo->crop[3]));
964         // If anamorphic or keep_aspect, the hight will be automatically calculated
965         gboolean keep_aspect, anamorphic;
966         keep_aspect = ghb_settings_get_boolean(ud->settings, "PictureKeepRatio");
967         anamorphic = ghb_settings_get_boolean(ud->settings, "anamorphic");
968         if (!(keep_aspect || anamorphic))
969         {
970                 ghb_ui_update(ud, "scale_height", 
971                         ghb_int64_value(tinfo->height - tinfo->crop[0] - tinfo->crop[1]));
972         }
973
974         // Set the limits of cropping.  hb_set_anamorphic_size crashes if
975         // you pass it a cropped width or height == 0.
976         gint bound;
977         bound = tinfo->height / 2 - 2;
978         widget = GHB_WIDGET (ud->builder, "PictureTopCrop");
979         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
980         widget = GHB_WIDGET (ud->builder, "PictureBottomCrop");
981         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
982         bound = tinfo->width / 2 - 2;
983         widget = GHB_WIDGET (ud->builder, "PictureLeftCrop");
984         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
985         widget = GHB_WIDGET (ud->builder, "PictureRightCrop");
986         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
987         if (ghb_settings_get_boolean(ud->settings, "PictureAutoCrop"))
988         {
989                 ghb_ui_update(ud, "PictureTopCrop", ghb_int64_value(tinfo->crop[0]));
990                 ghb_ui_update(ud, "PictureBottomCrop", ghb_int64_value(tinfo->crop[1]));
991                 ghb_ui_update(ud, "PictureLeftCrop", ghb_int64_value(tinfo->crop[2]));
992                 ghb_ui_update(ud, "PictureRightCrop", ghb_int64_value(tinfo->crop[3]));
993         }
994         ghb_set_scale (ud, GHB_SCALE_KEEP_NONE);
995         gint width, height, crop[4];
996         crop[0] = ghb_settings_get_int(ud->settings, "PictureTopCrop");
997         crop[1] = ghb_settings_get_int(ud->settings, "PictureBottomCrop");
998         crop[2] = ghb_settings_get_int(ud->settings, "PictureLeftCrop");
999         crop[3] = ghb_settings_get_int(ud->settings, "PictureRightCrop");
1000         width = tinfo->width - crop[2] - crop[3];
1001         height = tinfo->height - crop[0] - crop[1];
1002         widget = GHB_WIDGET (ud->builder, "crop_dimensions");
1003         text = g_strdup_printf ("%d x %d", width, height);
1004         gtk_label_set_text (GTK_LABEL(widget), text);
1005         g_free(text);
1006
1007         g_debug("setting max end chapter %d", tinfo->num_chapters);
1008         widget = GHB_WIDGET (ud->builder, "end_chapter");
1009         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, tinfo->num_chapters);
1010         gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), tinfo->num_chapters);
1011         widget = GHB_WIDGET (ud->builder, "start_chapter");
1012         gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), 1);
1013         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, tinfo->num_chapters);
1014 }
1015
1016 static gboolean update_preview = FALSE;
1017
1018 void
1019 title_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1020 {
1021         ghb_title_info_t tinfo;
1022         gint titleindex;
1023         
1024         g_debug("title_changed_cb ()");
1025         ghb_widget_to_setting(ud->settings, widget);
1026         ghb_check_dependency(ud, widget);
1027
1028         titleindex = ghb_settings_combo_int(ud->settings, "title");
1029         ghb_update_ui_combo_box (ud->builder, "AudioTrack", titleindex, FALSE);
1030         ghb_update_ui_combo_box (ud->builder, "Subtitles", titleindex, FALSE);
1031
1032         ghb_update_from_preset(ud, "Subtitles");
1033         if (ghb_get_title_info (&tinfo, titleindex))
1034         {
1035                 show_title_info(ud, &tinfo);
1036         }
1037         update_chapter_list (ud);
1038         ghb_adjust_audio_rate_combos(ud);
1039         ghb_set_pref_audio(titleindex, ud);
1040         if (ghb_settings_get_boolean(ud->settings, "vquality_type_target"))
1041         {
1042                 gint bitrate = ghb_calculate_target_bitrate (ud->settings, titleindex);
1043                 ghb_ui_update(ud, "VideoAvgBitrate", ghb_int64_value(bitrate));
1044         }
1045
1046         // Unfortunately, there is no way to query how many frames were
1047         // actually generated during the scan.
1048         // If I knew how many were generated, I would adjust the spin
1049         // control range here.
1050         // I do know how many were asked for.
1051         gint preview_count;
1052         preview_count = ghb_settings_get_int(ud->settings, "preview_count");
1053         widget = GHB_WIDGET(ud->builder, "preview_frame");
1054         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, preview_count);
1055         ghb_ui_update(ud, "preview_frame", ghb_int64_value(2));
1056
1057         ghb_set_preview_image (ud);
1058         if (ghb_settings_get_boolean(ud->settings, "title_no_in_destination"))
1059         {
1060                 set_destination(ud);
1061         }
1062 }
1063
1064 void
1065 setting_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1066 {
1067         ghb_widget_to_setting(ud->settings, widget);
1068         ghb_check_dependency(ud, widget);
1069         ghb_clear_presets_selection(ud);
1070         ghb_live_reset(ud);
1071 }
1072
1073 static void
1074 validate_filter_widget(signal_user_data_t *ud, const gchar *name)
1075 {
1076         GtkTreeModel *store;
1077         GtkTreeIter iter;
1078         const gchar *str;
1079         gboolean foundit = FALSE;
1080         GtkComboBox *combo = GTK_COMBO_BOX(GHB_WIDGET(ud->builder, name));
1081         if (gtk_combo_box_get_active(combo) < 0)
1082         { // Validate user input
1083                 gchar *val = ghb_settings_get_string(ud->settings, name);
1084                 store = gtk_combo_box_get_model(combo);
1085                 // Check to see if user manually entered one of the combo options
1086                 if (gtk_tree_model_get_iter_first(store, &iter))
1087                 {
1088                         do
1089                         {
1090                                 gtk_tree_model_get(store, &iter, 0, &str, -1);
1091                                 if (strcasecmp(val, str) == 0)
1092                                 {
1093                                         gtk_combo_box_set_active_iter(combo, &iter);
1094                                         foundit = TRUE;
1095                                         break;
1096                                 }
1097                         } while (gtk_tree_model_iter_next(store, &iter));
1098                 }
1099                 if (!foundit)
1100                 { // validate format of filter string
1101                         if (!ghb_validate_filter_string(val, -1))
1102                                 gtk_combo_box_set_active(combo, 0);
1103                 }
1104                 g_free(val);
1105         }
1106 }
1107
1108 gboolean
1109 deint_tweak_focus_out_cb(GtkWidget *widget, GdkEventFocus *event, 
1110         signal_user_data_t *ud)
1111 {
1112         g_debug("deint_tweak_focus_out_cb ()");
1113         validate_filter_widget(ud, "tweak_PictureDeinterlace");
1114         return FALSE;
1115 }
1116
1117 gboolean
1118 denoise_tweak_focus_out_cb(GtkWidget *widget, GdkEventFocus *event, 
1119         signal_user_data_t *ud)
1120 {
1121         g_debug("denoise_tweak_focus_out_cb ()");
1122         validate_filter_widget(ud, "tweak_PictureDenoise");
1123         return FALSE;
1124 }
1125
1126 void
1127 http_opt_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1128 {
1129         ghb_widget_to_setting(ud->settings, widget);
1130         ghb_check_dependency(ud, widget);
1131         ghb_clear_presets_selection(ud);
1132         ghb_live_reset(ud);
1133         // AC3 is not allowed when Web optimized
1134         ghb_grey_combo_options (ud->builder);
1135 }
1136
1137 void
1138 vcodec_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1139 {
1140         gdouble vqmin, vqmax, step, page;
1141         gint digits;
1142         gint vcodec;
1143
1144         ghb_widget_to_setting(ud->settings, widget);
1145         ghb_check_dependency(ud, widget);
1146         ghb_clear_presets_selection(ud);
1147         ghb_live_reset(ud);
1148         ghb_vquality_range(ud, &vqmin, &vqmax, &step, &page, &digits);
1149         GtkWidget *qp = GHB_WIDGET(ud->builder, "VideoQualitySlider");
1150         gtk_range_set_range (GTK_RANGE(qp), vqmin, vqmax);
1151         gtk_range_set_increments (GTK_RANGE(qp), step, page);
1152         gtk_scale_set_digits(GTK_SCALE(qp), digits);
1153         vcodec = ghb_settings_combo_int(ud->settings, "VideoEncoder");
1154         if (vcodec != HB_VCODEC_X264 && vcodec != HB_VCODEC_FFMPEG)
1155         {
1156                 ghb_ui_update(ud, "directqp", ghb_boolean_value(FALSE));
1157         }
1158 }
1159
1160 void
1161 target_size_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1162 {
1163         const gchar *name = gtk_widget_get_name(widget);
1164         g_debug("setting_widget_changed_cb () %s", name);
1165         ghb_widget_to_setting(ud->settings, widget);
1166         ghb_check_dependency(ud, widget);
1167         ghb_clear_presets_selection(ud);
1168         ghb_live_reset(ud);
1169         if (ghb_settings_get_boolean(ud->settings, "vquality_type_target"))
1170         {
1171                 gint titleindex;
1172                 titleindex = ghb_settings_combo_int(ud->settings, "title");
1173                 gint bitrate = ghb_calculate_target_bitrate (ud->settings, titleindex);
1174                 ghb_ui_update(ud, "VideoAvgBitrate", ghb_int64_value(bitrate));
1175         }
1176 }
1177
1178 void
1179 start_chapter_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1180 {
1181         gint start, end;
1182         const gchar *name = gtk_widget_get_name(widget);
1183
1184         g_debug("start_chapter_changed_cb () %s", name);
1185         ghb_widget_to_setting(ud->settings, widget);
1186         start = ghb_settings_get_int(ud->settings, "start_chapter");
1187         end = ghb_settings_get_int(ud->settings, "end_chapter");
1188         if (start > end)
1189                 ghb_ui_update(ud, "end_chapter", ghb_int_value(start));
1190         ghb_check_dependency(ud, widget);
1191         if (ghb_settings_get_boolean(ud->settings, "chapters_in_destination"))
1192         {
1193                 set_destination(ud);
1194         }
1195 }
1196
1197 void
1198 end_chapter_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1199 {
1200         gint start, end;
1201         const gchar *name = gtk_widget_get_name(widget);
1202
1203         g_debug("end_chapter_changed_cb () %s", name);
1204         ghb_widget_to_setting(ud->settings, widget);
1205         start = ghb_settings_get_int(ud->settings, "start_chapter");
1206         end = ghb_settings_get_int(ud->settings, "end_chapter");
1207         if (start > end)
1208                 ghb_ui_update(ud, "start_chapter", ghb_int_value(end));
1209         ghb_check_dependency(ud, widget);
1210         if (ghb_settings_get_boolean(ud->settings, "chapters_in_destination"))
1211         {
1212                 set_destination(ud);
1213         }
1214 }
1215
1216 void
1217 scale_width_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1218 {
1219         g_debug("scale_width_changed_cb ()");
1220         ghb_widget_to_setting(ud->settings, widget);
1221         ghb_check_dependency(ud, widget);
1222         ghb_set_scale (ud, GHB_SCALE_KEEP_WIDTH);
1223         update_preview = TRUE;
1224         gchar *text;
1225         gint width = ghb_settings_get_int(ud->settings, "scale_width");
1226         gint height = ghb_settings_get_int(ud->settings, "scale_height");
1227         widget = GHB_WIDGET (ud->builder, "scale_dimensions");
1228         text = g_strdup_printf ("%d x %d", width, height);
1229         gtk_label_set_text (GTK_LABEL(widget), text);
1230         g_free(text);
1231         ghb_live_reset(ud);
1232 }
1233
1234 void
1235 scale_height_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1236 {
1237         g_debug("scale_height_changed_cb ()");
1238         ghb_widget_to_setting(ud->settings, widget);
1239         ghb_check_dependency(ud, widget);
1240         ghb_set_scale (ud, GHB_SCALE_KEEP_HEIGHT);
1241         update_preview = TRUE;
1242         gchar *text;
1243         gint width = ghb_settings_get_int(ud->settings, "scale_width");
1244         gint height = ghb_settings_get_int(ud->settings, "scale_height");
1245         widget = GHB_WIDGET (ud->builder, "scale_dimensions");
1246         text = g_strdup_printf ("%d x %d", width, height);
1247         gtk_label_set_text (GTK_LABEL(widget), text);
1248         g_free(text);
1249         ghb_live_reset(ud);
1250 }
1251
1252 void
1253 crop_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1254 {
1255         gint titleindex, crop[4];
1256         ghb_title_info_t tinfo;
1257         
1258         g_debug("crop_changed_cb ()");
1259         ghb_widget_to_setting(ud->settings, widget);
1260         ghb_check_dependency(ud, widget);
1261         ghb_set_scale (ud, GHB_SCALE_KEEP_NONE);
1262
1263         crop[0] = ghb_settings_get_int(ud->settings, "PictureTopCrop");
1264         crop[1] = ghb_settings_get_int(ud->settings, "PictureBottomCrop");
1265         crop[2] = ghb_settings_get_int(ud->settings, "PictureLeftCrop");
1266         crop[3] = ghb_settings_get_int(ud->settings, "PictureRightCrop");
1267         titleindex = ghb_settings_combo_int(ud->settings, "title");
1268         if (ghb_get_title_info (&tinfo, titleindex))
1269         {
1270                 gint width, height;
1271                 gchar *text;
1272                 
1273                 width = tinfo.width - crop[2] - crop[3];
1274                 height = tinfo.height - crop[0] - crop[1];
1275                 widget = GHB_WIDGET (ud->builder, "crop_dimensions");
1276                 text = g_strdup_printf ("%d x %d", width, height);
1277                 gtk_label_set_text (GTK_LABEL(widget), text);
1278                 g_free(text);
1279         }
1280         gchar *text;
1281         widget = GHB_WIDGET (ud->builder, "crop_values");
1282         text = g_strdup_printf ("%d:%d:%d:%d", crop[0], crop[1], crop[2], crop[3]);
1283         gtk_label_set_text (GTK_LABEL(widget), text);
1284         g_free(text);
1285         update_preview = TRUE;
1286         ghb_live_reset(ud);
1287 }
1288
1289 void
1290 scale_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1291 {
1292         g_debug("scale_changed_cb ()");
1293         ghb_widget_to_setting(ud->settings, widget);
1294         ghb_check_dependency(ud, widget);
1295         ghb_clear_presets_selection(ud);
1296         ghb_live_reset(ud);
1297         ghb_set_scale (ud, GHB_SCALE_KEEP_NONE);
1298         update_preview = TRUE;
1299         
1300         gchar *text;
1301         
1302         text = ghb_settings_get_boolean(ud->settings, "PictureAutoCrop") ? "On" : "Off";
1303         widget = GHB_WIDGET (ud->builder, "crop_auto");
1304         gtk_label_set_text (GTK_LABEL(widget), text);
1305         text = ghb_settings_get_boolean(ud->settings, "autoscale") ? "On" : "Off";
1306         widget = GHB_WIDGET (ud->builder, "scale_auto");
1307         gtk_label_set_text (GTK_LABEL(widget), text);
1308         text = ghb_settings_get_boolean(ud->settings, "anamorphic") ? "On" : "Off";
1309         widget = GHB_WIDGET (ud->builder, "scale_anamorphic");
1310         gtk_label_set_text (GTK_LABEL(widget), text);
1311 }
1312
1313 void
1314 generic_entry_changed_cb(GtkEntry *entry, signal_user_data_t *ud)
1315 {
1316         // Normally (due to user input) I only want to process the entry
1317         // when editing is done and the focus-out signal is sent.
1318         // But... there's always a but.
1319         // If the entry is changed by software, the focus-out signal is not sent.
1320         // The changed signal is sent ... so here we are.
1321         // I don't want to process upon every keystroke, so I prevent processing
1322         // while the widget has focus.
1323         g_debug("generic_entry_changed_cb ()");
1324         if (!GTK_WIDGET_HAS_FOCUS((GtkWidget*)entry))
1325         {
1326                 ghb_widget_to_setting(ud->settings, (GtkWidget*)entry);
1327         }
1328 }
1329
1330 void
1331 prefs_dialog_cb(GtkWidget *xwidget, signal_user_data_t *ud)
1332 {
1333         GtkWidget *dialog;
1334         GtkResponseType response;
1335
1336         g_debug("prefs_dialog_cb ()");
1337         dialog = GHB_WIDGET(ud->builder, "prefs_dialog");
1338         response = gtk_dialog_run(GTK_DIALOG(dialog));
1339         gtk_widget_hide(dialog);
1340 }
1341
1342 gboolean
1343 ghb_message_dialog(GtkMessageType type, const gchar *message, const gchar *no, const gchar *yes)
1344 {
1345         GtkWidget *dialog;
1346         GtkResponseType response;
1347                         
1348         // Toss up a warning dialog
1349         dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
1350                                                         type, GTK_BUTTONS_NONE,
1351                                                         "%s", message);
1352         gtk_dialog_add_buttons( GTK_DIALOG(dialog), 
1353                                                    no, GTK_RESPONSE_NO,
1354                                                    yes, GTK_RESPONSE_YES, NULL);
1355         response = gtk_dialog_run(GTK_DIALOG(dialog));
1356         gtk_widget_destroy (dialog);
1357         if (response == GTK_RESPONSE_NO)
1358         {
1359                 return FALSE;
1360         }
1361         return TRUE;
1362 }
1363
1364 gboolean
1365 ghb_cancel_encode(const gchar *extra_msg)
1366 {
1367         GtkWidget *dialog;
1368         GtkResponseType response;
1369         
1370         if (extra_msg == NULL) extra_msg = "";
1371         // Toss up a warning dialog
1372         dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
1373                                 GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE,
1374                                 "%sYour movie will be lost if you don't continue encoding.",
1375                                 extra_msg);
1376         gtk_dialog_add_buttons( GTK_DIALOG(dialog), 
1377                                                    "Continue Encoding", GTK_RESPONSE_NO,
1378                                                    "Stop Encoding", GTK_RESPONSE_YES, NULL);
1379         response = gtk_dialog_run(GTK_DIALOG(dialog));
1380         gtk_widget_destroy (dialog);
1381         if (response == GTK_RESPONSE_NO) return FALSE;
1382         ghb_stop_queue();
1383         return TRUE;
1384 }
1385
1386 static void
1387 submit_job(GValue *settings)
1388 {
1389         static gint unique_id = 1;
1390         gchar *type, *modified, *preset;
1391         const GValue *path;
1392         gboolean preset_modified;
1393
1394         g_debug("submit_job");
1395         if (settings == NULL) return;
1396         preset_modified = ghb_settings_get_boolean(settings, "preset_modified");
1397         path = ghb_settings_get_value(settings, "preset");
1398         preset = ghb_preset_path_string(path);
1399         type = ghb_preset_is_custom() ? "Custom " : "";
1400         modified = preset_modified ? "Modified " : "";
1401         ghb_log("%s%sPreset: %s", modified, type, preset);
1402         g_free(preset);
1403
1404         ghb_settings_set_int(settings, "job_unique_id", unique_id);
1405         ghb_settings_set_int(settings, "job_status", GHB_QUEUE_RUNNING);
1406         ghb_add_job (settings, unique_id);
1407         ghb_start_queue();
1408         unique_id++;
1409 }
1410
1411 static void
1412 prune_logs(signal_user_data_t *ud)
1413 {
1414         gchar *dest_dir;
1415
1416         // Only prune logs stored in the default config dir location
1417         dest_dir = ghb_get_user_config_dir("EncodeLogs");
1418         if (g_file_test(dest_dir, G_FILE_TEST_IS_DIR))
1419         {
1420                 const gchar *file;
1421                 int week = 7*24*60*60;
1422                 GDir *gdir = g_dir_open(dest_dir, 0, NULL);
1423                 time_t now;
1424
1425                 now = time(NULL);
1426                 file = g_dir_read_name(gdir);
1427                 while (file)
1428                 {
1429                         gchar *path;
1430                         struct stat stbuf;
1431
1432                         path = g_strdup_printf("%s/%s", dest_dir, file);
1433                         g_stat(path, &stbuf);
1434                         if (now - stbuf.st_mtime > week)
1435                         {
1436                                 g_unlink(path);
1437                         }
1438                         g_free(path);
1439                         file = g_dir_read_name(gdir);
1440                 }
1441                 g_dir_close(gdir);
1442         }
1443         g_free(dest_dir);
1444         ghb_preview_cleanup(ud);
1445 }
1446
1447 static void
1448 queue_scan(signal_user_data_t *ud, GValue *js)
1449 {
1450         gchar *path;
1451         gint titlenum;
1452         time_t  _now;
1453         struct tm *now;
1454         gchar *log_path, *pos, *destname, *basename, *dest_dir;
1455
1456         _now = time(NULL);
1457         now = localtime(&_now);
1458         destname = ghb_settings_get_string(js, "destination");
1459         basename = g_path_get_basename(destname);
1460         if (ghb_settings_get_boolean(ud->settings, "EncodeLogLocation"))
1461         {
1462                 dest_dir = g_path_get_dirname (destname);
1463         }
1464         else
1465         {
1466                 dest_dir = ghb_get_user_config_dir("EncodeLogs");
1467         }
1468         g_free(destname);
1469         pos = g_strrstr( basename, "." );
1470         if (pos != NULL)
1471         {
1472                 *pos = 0;
1473         }
1474         log_path = g_strdup_printf("%s/%d-%02d-%02d %02d-%02d-%02d %s.log",
1475                 dest_dir,
1476                 now->tm_year + 1900, now->tm_mon + 1, now->tm_mday,
1477                 now->tm_hour, now->tm_min, now->tm_sec, basename);
1478         g_free(basename);
1479         g_free(dest_dir);
1480         if (ud->job_activity_log)
1481                 g_io_channel_unref(ud->job_activity_log);
1482         ud->job_activity_log = g_io_channel_new_file (log_path, "w", NULL);
1483         if (ud->job_activity_log)
1484         {
1485                 gchar *ver_str;
1486
1487                 ver_str = g_strdup_printf("Handbrake Version: %s (%d)\n", 
1488                                                                         HB_VERSION, HB_BUILD);
1489                 g_io_channel_write_chars (ud->job_activity_log, ver_str, 
1490                                                                         -1, NULL, NULL);
1491                 g_free(ver_str);
1492         }
1493         g_free(log_path);
1494
1495         path = ghb_settings_get_string( js, "source");
1496         titlenum = ghb_settings_get_int(js, "titlenum");
1497         ghb_backend_queue_scan(path, titlenum);
1498         g_free(path);
1499 }
1500
1501 GValue* 
1502 ghb_start_next_job(signal_user_data_t *ud, gboolean find_first)
1503 {
1504         static gint current = 0;
1505         gint count, ii, jj;
1506         GValue *js;
1507         gint status;
1508
1509         g_debug("start_next_job");
1510         count = ghb_array_len(ud->queue);
1511         if (find_first)
1512         {       // Start the first pending item in the queue
1513                 current = 0;
1514                 for (ii = 0; ii < count; ii++)
1515                 {
1516
1517                         js = ghb_array_get_nth(ud->queue, ii);
1518                         status = ghb_settings_get_int(js, "job_status");
1519                         if (status == GHB_QUEUE_PENDING)
1520                         {
1521                                 current = ii;
1522                                 ghb_inhibit_gpm();
1523                                 queue_scan(ud, js);
1524                                 return js;
1525                         }
1526                 }
1527                 // Nothing pending
1528                 ghb_uninhibit_gpm();
1529                 return NULL;
1530         }
1531         // Find the next pending item after the current running item
1532         for (ii = 0; ii < count-1; ii++)
1533         {
1534                 js = ghb_array_get_nth(ud->queue, ii);
1535                 status = ghb_settings_get_int(js, "job_status");
1536                 if (status == GHB_QUEUE_RUNNING)
1537                 {
1538                         for (jj = ii+1; jj < count; jj++)
1539                         {
1540                                 js = ghb_array_get_nth(ud->queue, jj);
1541                                 status = ghb_settings_get_int(js, "job_status");
1542                                 if (status == GHB_QUEUE_PENDING)
1543                                 {
1544                                         current = jj;
1545                                         ghb_inhibit_gpm();
1546                                         queue_scan(ud, js);
1547                                         return js;
1548                                 }
1549                         }
1550                 }
1551         }
1552         // No running item found? Maybe it was deleted
1553         // Look for a pending item starting from the last index we started
1554         for (ii = current; ii < count; ii++)
1555         {
1556                 js = ghb_array_get_nth(ud->queue, ii);
1557                 status = ghb_settings_get_int(js, "job_status");
1558                 if (status == GHB_QUEUE_PENDING)
1559                 {
1560                         current = ii;
1561                         ghb_inhibit_gpm();
1562                         queue_scan(ud, js);
1563                         return js;
1564                 }
1565         }
1566         // Nothing found
1567         ghb_uninhibit_gpm();
1568         return NULL;
1569 }
1570
1571 static gint
1572 find_queue_job(GValue *queue, gint unique_id, GValue **job)
1573 {
1574         GValue *js;
1575         gint ii, count;
1576         gint job_unique_id;
1577         
1578         *job = NULL;
1579         g_debug("find_queue_job");
1580         count = ghb_array_len(queue);
1581         for (ii = 0; ii < count; ii++)
1582         {
1583                 js = ghb_array_get_nth(queue, ii);
1584                 job_unique_id = ghb_settings_get_int(js, "job_unique_id");
1585                 if (job_unique_id == unique_id)
1586                 {
1587                         *job = js;
1588                         return ii;
1589                 }
1590         }
1591         return -1;
1592 }
1593
1594 gchar*
1595 working_status_string(signal_user_data_t *ud, ghb_instance_status_t *status)
1596 {
1597         gchar *task_str, *job_str, *status_str;
1598         gint qcount;
1599         gint index;
1600         GValue *js;
1601
1602         if (status->job_count > 1)
1603         {
1604                 task_str = g_strdup_printf("pass %d of %d, ", 
1605                         status->job_cur, status->job_count);
1606         }
1607         else
1608         {
1609                 task_str = g_strdup("");
1610         }
1611         qcount = ghb_array_len(ud->queue);
1612         if (qcount > 1)
1613         {
1614                 index = find_queue_job(ud->queue, status->unique_id, &js);
1615                 job_str = g_strdup_printf("job %d of %d, ", index+1, qcount);
1616         }
1617         else
1618         {
1619                 job_str = g_strdup("");
1620         }
1621         if(status->seconds > -1)
1622         {
1623                 status_str= g_strdup_printf(
1624                         "Encoding: %s%s%.2f %%"
1625                         " (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)",
1626                         job_str, task_str,
1627                         100.0 * status->progress,
1628                         status->rate_cur, status->rate_avg, status->hours, 
1629                         status->minutes, status->seconds );
1630         }
1631         else
1632         {
1633                 status_str= g_strdup_printf(
1634                         "Encoding: %s%s%.2f %%",
1635                         job_str, task_str,
1636                         100.0 * status->progress );
1637         }
1638         g_free(task_str);
1639         g_free(job_str);
1640         return status_str;
1641 }
1642
1643 static void
1644 ghb_backend_events(signal_user_data_t *ud)
1645 {
1646         ghb_status_t status;
1647         gchar *status_str;
1648         GtkProgressBar *progress;
1649         gint titleindex;
1650         GValue *js;
1651         gint index;
1652         GtkTreeView *treeview;
1653         GtkTreeStore *store;
1654         GtkTreeIter iter;
1655         static gint working = 0;
1656         static gboolean work_started = FALSE;
1657         
1658         ghb_track_status();
1659         ghb_get_status(&status);
1660         progress = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "progressbar"));
1661         // First handle the status of title scans
1662         // Then handle the status of the queue
1663         if (status.scan.state & GHB_STATE_SCANNING)
1664         {
1665                 if (status.scan.title_cur == 0)
1666                 {
1667                         status_str = g_strdup ("Scanning...");
1668                 }
1669                 else
1670                 {
1671                         status_str = g_strdup_printf ("Scanning title %d of %d...", 
1672                                                           status.scan.title_cur, status.scan.title_count );
1673                 }
1674                 gtk_progress_bar_set_text (progress, status_str);
1675                 g_free(status_str);
1676                 if (status.scan.title_count > 0)
1677                 {
1678                         gtk_progress_bar_set_fraction (progress, 
1679                                 (gdouble)status.scan.title_cur / status.scan.title_count);
1680                 }
1681         }
1682         else if (status.scan.state & GHB_STATE_SCANDONE)
1683         {
1684                 status_str = g_strdup_printf ("Scan done"); 
1685                 gtk_progress_bar_set_text (progress, status_str);
1686                 g_free(status_str);
1687                 gtk_progress_bar_set_fraction (progress, 1.0);
1688
1689                 ghb_title_info_t tinfo;
1690                         
1691                 ghb_update_ui_combo_box(ud->builder, "title", 0, FALSE);
1692                 titleindex = ghb_longest_title();
1693                 ghb_ui_update(ud, "title", ghb_int64_value(titleindex));
1694
1695                 // Are there really any titles.
1696                 if (!ghb_get_title_info(&tinfo, titleindex))
1697                 {
1698                         GtkProgressBar *progress;
1699                         progress = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "progressbar"));
1700                         gtk_progress_bar_set_fraction (progress, 0);
1701                         gtk_progress_bar_set_text (progress, "No Source");
1702                 }
1703                 ghb_clear_scan_state(GHB_STATE_SCANDONE);
1704                 ghb_queue_buttons_grey(ud, work_started);
1705                 if (ghb_queue_edit_settings)
1706                 {
1707                         gint jstatus;
1708
1709                         jstatus = ghb_settings_get_int(ghb_queue_edit_settings, "job_status");
1710                         ghb_settings_to_ui(ud, ghb_queue_edit_settings);
1711                         ghb_set_audio(ud, ghb_queue_edit_settings);
1712                         if (jstatus == GHB_QUEUE_PENDING)
1713                         {
1714                                 ghb_value_free(ghb_queue_edit_settings);
1715                         }
1716                         ghb_queue_edit_settings = NULL;
1717                 }
1718         }
1719         else if (status.queue.state & GHB_STATE_SCANNING)
1720         {
1721                 status_str = g_strdup_printf ("Scanning ...");
1722                 gtk_progress_bar_set_text (progress, status_str);
1723                 g_free(status_str);
1724                 gtk_progress_bar_set_fraction (progress, 0);
1725         }
1726         else if (status.queue.state & GHB_STATE_SCANDONE)
1727         {
1728                 ghb_clear_queue_state(GHB_STATE_SCANDONE);
1729                 usleep(2000000);
1730                 submit_job(ud->current_job);
1731         }
1732         else if (status.queue.state & GHB_STATE_PAUSED)
1733         {
1734                 status_str = g_strdup_printf ("Paused"); 
1735                 gtk_progress_bar_set_text (progress, status_str);
1736                 g_free(status_str);
1737         }
1738         else if (status.queue.state & GHB_STATE_WORKING)
1739         {
1740                 status_str = working_status_string(ud, &status.queue);
1741                 gtk_progress_bar_set_text (progress, status_str);
1742                 gtk_progress_bar_set_fraction (progress, status.queue.progress);
1743                 g_free(status_str);
1744         }
1745         else if (status.queue.state & GHB_STATE_WORKDONE)
1746         {
1747                 gint qstatus;
1748
1749                 work_started = FALSE;
1750                 ghb_queue_buttons_grey(ud, FALSE);
1751                 index = find_queue_job(ud->queue, status.queue.unique_id, &js);
1752                 treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
1753                 store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
1754                 if (ud->cancel_encode)
1755                         status.queue.error = GHB_ERROR_CANCELED;
1756                 switch( status.queue.error )
1757                 {
1758                         case GHB_ERROR_NONE:
1759                                 gtk_progress_bar_set_text( progress, "Rip done!" );
1760                                 qstatus = GHB_QUEUE_DONE;
1761                                 if (js != NULL)
1762                                 {
1763                                         gchar *path = g_strdup_printf ("%d", index);
1764                                         if (gtk_tree_model_get_iter_from_string(
1765                                                         GTK_TREE_MODEL(store), &iter, path))
1766                                         {
1767                                                 gtk_tree_store_set(store, &iter, 0, "hb-complete", -1);
1768                                         }
1769                                         g_free(path);
1770                                 }
1771                                 break;
1772                         case GHB_ERROR_CANCELED:
1773                                 gtk_progress_bar_set_text( progress, "Rip canceled." );
1774                                 qstatus = GHB_QUEUE_CANCELED;
1775                                 if (js != NULL)
1776                                 {
1777                                         gchar *path = g_strdup_printf ("%d", index);
1778                                         if (gtk_tree_model_get_iter_from_string(
1779                                                         GTK_TREE_MODEL(store), &iter, path))
1780                                         {
1781                                                 gtk_tree_store_set(store, &iter, 0, "hb-canceled", -1);
1782                                         }
1783                                         g_free(path);
1784                                 }
1785                                 break;
1786                         default:
1787                                 gtk_progress_bar_set_text( progress, "Rip failed.");
1788                                 qstatus = GHB_QUEUE_CANCELED;
1789                                 if (js != NULL)
1790                                 {
1791                                         gchar *path = g_strdup_printf ("%d", index);
1792                                         if (gtk_tree_model_get_iter_from_string(
1793                                                         GTK_TREE_MODEL(store), &iter, path))
1794                                         {
1795                                                 gtk_tree_store_set(store, &iter, 0, "hb-canceled", -1);
1796                                         }
1797                                         g_free(path);
1798                                 }
1799                 }
1800                 gtk_progress_bar_set_fraction (progress, 1.0);
1801                 ghb_clear_queue_state(GHB_STATE_WORKDONE);
1802                 if (ud->job_activity_log)
1803                         g_io_channel_unref(ud->job_activity_log);
1804                 ud->job_activity_log = NULL;
1805                 if (!ud->cancel_encode)
1806                 {
1807                         ud->current_job = ghb_start_next_job(ud, FALSE);
1808                 }
1809                 else
1810                 {
1811                         ghb_uninhibit_gpm();
1812                         ud->current_job = NULL;
1813                 }
1814                 if (js)
1815                         ghb_settings_set_int(js, "job_status", qstatus);
1816                 ghb_save_queue(ud->queue);
1817                 ud->cancel_encode = FALSE;
1818         }
1819         else if (status.queue.state & GHB_STATE_MUXING)
1820         {
1821                 gtk_progress_bar_set_text(progress, "Muxing: this may take awhile...");
1822         }
1823         if (status.queue.state & GHB_STATE_SCANNING)
1824         {
1825                 // This needs to be in scanning and working since scanning
1826                 // happens fast enough that it can be missed
1827                 if (!work_started)
1828                 {
1829                         work_started = TRUE;
1830                         ghb_queue_buttons_grey(ud, TRUE);
1831                 }
1832         }
1833         if (status.queue.state & GHB_STATE_WORKING)
1834         {
1835                 // This needs to be in scanning and working since scanning
1836                 // happens fast enough that it can be missed
1837                 if (!work_started)
1838                 {
1839                         work_started = TRUE;
1840                         ghb_queue_buttons_grey(ud, TRUE);
1841                 }
1842                 index = find_queue_job(ud->queue, status.queue.unique_id, &js);
1843                 if (status.queue.unique_id != 0 && index >= 0)
1844                 {
1845                         gchar working_icon[] = "hb-working0";
1846                         working_icon[10] = '0' + working;
1847                         working = (working+1) % 6;
1848                         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
1849                         store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
1850                         gchar *path = g_strdup_printf ("%d", index);
1851                         if (gtk_tree_model_get_iter_from_string(
1852                                         GTK_TREE_MODEL(store), &iter, path))
1853                         {
1854                                 gtk_tree_store_set(store, &iter, 0, working_icon, -1);
1855                         }
1856                         g_free(path);
1857                 }
1858                 GtkLabel *label;
1859                 gchar *status_str;
1860
1861                 status_str = working_status_string(ud, &status.queue);
1862                 label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_status"));
1863                 gtk_label_set_text (label, status_str);
1864                 g_free(status_str);
1865         }
1866         if (status.scan.state & GHB_STATE_WORKING)
1867         {
1868                 GtkProgressBar *live_progress;
1869                 live_progress = GTK_PROGRESS_BAR(
1870                         GHB_WIDGET(ud->builder, "live_encode_progress"));
1871                 status_str = working_status_string(ud, &status.scan);
1872                 gtk_progress_bar_set_text (live_progress, status_str);
1873                 gtk_progress_bar_set_fraction (live_progress, status.scan.progress);
1874                 g_free(status_str);
1875         }
1876         if (status.scan.state & GHB_STATE_WORKDONE)
1877         {
1878                 switch( status.scan.error )
1879                 {
1880                         case GHB_ERROR_NONE:
1881                         {
1882                                 ghb_live_encode_done(ud, TRUE);
1883                         } break;
1884                         default:
1885                         {
1886                                 ghb_live_encode_done(ud, FALSE);
1887                         } break;
1888                 }
1889                 ghb_clear_scan_state(GHB_STATE_WORKDONE);
1890         }
1891 }
1892
1893 gboolean
1894 ghb_timer_cb(gpointer data)
1895 {
1896         signal_user_data_t *ud = (signal_user_data_t*)data;
1897
1898         ghb_live_preview_progress(ud);
1899         ghb_backend_events(ud);
1900         if (update_default_destination)
1901         {
1902                 gchar *dest, *dest_dir, *def_dest;
1903                 dest = ghb_settings_get_string(ud->settings, "destination");
1904                 dest_dir = g_path_get_dirname (dest);
1905                 def_dest = ghb_settings_get_string(ud->settings, "destination_dir");
1906                 if (strcmp(dest_dir, def_dest) != 0)
1907                 {
1908                         ghb_settings_set_string (ud->settings, "destination_dir", dest_dir);
1909                         ghb_pref_save (ud->settings, "destination_dir");
1910                 }
1911                 g_free(dest);
1912                 g_free(dest_dir);
1913                 g_free(def_dest);
1914                 update_default_destination = FALSE;
1915         }
1916         if (update_preview)
1917         {
1918                 ghb_set_preview_image (ud);
1919                 update_preview = FALSE;
1920         }
1921         return TRUE;
1922 }
1923
1924 gboolean
1925 ghb_log_cb(GIOChannel *source, GIOCondition cond, gpointer data)
1926 {
1927         gchar *text = NULL;
1928         gsize length;
1929         GtkTextView *textview;
1930         GtkTextBuffer *buffer;
1931         GtkTextIter iter;
1932         GtkTextMark *mark;
1933         GError *gerror = NULL;
1934         GIOStatus status;
1935         
1936         signal_user_data_t *ud = (signal_user_data_t*)data;
1937
1938         status = g_io_channel_read_line (source, &text, &length, NULL, &gerror);
1939         if (text != NULL)
1940         {
1941                 GdkWindow *window;
1942                 gint width, height;
1943                 gint x, y;
1944                 gboolean bottom = FALSE;
1945
1946                 textview = GTK_TEXT_VIEW(GHB_WIDGET (ud->builder, "activity_view"));
1947                 buffer = gtk_text_view_get_buffer (textview);
1948                 // I would like to auto-scroll the window when the scrollbar
1949                 // is at the bottom, 
1950                 // must determine whether the insert point is at
1951                 // the bottom of the window 
1952                 window = gtk_text_view_get_window(textview, GTK_TEXT_WINDOW_TEXT);
1953                 if (window != NULL)
1954                 {
1955                         gdk_drawable_get_size(GDK_DRAWABLE(window), &width, &height);
1956                         gtk_text_view_window_to_buffer_coords(textview, 
1957                                 GTK_TEXT_WINDOW_TEXT, width, height, &x, &y);
1958                         gtk_text_view_get_iter_at_location(textview, &iter, x, y);
1959                         if (gtk_text_iter_is_end(&iter))
1960                         {
1961                                 bottom = TRUE;
1962                         }
1963                 }
1964                 else
1965                 {
1966                         // If the window isn't available, assume bottom
1967                         bottom = TRUE;
1968                 }
1969                 gtk_text_buffer_get_end_iter(buffer, &iter);
1970                 gtk_text_buffer_insert(buffer, &iter, text, -1);
1971                 if (bottom)
1972                 {
1973                         gtk_text_buffer_get_end_iter(buffer, &iter);
1974                         mark = gtk_text_buffer_create_mark(buffer, NULL, &iter, FALSE);
1975                         gtk_text_view_scroll_mark_onscreen(textview, mark);
1976                         gtk_text_buffer_delete_mark(buffer, mark);
1977                 }
1978                 g_io_channel_write_chars (ud->activity_log, text, 
1979                                                                 length, &length, NULL);
1980                 g_io_channel_flush(ud->activity_log, NULL);
1981                 if (ud->job_activity_log)
1982                 {
1983                         g_io_channel_write_chars (ud->job_activity_log, text, 
1984                                                                         length, &length, NULL);
1985                         g_io_channel_flush(ud->job_activity_log, NULL);
1986                 }
1987                 g_free(text);
1988         }
1989         if (status != G_IO_STATUS_NORMAL)
1990         {
1991                 // This should never happen, but if it does I would get into an
1992                 // infinite loop.  Returning false removes this callback.
1993                 g_warning("Error while reading activity from pipe");
1994                 if (gerror != NULL)
1995                 {
1996                         g_warning("%s", gerror->message);
1997                         g_error_free (gerror);
1998                 }
1999                 return FALSE;
2000         }
2001         if (gerror != NULL)
2002                 g_error_free (gerror);
2003         return TRUE;
2004 }
2005
2006 static void
2007 set_visible(GtkWidget *widget, gboolean visible)
2008 {
2009         if (visible)
2010         {
2011                 gtk_widget_show_now(widget);
2012         }
2013         else
2014         {
2015                 gtk_widget_hide(widget);
2016         }
2017 }
2018
2019 void
2020 show_activity_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
2021 {
2022         GtkWidget *widget = GHB_WIDGET (ud->builder, "activity_window");
2023         set_visible(widget, gtk_toggle_tool_button_get_active(
2024                                                 GTK_TOGGLE_TOOL_BUTTON(xwidget)));
2025 }
2026
2027 void
2028 show_activity_menu_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
2029 {
2030         GtkWidget *widget = GHB_WIDGET (ud->builder, "activity_window");
2031         set_visible(widget, TRUE);
2032         widget = GHB_WIDGET (ud->builder, "show_activity");
2033         gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), TRUE);
2034 }
2035
2036 gboolean
2037 activity_window_delete_cb(GtkWidget *xwidget, GdkEvent *event, signal_user_data_t *ud)
2038 {
2039         set_visible(xwidget, FALSE);
2040         GtkWidget *widget = GHB_WIDGET (ud->builder, "show_activity");
2041         gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), FALSE);
2042         return TRUE;
2043 }
2044
2045 void
2046 ghb_log(gchar *log, ...)
2047 {
2048         va_list args;
2049         time_t _now;
2050     struct tm *now;
2051         gchar fmt[362];
2052
2053         _now = time(NULL);
2054         now = localtime( &_now );
2055         snprintf(fmt, 362, "[%02d:%02d:%02d] lingui: %s\n", 
2056                         now->tm_hour, now->tm_min, now->tm_sec, log);
2057         va_start(args, log);
2058         vfprintf(stderr, fmt, args);
2059         va_end(args);
2060 }
2061
2062 void
2063 about_activate_cb(GtkWidget *xwidget, signal_user_data_t *ud)
2064 {
2065         GtkWidget *widget = GHB_WIDGET (ud->builder, "hb_about");
2066         gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(widget), ghb_version());
2067         gtk_widget_show (widget);
2068 }
2069
2070 static void
2071 browse_url(const gchar *url)
2072 {
2073         gboolean result;
2074         char *argv[] = 
2075                 {"xdg-open",NULL,NULL,NULL};
2076         argv[1] = (gchar*)url;
2077         result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
2078                                 NULL, NULL, NULL);
2079         if (result) return;
2080
2081         argv[0] = "gnome-open";
2082         result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
2083                                 NULL, NULL, NULL);
2084         if (result) return;
2085
2086         argv[0] = "kfmclient";
2087         argv[1] = "exec";
2088         argv[2] = "http://trac.handbrake.fr/wiki/HandBrakeGuide";
2089         result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
2090                                 NULL, NULL, NULL);
2091         if (result) return;
2092
2093         argv[0] = "firefox";
2094         argv[1] = "http://trac.handbrake.fr/wiki/HandBrakeGuide";
2095         argv[2] = NULL;
2096         result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
2097                                 NULL, NULL, NULL);
2098 }
2099
2100 void
2101 guide_activate_cb(GtkWidget *xwidget, signal_user_data_t *ud)
2102 {
2103         browse_url("http://trac.handbrake.fr/wiki/HandBrakeGuide");
2104 }
2105
2106 void
2107 hb_about_response_cb(GtkWidget *widget, gint response, signal_user_data_t *ud)
2108 {
2109         gtk_widget_hide (widget);
2110 }
2111
2112 void
2113 show_queue_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
2114 {
2115         GtkWidget *widget = GHB_WIDGET (ud->builder, "queue_window");
2116         set_visible(widget, gtk_toggle_tool_button_get_active(
2117                                                 GTK_TOGGLE_TOOL_BUTTON(xwidget)));
2118 }
2119
2120 void
2121 show_queue_menu_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
2122 {
2123         GtkWidget *widget = GHB_WIDGET (ud->builder, "queue_window");
2124         set_visible(widget, TRUE);
2125         widget = GHB_WIDGET (ud->builder, "show_queue");
2126         gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), TRUE);
2127 }
2128
2129 gboolean
2130 queue_window_delete_cb(GtkWidget *xwidget, GdkEvent *event, signal_user_data_t *ud)
2131 {
2132         set_visible(xwidget, FALSE);
2133         GtkWidget *widget = GHB_WIDGET (ud->builder, "show_queue");
2134         gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), FALSE);
2135         return TRUE;
2136 }
2137
2138 void
2139 show_presets_toggled_cb(GtkWidget *action, signal_user_data_t *ud)
2140 {
2141         GtkWidget *widget;
2142         GtkWindow *hb_window;
2143         
2144         g_debug("show_presets_clicked_cb ()");
2145         widget = GHB_WIDGET (ud->builder, "presets_frame");
2146         ghb_widget_to_setting(ud->settings, action);
2147         if (ghb_settings_get_boolean(ud->settings, "show_presets"))
2148         {
2149                 gtk_widget_show_now(widget);
2150         }
2151         else
2152         {
2153                 gtk_widget_hide(widget);
2154                 hb_window = GTK_WINDOW(GHB_WIDGET (ud->builder, "hb_window"));
2155                 gtk_window_resize(hb_window, 16, 16);
2156         }
2157         ghb_pref_save(ud->settings, "show_presets");
2158 }
2159
2160 static void
2161 update_chapter_list(signal_user_data_t *ud)
2162 {
2163         GtkTreeView *treeview;
2164         GtkTreeIter iter;
2165         GtkListStore *store;
2166         gboolean done;
2167         GValue *chapters;
2168         gint titleindex, ii;
2169         gint count;
2170         
2171         g_debug("update_chapter_list ()");
2172         titleindex = ghb_settings_combo_int(ud->settings, "title");
2173         chapters = ghb_get_chapters(titleindex);
2174         count = ghb_array_len(chapters);
2175         if (chapters)
2176                 ghb_settings_set_value(ud->settings, "chapter_list", chapters);
2177         
2178         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "chapters_list"));
2179         store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview));
2180         ii = 0;
2181         if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter))
2182         {
2183                 do
2184                 {
2185
2186                         if (ii < count)
2187                         {
2188                                 gchar *chapter;
2189
2190                                 // Update row with settings data
2191                                 g_debug("Updating row");
2192                                 chapter = ghb_value_string(ghb_array_get_nth(chapters, ii));
2193                                 gtk_list_store_set(store, &iter, 
2194                                         0, ii+1,
2195                                         1, chapter,
2196                                         2, TRUE,
2197                                         -1);
2198                                 g_free(chapter);
2199                                 ii++;
2200                                 done = !gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
2201                         }
2202                         else
2203                         {
2204                                 // No more settings data, remove row
2205                                 g_debug("Removing row");
2206                                 done = !gtk_list_store_remove(store, &iter);
2207                         }
2208                 } while (!done);
2209         }
2210         while (ii < count)
2211         {
2212                 gchar *chapter;
2213
2214                 // Additional settings, add row
2215                 g_debug("Adding row");
2216                 chapter = ghb_value_string(ghb_array_get_nth(chapters, ii));
2217                 gtk_list_store_append(store, &iter);
2218                 gtk_list_store_set(store, &iter, 
2219                         0, ii+1,
2220                         1, chapter,
2221                         2, TRUE,
2222                         -1);
2223                 g_free(chapter);
2224                 ii++;
2225         }
2226 }
2227
2228 static gint chapter_edit_key = 0;
2229
2230 gboolean
2231 chapter_keypress_cb(
2232         GhbCellRendererText *cell,
2233         GdkEventKey *event,
2234         signal_user_data_t *ud)
2235 {
2236         chapter_edit_key = event->keyval;
2237         return FALSE;
2238 }
2239
2240 void
2241 chapter_edited_cb(
2242         GhbCellRendererText *cell, 
2243         gchar *path, 
2244         gchar *text, 
2245         signal_user_data_t *ud)
2246 {
2247         GtkTreePath *treepath;
2248         GtkListStore *store;
2249         GtkTreeView *treeview;
2250         GtkTreeIter iter;
2251         gint index;
2252         gint *pi;
2253         gint row;
2254         
2255         g_debug("chapter_edited_cb ()");
2256         g_debug("path (%s)", path);
2257         g_debug("text (%s)", text);
2258         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "chapters_list"));
2259         store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview));
2260         treepath = gtk_tree_path_new_from_string (path);
2261         pi = gtk_tree_path_get_indices(treepath);
2262         row = pi[0];
2263         gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, treepath);
2264         gtk_list_store_set(store, &iter, 
2265                 1, text,
2266                 2, TRUE,
2267                 -1);
2268         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 0, &index, -1);
2269
2270         const GValue *chapters;
2271         GValue *chapter;
2272
2273         chapters = ghb_settings_get_value(ud->settings, "chapter_list");
2274         chapter = ghb_array_get_nth(chapters, index-1);
2275         g_value_set_string(chapter, text);
2276         if ((chapter_edit_key == GDK_Return || chapter_edit_key == GDK_Down) &&
2277                 gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter))
2278         {
2279                 GtkTreeViewColumn *column;
2280
2281                 gtk_tree_path_next(treepath);
2282                 // When a cell has been edited, I want to advance to the
2283                 // next cell and start editing it automaitcally.
2284                 // Unfortunately, we may not be in a state here where
2285                 // editing is allowed.  This happens when the user selects
2286                 // a new cell with the mouse instead of just hitting enter.
2287                 // Some kind of Gtk quirk.  widget_editable==NULL assertion.
2288                 // Editing is enabled again once the selection event has been
2289                 // processed.  So I'm queueing up a callback to be called
2290                 // when things go idle.  There, I will advance to the next
2291                 // cell and initiate editing.
2292                 //
2293                 // Now, you might be asking why I don't catch the keypress
2294                 // event and determine what action to take based on that.
2295                 // The Gtk developers in their infinite wisdom have made the 
2296                 // actual GtkEdit widget being used a private member of
2297                 // GtkCellRendererText, so it can not be accessed to hang a
2298                 // signal handler off of.  And they also do not propagate the
2299                 // keypress signals in any other way.  So that information is lost.
2300                 //g_idle_add((GSourceFunc)next_cell, ud);
2301                 //
2302                 // Keeping the above comment for posterity.
2303                 // I got industrious and made my own CellTextRendererText that
2304                 // passes on the key-press-event. So now I have much better
2305                 // control of this.
2306                 column = gtk_tree_view_get_column(treeview, 1);
2307                 gtk_tree_view_set_cursor(treeview, treepath, column, TRUE);
2308         }
2309         else if (chapter_edit_key == GDK_Up && row > 0)
2310         {
2311                 GtkTreeViewColumn *column;
2312                 gtk_tree_path_prev(treepath);
2313                 column = gtk_tree_view_get_column(treeview, 1);
2314                 gtk_tree_view_set_cursor(treeview, treepath, column, TRUE);
2315         }
2316         gtk_tree_path_free (treepath);
2317 }
2318
2319 void
2320 chapter_list_selection_changed_cb(GtkTreeSelection *selection, signal_user_data_t *ud)
2321 {
2322         g_debug("chapter_list_selection_changed_cb ()");
2323         //chapter_selection_changed = TRUE;
2324 }
2325
2326 void
2327 debug_log_handler(const gchar *domain, GLogLevelFlags flags, const gchar *msg, gpointer data)
2328 {
2329         signal_user_data_t *ud = (signal_user_data_t*)data;
2330         
2331         if (ud->debug)
2332         {
2333                 printf("%s: %s\n", domain, msg);
2334         }
2335 }
2336
2337 void
2338 ghb_hbfd(signal_user_data_t *ud, gboolean hbfd)
2339 {
2340         GtkWidget *widget;
2341         g_debug("ghb_hbfd");
2342         widget = GHB_WIDGET(ud->builder, "queue_pause1");
2343         set_visible(widget, !hbfd);
2344         widget = GHB_WIDGET(ud->builder, "queue_add");
2345         set_visible(widget, !hbfd);
2346         widget = GHB_WIDGET(ud->builder, "show_queue");
2347         set_visible(widget, !hbfd);
2348         widget = GHB_WIDGET(ud->builder, "show_activity");
2349         set_visible(widget, !hbfd);
2350
2351         widget = GHB_WIDGET(ud->builder, "chapter_box");
2352         set_visible(widget, !hbfd);
2353         widget = GHB_WIDGET(ud->builder, "container_box");
2354         set_visible(widget, !hbfd);
2355         widget = GHB_WIDGET(ud->builder, "settings_box");
2356         set_visible(widget, !hbfd);
2357         widget = GHB_WIDGET(ud->builder, "presets_save");
2358         set_visible(widget, !hbfd);
2359         widget = GHB_WIDGET(ud->builder, "presets_remove");
2360         set_visible(widget, !hbfd);
2361         widget = GHB_WIDGET (ud->builder, "hb_window");
2362         gtk_window_resize(GTK_WINDOW(widget), 16, 16);
2363
2364 }
2365
2366 void
2367 hbfd_toggled_cb(GtkWidget *widget, signal_user_data_t *ud)
2368 {
2369         g_debug("hbfd_toggled_cb");
2370         ghb_widget_to_setting (ud->settings, widget);
2371         gboolean hbfd = ghb_settings_get_boolean(ud->settings, "hbfd");
2372         ghb_hbfd(ud, hbfd);
2373         ghb_pref_save(ud->settings, "hbfd");
2374 }
2375
2376 void
2377 pref_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
2378 {
2379         g_debug("pref_changed_cb");
2380         ghb_widget_to_setting (ud->settings, widget);
2381         ghb_check_dependency(ud, widget);
2382         const gchar *name = gtk_widget_get_name(widget);
2383         ghb_pref_save(ud->settings, name);
2384 }
2385
2386 void
2387 tweaks_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
2388 {
2389         g_debug("tweaks_changed_cb");
2390         ghb_widget_to_setting (ud->settings, widget);
2391         const gchar *name = gtk_widget_get_name(widget);
2392         ghb_pref_save(ud->settings, name);
2393
2394         gboolean tweaks = ghb_settings_get_boolean(ud->settings, "allow_tweaks");
2395         widget = GHB_WIDGET(ud->builder, "PictureDeinterlace");
2396         tweaks ? gtk_widget_hide(widget) : gtk_widget_show(widget);
2397         widget = GHB_WIDGET(ud->builder, "tweak_PictureDeinterlace");
2398         !tweaks ? gtk_widget_hide(widget) : gtk_widget_show(widget);
2399
2400         widget = GHB_WIDGET(ud->builder, "PictureDenoise");
2401         tweaks ? gtk_widget_hide(widget) : gtk_widget_show(widget);
2402         widget = GHB_WIDGET(ud->builder, "tweak_PictureDenoise");
2403         !tweaks ? gtk_widget_hide(widget) : gtk_widget_show(widget);
2404         if (tweaks)
2405         {
2406                 const GValue *value;
2407                 value = ghb_settings_get_value(ud->settings, "PictureDeinterlace");
2408                 ghb_ui_update(ud, "tweak_PictureDeinterlace", value);
2409                 value = ghb_settings_get_value(ud->settings, "PictureDenoise");
2410                 ghb_ui_update(ud, "tweak_PictureDenoise", value);
2411         }
2412         else
2413         {
2414                 const GValue *value;
2415                 value = ghb_settings_get_value(ud->settings, "tweak_PictureDeinterlace");
2416                 ghb_ui_update(ud, "PictureDeinterlace", value);
2417                 value = ghb_settings_get_value(ud->settings, "tweak_PictureDenoise");
2418                 ghb_ui_update(ud, "PictureDenoise", value);
2419         }
2420 }
2421
2422 void
2423 hbfd_feature_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
2424 {
2425         g_debug("hbfd_feature_changed_cb");
2426         ghb_widget_to_setting (ud->settings, widget);
2427         const gchar *name = gtk_widget_get_name(widget);
2428         ghb_pref_save(ud->settings, name);
2429
2430         gboolean hbfd = ghb_settings_get_boolean(ud->settings, "hbfd_feature");
2431         GtkAction *action;
2432         if (hbfd)
2433         {
2434                 const GValue *val;
2435                 val = ghb_settings_get_value(ud->settings, "hbfd");
2436                 ghb_ui_update(ud, "hbfd", val);
2437         }
2438         action = GHB_ACTION (ud->builder, "hbfd");
2439         gtk_action_set_visible(action, hbfd);
2440 }
2441
2442 void
2443 ghb_file_menu_add_dvd(signal_user_data_t *ud)
2444 {
2445         GList *link, *drives;
2446
2447         GtkActionGroup *agroup = GTK_ACTION_GROUP(
2448                 gtk_builder_get_object(ud->builder, "actiongroup1"));
2449         GtkUIManager *ui = GTK_UI_MANAGER(
2450                 gtk_builder_get_object(ud->builder, "uimanager1"));
2451         guint merge_id = gtk_ui_manager_new_merge_id(ui);
2452
2453         link = drives = dvd_device_list();
2454         while (link != NULL)
2455         {
2456                 gchar *name = (gchar*)link->data;
2457                 // Create action for this drive
2458                 GtkAction *action = gtk_action_new(name, name,
2459                         "Scan this DVD source", "gtk-cdrom");
2460                 // Add action to action group
2461                 gtk_action_group_add_action_with_accel(agroup, action, "");
2462                 // Add to ui manager
2463                 gtk_ui_manager_add_ui(ui, merge_id, 
2464                         "ui/menubar1/menuitem1/quit1", name, name,
2465                         GTK_UI_MANAGER_AUTO, TRUE);
2466                 // Connect signal to action (menu item)
2467                 g_signal_connect(action, "activate", 
2468                         (GCallback)dvd_source_activate_cb, ud);
2469                 g_free(name);
2470                 link = link->next;
2471         }
2472         g_list_free(drives);
2473
2474         // Add separator
2475         gtk_ui_manager_add_ui(ui, merge_id, 
2476                 "ui/menubar1/menuitem1/quit1", "", NULL,
2477                 GTK_UI_MANAGER_AUTO, TRUE);
2478 }
2479
2480 gboolean ghb_is_cd(GDrive *gd);
2481
2482 static GList*
2483 dvd_device_list()
2484 {
2485         GVolumeMonitor *gvm;
2486         GList *drives, *link;
2487         GList *dvd_devices = NULL;
2488         
2489         gvm = g_volume_monitor_get ();
2490         drives = g_volume_monitor_get_connected_drives (gvm);
2491         link = drives;
2492         while (link != NULL)
2493         {
2494                 GDrive *gd;
2495                 
2496                 gd = (GDrive*)link->data;
2497                 if (ghb_is_cd(gd))
2498                 {
2499                         gchar *device;
2500                         device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
2501                         dvd_devices = g_list_append(dvd_devices, (gpointer)device);
2502                 }
2503                 g_object_unref (gd);
2504                 link = link->next;
2505         }
2506         g_list_free(drives);
2507         return dvd_devices;
2508 }
2509
2510 static LibHalContext *hal_ctx;
2511
2512 gboolean
2513 ghb_is_cd(GDrive *gd)
2514 {
2515         gchar *device;
2516         LibHalDrive *halDrive;
2517         LibHalDriveType dtype;
2518
2519         device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
2520         halDrive = libhal_drive_from_device_file (hal_ctx, device);
2521         dtype = libhal_drive_get_type(halDrive);
2522         libhal_drive_free(halDrive);
2523         g_free(device);
2524         return (dtype == LIBHAL_DRIVE_TYPE_CDROM);
2525 }
2526
2527 void
2528 drive_changed_cb(GVolumeMonitor *gvm, GDrive *gd, signal_user_data_t *ud)
2529 {
2530         gchar *device;
2531         gint state = ghb_get_scan_state();
2532         static gboolean first_time = TRUE;
2533
2534         if (state != GHB_STATE_IDLE) return;
2535         if (ud->current_dvd_device == NULL) return;
2536         // A drive change event happens when the program initially starts
2537         // and I don't want to automatically scan at that time.
2538         if (first_time)
2539         {
2540                 first_time = FALSE;
2541                 return;
2542         }
2543         device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
2544         
2545         // DVD insertion detected.  Scan it.
2546         if (strcmp(device, ud->current_dvd_device) == 0)
2547         {
2548                 if (g_drive_has_media (gd))
2549                 {
2550                         GtkProgressBar *progress;
2551                         progress = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "progressbar"));
2552                         gtk_progress_bar_set_text (progress, "Scanning ...");
2553                         gtk_progress_bar_set_fraction (progress, 0);
2554                         update_source_label(ud, device);
2555                         ghb_hb_cleanup(TRUE);
2556                         prune_logs(ud);
2557                         gint preview_count;
2558                         preview_count = ghb_settings_get_int(ud->settings, "preview_count");
2559                         ghb_backend_scan(device, 0, preview_count);
2560                 }
2561                 else
2562                 {
2563                         ghb_hb_cleanup(TRUE);
2564                         prune_logs(ud);
2565                         ghb_backend_scan("/dev/null", 0, 1);
2566                 }
2567         }
2568         g_free(device);
2569 }
2570
2571
2572 static void
2573 dbus_init (void)
2574 {
2575         dbus_g_thread_init();
2576 }
2577
2578 #define GPM_DBUS_SERVICE                        "org.freedesktop.PowerManagement"
2579 #define GPM_DBUS_INHIBIT_PATH           "/org/freedesktop/PowerManagement/Inhibit"
2580 #define GPM_DBUS_INHIBIT_INTERFACE      "org.freedesktop.PowerManagement.Inhibit" 
2581 static gboolean gpm_inhibited = FALSE;
2582 static guint gpm_cookie = -1;
2583
2584 void
2585 ghb_inhibit_gpm()
2586 {
2587         DBusGConnection *conn;
2588         DBusGProxy      *proxy;
2589         GError *error = NULL;
2590         gboolean res;
2591         
2592
2593         if (gpm_inhibited)
2594         {
2595                 // Already inhibited
2596                 return;
2597         }
2598         g_debug("ghb_inhibit_gpm()");
2599         conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
2600         if (error != NULL)
2601         {
2602                 g_debug("DBUS cannot connect: %s", error->message);
2603                 g_error_free(error);
2604                 return;
2605         }
2606         proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SERVICE,
2607                                                         GPM_DBUS_INHIBIT_PATH, GPM_DBUS_INHIBIT_INTERFACE);
2608         if (proxy == NULL)
2609         {
2610                 g_debug("Could not get DBUS proxy: %s", GPM_DBUS_SERVICE);
2611                 dbus_g_connection_unref(conn);
2612                 return;
2613         }
2614         res = dbus_g_proxy_call(proxy, "Inhibit", &error,
2615                                                         G_TYPE_STRING, "ghb",
2616                                                         G_TYPE_STRING, "Encoding",
2617                                                         G_TYPE_INVALID,
2618                                                         G_TYPE_UINT, &gpm_cookie,
2619                                                         G_TYPE_INVALID);
2620         if (!res)
2621         {
2622                 g_warning("Inhibit method failed");
2623                 gpm_cookie = -1;
2624         }
2625         if (error != NULL)
2626         {
2627                 g_warning("Inhibit problem: %s", error->message);
2628                 g_error_free(error);
2629                 gpm_cookie = -1;
2630         }
2631         gpm_inhibited = TRUE;
2632         g_object_unref(G_OBJECT(proxy));
2633         dbus_g_connection_unref(conn);
2634 }
2635
2636 void
2637 ghb_uninhibit_gpm()
2638 {
2639         DBusGConnection *conn;
2640         DBusGProxy      *proxy;
2641         GError *error = NULL;
2642         gboolean res;
2643         
2644         g_debug("ghb_uninhibit_gpm() gpm_cookie %u", gpm_cookie);
2645
2646         if (!gpm_inhibited)
2647         {
2648                 // Not inhibited
2649                 return;
2650         }
2651         conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
2652         if (error != NULL)
2653         {
2654                 g_debug("DBUS cannot connect: %s", error->message);
2655                 g_error_free(error);
2656                 return;
2657         }
2658         proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SERVICE,
2659                                                         GPM_DBUS_INHIBIT_PATH, GPM_DBUS_INHIBIT_INTERFACE);
2660         if (proxy == NULL)
2661         {
2662                 g_debug("Could not get DBUS proxy: %s", GPM_DBUS_SERVICE);
2663                 dbus_g_connection_unref(conn);
2664                 return;
2665         }
2666         res = dbus_g_proxy_call(proxy, "UnInhibit", &error,
2667                                                         G_TYPE_UINT, gpm_cookie,
2668                                                         G_TYPE_INVALID,
2669                                                         G_TYPE_INVALID);
2670         if (!res)
2671         {
2672                 g_warning("UnInhibit method failed");
2673         }
2674         if (error != NULL)
2675         {
2676                 g_warning("UnInhibit problem: %s", error->message);
2677                 g_error_free(error);
2678         }
2679         gpm_inhibited = FALSE;
2680         dbus_g_connection_unref(conn);
2681         g_object_unref(G_OBJECT(proxy));
2682 }
2683
2684 void
2685 ghb_hal_init()
2686 {
2687         DBusGConnection *gconn;
2688         DBusConnection *conn;
2689         GError *gerror = NULL;
2690         DBusError error;
2691         char **devices;
2692         int nr;
2693
2694         dbus_init ();
2695
2696         if (!(hal_ctx = libhal_ctx_new ())) {
2697                 g_warning ("failed to create a HAL context!");
2698                 return;
2699         }
2700
2701         gconn = dbus_g_bus_get(DBUS_BUS_SYSTEM, &gerror);
2702         if (gerror != NULL)
2703         {
2704                 g_warning("DBUS cannot connect: %s", gerror->message);
2705                 g_error_free(gerror);
2706                 return;
2707         }
2708         conn = dbus_g_connection_get_connection(gconn);
2709         libhal_ctx_set_dbus_connection (hal_ctx, conn);
2710         dbus_error_init (&error);
2711         if (!libhal_ctx_init (hal_ctx, &error)) {
2712                 g_warning ("libhal_ctx_init failed: %s", error.message ? error.message : "unknown");
2713                 dbus_error_free (&error);
2714                 libhal_ctx_free (hal_ctx);
2715                 dbus_g_connection_unref(gconn);
2716                 return;
2717         }
2718
2719         /*
2720          * Do something to ping the HAL daemon - the above functions will
2721          * succeed even if hald is not running, so long as DBUS is.  But we
2722          * want to exit silently if hald is not running, to behave on
2723          * pre-2.6 systems.
2724          */
2725         if (!(devices = libhal_get_all_devices (hal_ctx, &nr, &error))) {
2726                 g_warning ("seems that HAL is not running: %s", error.message ? error.message : "unknown");
2727                 dbus_error_free (&error);
2728
2729                 libhal_ctx_shutdown (hal_ctx, NULL);
2730                 libhal_ctx_free (hal_ctx);
2731                 dbus_g_connection_unref(gconn);
2732                 return;
2733         }
2734
2735         libhal_free_string_array (devices);
2736         dbus_g_connection_unref(gconn);
2737 }
2738
2739 gboolean 
2740 tweak_setting_cb(
2741         GtkWidget *widget, 
2742         GdkEventButton *event, 
2743         signal_user_data_t *ud)
2744 {
2745         const gchar *name;
2746         gchar *tweak_name;
2747         gboolean ret = FALSE;
2748         gboolean allow_tweaks;
2749
2750         g_debug("press %d %d", event->type, event->button);
2751         allow_tweaks = ghb_settings_get_boolean(ud->settings, "allow_tweaks");
2752         if (allow_tweaks && event->type == GDK_BUTTON_PRESS && event->button == 3)
2753         { // Its a right mouse click
2754                 GtkWidget *dialog;
2755                 GtkEntry *entry;
2756                 GtkResponseType response;
2757                 gchar *tweak = NULL;
2758
2759                 name = gtk_widget_get_name(widget);
2760                 if (g_str_has_prefix(name, "tweak_"))
2761                 {
2762                         tweak_name = g_strdup(name);
2763                 }
2764                 else
2765                 {
2766                         tweak_name = g_strdup_printf("tweak_%s", name);
2767                 }
2768
2769                 tweak = ghb_settings_get_string (ud->settings, tweak_name);
2770                 dialog = GHB_WIDGET(ud->builder, "tweak_dialog");
2771                 gtk_window_set_title(GTK_WINDOW(dialog), tweak_name);
2772                 entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "tweak_setting"));
2773                 if (tweak)
2774                 {
2775                         gtk_entry_set_text(entry, tweak);
2776                         g_free(tweak);
2777                 }
2778                 response = gtk_dialog_run(GTK_DIALOG(dialog));
2779                 gtk_widget_hide(dialog);
2780                 if (response == GTK_RESPONSE_OK)
2781                 {
2782                         tweak = (gchar*)gtk_entry_get_text(entry);
2783                         if (ghb_validate_filter_string(tweak, -1))
2784                                 ghb_settings_set_string(ud->settings, tweak_name, tweak);
2785                         else
2786                         {
2787                                 gchar *message;
2788                                 message = g_strdup_printf(
2789                                                         "Invalid Settings:\n%s",
2790                                                         tweak);
2791                                 ghb_message_dialog(GTK_MESSAGE_ERROR, message, "Cancel", NULL);
2792                                 g_free(message);
2793                         }
2794                 }
2795                 g_free(tweak_name);
2796                 ret = TRUE;
2797         }
2798         return ret;
2799 }
2800
2801 gboolean 
2802 easter_egg_cb(
2803         GtkWidget *widget, 
2804         GdkEventButton *event, 
2805         signal_user_data_t *ud)
2806 {
2807         g_debug("press %d %d", event->type, event->button);
2808         if (event->type == GDK_3BUTTON_PRESS && event->button == 1)
2809         { // Its a tripple left mouse button click
2810                 GtkWidget *widget;
2811                 widget = GHB_WIDGET(ud->builder, "allow_tweaks");
2812                 gtk_widget_show(widget);
2813                 widget = GHB_WIDGET(ud->builder, "hbfd_feature");
2814                 gtk_widget_show(widget);
2815         }
2816         else if (event->type == GDK_BUTTON_PRESS && event->button == 1)
2817         {
2818                 GtkWidget *widget;
2819                 widget = GHB_WIDGET(ud->builder, "allow_tweaks");
2820                 gtk_widget_hide(widget);
2821                 widget = GHB_WIDGET(ud->builder, "hbfd_feature");
2822                 gtk_widget_hide(widget);
2823         }
2824         return FALSE;
2825 }
2826
2827 gchar*
2828 format_deblock_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud)
2829 {
2830         if (val < 5.0)
2831         {
2832                 return g_strdup_printf("Off");
2833         }
2834         else
2835         {
2836                 return g_strdup_printf("%d", (gint)val);
2837         }
2838 }
2839
2840 gchar*
2841 format_vquality_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud)
2842 {
2843         if (ghb_settings_get_boolean(ud->settings, "directqp"))
2844         {
2845                 gint vcodec = ghb_settings_combo_int(ud->settings, "VideoEncoder");
2846                 // Only x264 and ffmpeg currently support direct qp/crf entry
2847                 if (vcodec != HB_VCODEC_X264 && vcodec != HB_VCODEC_FFMPEG)
2848                 {
2849                         val *= 100;
2850                         return g_strdup_printf("%.1f", val);
2851                 }
2852                 return g_strdup_printf("%d", (gint)val);
2853         }
2854         else
2855         {
2856                 val *= 100;
2857                 return g_strdup_printf("%.1f", val);
2858         }
2859 }
2860
2861 static void
2862 html_link_cb(GtkHTML *html, const gchar *url, signal_user_data_t *ud)
2863 {
2864         browse_url(url);
2865 }
2866
2867 static gpointer check_stable_update(signal_user_data_t *ud);
2868 static gboolean stable_update_lock = FALSE;
2869
2870 static void
2871 process_appcast(signal_user_data_t *ud)
2872 {
2873         gchar *description = NULL, *build = NULL, *version = NULL, *msg;
2874         GtkWidget *html, *window, *dialog, *label;
2875         gint    response, ibuild = 0, skip;
2876
2877         if (ud->appcast == NULL || ud->appcast_len < 15 || 
2878                 strncmp(&(ud->appcast[9]), "200 OK", 6))
2879         {
2880                 if (!stable_update_lock && HB_BUILD % 100)
2881                         g_idle_add((GSourceFunc)check_stable_update, ud);
2882                 goto done;
2883         }
2884         ghb_appcast_parse(ud->appcast, &description, &build, &version);
2885         if (build)
2886                 ibuild = g_strtod(build, NULL);
2887         skip = ghb_settings_get_int(ud->settings, "update_skip_version");
2888         if (description == NULL || build == NULL || version == NULL 
2889                 || ibuild <= HB_BUILD || skip == ibuild)
2890         {
2891                 if (!stable_update_lock && HB_BUILD % 100)
2892                         g_thread_create((GThreadFunc)check_stable_update, ud, FALSE, NULL);
2893                 goto done;
2894         }
2895         msg = g_strdup_printf("HandBrake %s/%s is now available (you have %s/%d).",
2896                         version, build, HB_VERSION, HB_BUILD);
2897         label = GHB_WIDGET(ud->builder, "update_message");
2898         gtk_label_set_text(GTK_LABEL(label), msg);
2899         html = gtk_html_new_from_string(description, -1);
2900         g_signal_connect(html, "link_clicked", G_CALLBACK(html_link_cb), ud);
2901         window = GHB_WIDGET(ud->builder, "update_scroll");
2902         gtk_container_add(GTK_CONTAINER(window), html);
2903         // Show it
2904         dialog = GHB_WIDGET(ud->builder, "update_dialog");
2905         gtk_widget_set_size_request(html, 420, 240);
2906         gtk_widget_show(html);
2907         response = gtk_dialog_run(GTK_DIALOG(dialog));
2908         gtk_widget_hide(dialog);
2909         gtk_widget_destroy(html);
2910         if (response == GTK_RESPONSE_OK)
2911         {
2912                 // Skip
2913                 ghb_settings_set_int(ud->settings, "update_skip_version", ibuild);
2914                 ghb_pref_save(ud->settings, "update_skip_version");
2915         }
2916         g_free(msg);
2917
2918 done:
2919         if (description) g_free(description);
2920         if (build) g_free(build);
2921         if (version) g_free(version);
2922         g_free(ud->appcast);
2923         ud->appcast_len = 0;
2924         ud->appcast = NULL;
2925 }
2926
2927 void
2928 ghb_net_close(GIOChannel *ioc)
2929 {
2930         gint fd;
2931
2932         g_debug("ghb_net_close");
2933         if (ioc == NULL) return;
2934         fd = g_io_channel_unix_get_fd(ioc);
2935         close(fd);
2936         g_io_channel_unref(ioc);
2937 }
2938
2939 gboolean
2940 ghb_net_recv_cb(GIOChannel *ioc, GIOCondition cond, gpointer data)
2941 {
2942         gchar buf[2048];
2943         gsize len;
2944         GError *gerror = NULL;
2945         GIOStatus status;
2946         
2947         g_debug("ghb_net_recv_cb");
2948         signal_user_data_t *ud = (signal_user_data_t*)data;
2949
2950         status = g_io_channel_read_chars (ioc, buf, 2048, &len, &gerror);
2951         if ((status == G_IO_STATUS_NORMAL || status == G_IO_STATUS_EOF) &&
2952                 len > 0)
2953         {
2954                 gint new_len = ud->appcast_len + len;
2955                 ud->appcast = g_realloc(ud->appcast, new_len + 1);
2956                 memcpy(&(ud->appcast[ud->appcast_len]), buf, len);
2957                 ud->appcast_len = new_len;
2958         }
2959         if (status == G_IO_STATUS_EOF)
2960         {
2961                 ud->appcast[ud->appcast_len] = 0;
2962                 ghb_net_close(ioc);
2963                 process_appcast(ud);
2964                 return FALSE;
2965         }
2966         return TRUE;
2967 }
2968
2969 GIOChannel*
2970 ghb_net_open(signal_user_data_t *ud, gchar *address, gint port)
2971 {
2972         GIOChannel *ioc;
2973         gint fd;
2974
2975         struct sockaddr_in   sock;
2976         struct hostent     * host;
2977
2978         g_debug("ghb_net_open");
2979         if( !( host = gethostbyname( address ) ) )
2980         {
2981                 g_warning( "gethostbyname failed (%s)", address );
2982                 return NULL;
2983         }
2984
2985         memset( &sock, 0, sizeof( struct sockaddr_in ) );
2986         sock.sin_family = host->h_addrtype;
2987         sock.sin_port   = htons( port );
2988         memcpy( &sock.sin_addr, host->h_addr, host->h_length );
2989
2990         fd = socket(host->h_addrtype, SOCK_STREAM, 0);
2991         if( fd < 0 )
2992         {
2993                 g_debug( "socket failed" );
2994                 return NULL;
2995         }
2996
2997         if(connect(fd, (struct sockaddr*)&sock, sizeof(struct sockaddr_in )) < 0 )
2998         {
2999                 g_debug( "connect failed" );
3000                 return NULL;
3001         }
3002         ioc = g_io_channel_unix_new(fd);
3003         g_io_channel_set_encoding (ioc, NULL, NULL);
3004         g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
3005         g_io_add_watch (ioc, G_IO_IN, ghb_net_recv_cb, (gpointer)ud );
3006
3007         return ioc;
3008 }
3009
3010 gpointer
3011 ghb_check_update(signal_user_data_t *ud)
3012 {
3013         gchar *query;
3014         gsize len;
3015         GIOChannel *ioc;
3016         GError *gerror = NULL;
3017
3018         g_debug("ghb_check_update");
3019         if (HB_BUILD % 100)
3020         {
3021         query = 
3022                 "GET /appcast_unstable.xml HTTP/1.0\r\nHost: handbrake.fr\r\n\r\n";
3023         }
3024         else
3025         {
3026                 stable_update_lock = TRUE;
3027         query = "GET /appcast.xml HTTP/1.0\r\nHost: handbrake.fr\r\n\r\n";
3028         }
3029         ioc = ghb_net_open(ud, "handbrake.fr", 80);
3030         if (ioc == NULL)
3031                 return NULL;
3032
3033         g_io_channel_write_chars(ioc, query, strlen(query), &len, &gerror);
3034         g_io_channel_flush(ioc, &gerror);
3035         // This function is initiated by g_idle_add.  Must return false
3036         // so that it is not called again
3037         return NULL;
3038 }
3039
3040 static gpointer
3041 check_stable_update(signal_user_data_t *ud)
3042 {
3043         gchar *query;
3044         gsize len;
3045         GIOChannel *ioc;
3046         GError *gerror = NULL;
3047
3048         g_debug("check_stable_update");
3049         stable_update_lock = TRUE;
3050         query = "GET /appcast.xml HTTP/1.0\r\nHost: handbrake.fr\r\n\r\n";
3051         ioc = ghb_net_open(ud, "handbrake.fr", 80);
3052         if (ioc == NULL)
3053                 return NULL;
3054
3055         g_io_channel_write_chars(ioc, query, strlen(query), &len, &gerror);
3056         g_io_channel_flush(ioc, &gerror);
3057         // This function is initiated by g_idle_add.  Must return false
3058         // so that it is not called again
3059         return NULL;
3060 }
3061