OSDN Git Service

LinGui: add a hidden feature to allow tweaking some settings. currently
[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 <libhal-storage.h>
23 #include <gtk/gtk.h>
24 #include <glib/gstdio.h>
25 #include <gio/gio.h>
26
27 #include "callbacks.h"
28 #include "settings.h"
29 #include "hb-backend.h"
30 #include "ghb-dvd.h"
31
32 static void update_chapter_list(signal_user_data_t *ud);
33 static void clear_audio_list(signal_user_data_t *ud);
34 static GList* dvd_device_list();
35 static gboolean cancel_encode();
36 static void audio_list_refresh_selected(signal_user_data_t *ud);
37 static GHashTable* get_selected_asettings(signal_user_data_t *ud);
38
39 // This is a dependency map used for greying widgets
40 // that are dependent on the state of another widget.
41 // The enable_value comes from the values that are
42 // obtained from ghb_widget_value().  For combo boxes
43 // you will have to look further to combo box options
44 // maps in hb-backend.c
45 typedef struct
46 {
47         const gchar *widget_name;
48         const gchar *dep_name;
49         const gchar *enable_value;
50         const gboolean disable_if_equal;
51 } dependency_t;
52
53 static dependency_t dep_map[] =
54 {
55         {"title", "queue_add", "none", TRUE},
56         {"title", "queue_add_menu", "none", TRUE},
57         {"title", "preview_button", "none", TRUE},
58         {"title", "show_preview_menu", "none", TRUE},
59         {"title", "preview_frame", "none", TRUE},
60         {"title", "picture_label", "none", TRUE},
61         {"title", "picture_tab", "none", TRUE},
62         {"title", "chapters_label", "none", TRUE},
63         {"title", "chapters_tab", "none", TRUE},
64         {"title", "title", "none", TRUE},
65         {"title", "start_chapter", "none", TRUE},
66         {"title", "end_chapter", "none", TRUE},
67         {"vquality_type_bitrate", "video_bitrate", "enable", FALSE},
68         {"vquality_type_target", "video_target_size", "enable", FALSE},
69         {"vquality_type_constant", "video_quality", "enable", FALSE},
70         {"vquality_type_constant", "constant_rate_factor", "enable", FALSE},
71         {"vquality_type_constant", "two_pass", "enable", TRUE},
72         {"vquality_type_constant", "turbo", "enable", TRUE},
73         {"two_pass", "turbo", "enable", FALSE},
74         {"container", "large_mp4", "mp4|m4v", FALSE},
75         {"container", "http_optimize_mp4", "mp4|m4v", FALSE},
76         {"container", "ipod_file", "mp4|m4v", FALSE},
77         {"container", "variable_frame_rate", "avi", TRUE},
78         {"variable_frame_rate", "framerate", "enable", TRUE},
79         {"variable_frame_rate", "detelecine", "enable", TRUE},
80         {"decomb", "deinterlace", "enable", TRUE},
81         {"autocrop", "crop_top", "disable", FALSE},
82         {"autocrop", "crop_bottom", "disable", FALSE},
83         {"autocrop", "crop_left", "disable", FALSE},
84         {"autocrop", "crop_right", "disable", FALSE},
85         {"autoscale", "scale_width", "disable", FALSE},
86         {"autoscale", "scale_height", "disable", FALSE},
87         {"anamorphic", "keep_aspect", "disable", FALSE},
88         {"anamorphic", "scale_height", "disable", FALSE},
89         {"keep_aspect", "scale_height", "disable", FALSE},
90         {"video_codec", "x264_tab", "x264", FALSE},
91         {"video_codec", "x264_tab_label", "x264", FALSE},
92         {"video_codec", "ipod_file", "x264", FALSE},
93         {"audio_codec", "audio_bitrate", "ac3", TRUE},
94         {"audio_codec", "audio_rate", "ac3", TRUE},
95         {"audio_codec", "audio_mix", "ac3", TRUE},
96         {"audio_codec", "audio_drc", "ac3", TRUE},
97         {"x264_bframes", "x264_weighted_bframes", "0", TRUE},
98         {"x264_bframes", "x264_brdo", "0", TRUE},
99         {"x264_bframes", "x264_bime", "0", TRUE},
100         {"x264_bframes", "x264_bpyramid", "<2", TRUE},
101         {"x264_bframes", "x264_direct", "0", TRUE},
102         {"x264_refs", "x264_mixed_refs", "<2", TRUE},
103         {"x264_cabac", "x264_trellis", "enable", FALSE},
104         {"x264_subme", "x264_brdo", "<6", TRUE},
105         {"x264_analyse", "x264_direct", "none", TRUE},
106         {"x264_me", "x264_merange", "umh|esa", FALSE},
107         {"chapter_markers", "chapters_list", "enable", FALSE},
108 };
109
110 static gboolean
111 dep_check(signal_user_data_t *ud, const gchar *name)
112 {
113         GtkWidget *widget;
114         GObject *dep_object;
115         int ii;
116         int count = sizeof(dep_map) / sizeof(dependency_t);
117         gboolean result = TRUE;
118         
119         g_debug("dep_check () %s\n", name);
120         for (ii = 0; ii < count; ii++)
121         {
122                 if (strcmp(dep_map[ii].dep_name, name) == 0)
123                 {
124                         widget = GHB_WIDGET(ud->builder, dep_map[ii].widget_name);
125                         dep_object = gtk_builder_get_object(ud->builder, dep_map[ii].dep_name);
126                         if (dep_object == NULL)
127                         {
128                                 g_message("Failed to find widget\n");
129                         }
130                         else
131                         {
132                                 gchar *value;
133                                 gint jj = 0;
134                                 gchar **values = g_strsplit(dep_map[ii].enable_value, "|", 10);
135                                 gboolean sensitive = FALSE;
136
137                                 if (widget)
138                                         value = ghb_widget_short_opt(widget);
139                                 else
140                                         value = g_strdup( ghb_settings_get_short_opt(
141                                                 ud->settings, dep_map[ii].widget_name));
142                                 while (values && values[jj])
143                                 {
144                                         if (values[jj][0] == '>')
145                                         {
146                                                 gdouble dbl = g_strtod (&values[jj][1], NULL);
147                                                 gdouble dvalue = ghb_widget_dbl (widget);
148                                                 if (dvalue > dbl)
149                                                 {
150                                                         sensitive = TRUE;
151                                                         break;
152                                                 }
153                                         }
154                                         else if (values[jj][0] == '<')
155                                         {
156                                                 gdouble dbl = g_strtod (&values[jj][1], NULL);
157                                                 gdouble dvalue = ghb_widget_dbl (widget);
158                                                 if (dvalue < dbl)
159                                                 {
160                                                         sensitive = TRUE;
161                                                         break;
162                                                 }
163                                         }
164                                         if (strcmp(values[jj], value) == 0)
165                                         {
166                                                 sensitive = TRUE;
167                                                 break;
168                                         }
169                                         jj++;
170                                 }
171                                 sensitive = dep_map[ii].disable_if_equal ^ sensitive;
172                                 if (!sensitive) result = FALSE;
173                                 g_strfreev (values);
174                                 g_free(value);
175                         }
176                 }
177         }
178         return result;
179 }
180
181 static void
182 check_depencency(signal_user_data_t *ud, GtkWidget *widget)
183 {
184         GObject *dep_object;
185         const gchar *name;
186         int ii;
187         int count = sizeof(dep_map) / sizeof(dependency_t);
188
189         if (ghb_widget_index(widget) < 0) return;
190         name = gtk_widget_get_name(widget);
191         g_debug("check_depencency () %s\n", name);
192         for (ii = 0; ii < count; ii++)
193         {
194                 if (strcmp(dep_map[ii].widget_name, name) == 0)
195                 {
196                         gboolean sensitive;
197
198                         dep_object = gtk_builder_get_object (ud->builder, dep_map[ii].dep_name);
199                         if (dep_object == NULL)
200                         {
201                                 g_message("Failed to find dependent widget %s\n", dep_map[ii].dep_name);
202                                 continue;
203                         }
204                         sensitive = dep_check(ud, dep_map[ii].dep_name);
205                         if (GTK_IS_ACTION(dep_object))
206                                 gtk_action_set_sensitive(GTK_ACTION(dep_object), sensitive);
207                         else
208                                 gtk_widget_set_sensitive(GTK_WIDGET(dep_object), sensitive);
209                 }
210         }
211 }
212
213 void
214 ghb_check_all_depencencies(signal_user_data_t *ud)
215 {
216         GObject *dep_object;
217         int ii;
218         int count = sizeof(dep_map) / sizeof(dependency_t);
219
220         g_debug("ghb_check_all_depencencies ()\n");
221         for (ii = 0; ii < count; ii++)
222         {
223                 gboolean sensitive;
224                 dep_object = gtk_builder_get_object (ud->builder, dep_map[ii].dep_name);
225                 if (dep_object == NULL)
226                 {
227                         g_message("Failed to find dependent widget %s\n", dep_map[ii].dep_name);
228                         continue;
229                 }
230                 sensitive = dep_check(ud, dep_map[ii].dep_name);
231                 if (GTK_IS_ACTION(dep_object))
232                         gtk_action_set_sensitive(GTK_ACTION(dep_object), sensitive);
233                 else
234                         gtk_widget_set_sensitive(GTK_WIDGET(dep_object), sensitive);
235         }
236 }
237
238 static void
239 clear_presets_selection(signal_user_data_t *ud)
240 {
241         GtkTreeView *treeview;
242         GtkTreeSelection *selection;
243         
244         if (ud->dont_clear_presets) return;
245         g_debug("clear_presets_selection()\n");
246         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
247         selection = gtk_tree_view_get_selection (treeview);
248         gtk_tree_selection_unselect_all (selection);
249 }
250
251 static gchar*
252 expand_tilde(const gchar *path)
253 {
254         const gchar *user_home;
255         gchar *home;
256         const gchar *suffix;
257         gchar *expanded_path = NULL;
258         
259         g_debug("expand_tilde ()\n");
260         if (path[0] == '~')
261         {
262                 user_home = g_get_home_dir();
263                 home = NULL; // squash warning about home uninitialized
264                 if (path[1] == 0)
265                 {
266                         home = g_strdup(user_home);
267                         suffix = "";
268                 }
269                 else if (path[1] == '/')
270                 {
271                         home = g_strdup(user_home);
272                         suffix = &path[2];
273                 }
274                 else
275                 {
276                         home = g_path_get_dirname(user_home);
277                         suffix = &path[1];
278                 }
279                 expanded_path = g_strdup_printf("%s/%s", home, suffix);
280                 g_free(home);
281         }
282         return expanded_path;
283 }
284
285 void
286 on_quit1_activate(GtkMenuItem *quit, signal_user_data_t *ud)
287 {
288         gint state = ghb_get_state();
289         g_debug("on_quit1_activate ()\n");
290     if (state & GHB_STATE_WORKING)
291     {
292         if (cancel_encode("Closing HandBrake will terminate encoding.\n"))
293         {
294                         ghb_hb_cleanup(FALSE);
295                 gtk_main_quit();
296             return;
297         }
298         return;
299     }
300         ghb_hb_cleanup(FALSE);
301         gtk_main_quit();
302 }
303
304 static void
305 set_destination(signal_user_data_t *ud)
306 {
307         if (ghb_settings_get_bool(ud->settings, "use_source_name"))
308         {
309                 const gchar *vol_name, *filename, *extension;
310                 gchar *dir, *new_name;
311                 
312                 filename = ghb_settings_get_string(ud->settings, "destination");
313                 extension = ghb_settings_get_string(ud->settings, "container");
314                 dir = g_path_get_dirname (filename);
315                 vol_name = ghb_settings_get_string(ud->settings, "volume_label");
316                 g_debug("volume_label (%s)\n", vol_name);
317                 if (vol_name == NULL)
318                 {
319                         vol_name = "new_video";
320                 }
321                 new_name = g_strdup_printf("%s/%s.%s", dir, vol_name, extension);
322                 ghb_ui_update(ud, "destination", new_name);
323                 g_free(dir);
324                 g_free(new_name);
325         }
326 }
327
328 gboolean
329 uppers_and_unders(const gchar *str)
330 {
331         if (str == NULL) return FALSE;
332         while (*str)
333         {
334                 if (*str == ' ')
335                 {
336                         return FALSE;
337                 }
338                 if (*str >= 'a' && *str <= 'z')
339                 {
340                         return FALSE;
341                 }
342                 str++;
343         }
344         return TRUE;
345 }
346
347 enum
348 {
349         CAMEL_FIRST_UPPER,
350         CAMEL_OTHER
351 };
352
353 void
354 camel_convert(gchar *str)
355 {
356         gint state = CAMEL_OTHER;
357         
358         if (str == NULL) return;
359         while (*str)
360         {
361                 if (*str == '_') *str = ' ';
362                 switch (state)
363                 {
364                         case CAMEL_OTHER:
365                         {
366                                 if (*str >= 'A' && *str <= 'Z')
367                                         state = CAMEL_FIRST_UPPER;
368                                 else
369                                         state = CAMEL_OTHER;
370                                 
371                         } break;
372                         case CAMEL_FIRST_UPPER:
373                         {
374                                 if (*str >= 'A' && *str <= 'Z')
375                                         *str = *str - 'A' + 'a';
376                                 else
377                                         state = CAMEL_OTHER;
378                         } break;
379                 }
380                 str++;
381         }
382 }
383
384 static gboolean
385 update_source_label(signal_user_data_t *ud, const gchar *source)
386 {
387         gchar *label = NULL;
388         gint len;
389         gchar **path;
390         gchar *filename = g_strdup(source);
391         
392         len = strlen(filename);
393         if (filename[len-1] == '/') filename[len-1] = 0;
394         if (g_file_test(filename, G_FILE_TEST_IS_DIR))
395         {
396                 path = g_strsplit(filename, "/", -1);
397                 len = g_strv_length (path);
398                 if ((len > 1) && (strcmp("VIDEO_TS", path[len-1]) == 0))
399                 {
400                         label = g_strdup(path[len-2]);
401                 }
402                 else
403                 {
404                         label = g_strdup(path[len-1]);
405                 }
406                 g_strfreev (path);
407         }
408         else
409         {
410                 // Is regular file or block dev.
411                 // Check to see if it is a dvd image
412                 label = ghb_dvd_volname (filename);
413                 if (label == NULL)
414                 {
415                         path = g_strsplit(filename, "/", -1);
416                         len = g_strv_length (path);
417                         // Just use the last combonent of the path
418                         label = g_strdup(path[len-1]);
419                         g_strfreev (path);
420                 }
421                 else
422                 {
423                         if (uppers_and_unders(label))
424                         {
425                                 camel_convert(label);
426                         }
427                 }
428         }
429         g_free(filename);
430         GtkWidget *widget = GHB_WIDGET (ud->builder, "source_title");
431         if (label != NULL)
432         {
433                 gtk_label_set_text (GTK_LABEL(widget), label);
434                 ghb_settings_set_string(ud->settings, "volume_label", label);
435                 g_free(label);
436                 set_destination(ud);
437         }
438         else
439         {
440                 label = "No Title Found";
441                 gtk_label_set_text (GTK_LABEL(widget), label);
442                 ghb_settings_set_string(ud->settings, "volume_label", label);
443                 return FALSE;
444         }
445         return TRUE;
446 }
447
448 static GtkWidget *dvd_device_combo = NULL;
449
450 void
451 chooser_file_selected_cb(GtkFileChooser *dialog, GtkComboBox *combo)
452 {
453         const gchar *name = gtk_file_chooser_get_filename (dialog);
454         GtkTreeModel *store;
455         GtkTreeIter iter;
456         const gchar *device;
457         gboolean foundit = FALSE;
458         
459         if (name == NULL) return;
460         store = gtk_combo_box_get_model(combo);
461         if (gtk_tree_model_get_iter_first(store, &iter))
462         {
463                 do
464                 {
465                         gtk_tree_model_get(store, &iter, 0, &device, -1);
466                         if (strcmp(name, device) == 0)
467                         {
468                                 foundit = TRUE;
469                                 break;
470                         }
471                 } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter));
472         }
473         if (foundit)
474                 gtk_combo_box_set_active_iter (combo, &iter);
475         else
476                 gtk_combo_box_set_active (combo, 0);
477 }
478
479 void
480 dvd_device_changed_cb(GtkComboBox *combo, GtkWidget *dialog)
481 {
482         gint ii = gtk_combo_box_get_active (combo);
483         if (ii != 0)
484         {
485                 const gchar *device = gtk_combo_box_get_active_text (combo);
486                 const gchar *name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog));
487                 if (name == NULL || strcmp(name, device) != 0)
488                         gtk_file_chooser_select_filename (GTK_FILE_CHOOSER(dialog), device);
489         }
490 }
491
492
493 void
494 source_type_changed_cb(GtkToggleButton *toggle, GtkFileChooser *chooser)
495 {
496         gchar *filename;
497         
498         g_debug("source_type_changed_cb ()\n");
499         if (gtk_toggle_button_get_active (toggle))
500         {
501                 filename = gtk_file_chooser_get_filename (chooser);
502                 gtk_file_chooser_set_action (chooser, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
503                 if (filename != NULL)
504                 {
505                         gtk_file_chooser_set_filename(chooser, filename);
506                         g_free(filename);
507                 }
508                 gtk_widget_set_sensitive (dvd_device_combo, FALSE);
509                 gtk_combo_box_set_active (GTK_COMBO_BOX(dvd_device_combo), 0);
510         }
511         else
512         {
513                 filename = gtk_file_chooser_get_filename (chooser);
514                 gtk_file_chooser_set_action (chooser, GTK_FILE_CHOOSER_ACTION_OPEN);
515                 if (filename != NULL)
516                 {
517                         gtk_file_chooser_set_filename(chooser, filename);
518                         g_free(filename);
519                 }
520                 gtk_widget_set_sensitive (dvd_device_combo, TRUE);
521         }
522 }
523
524 static GtkWidget*
525 source_dialog_extra_widgets(GtkWidget *dialog, gboolean checkbutton_active)
526 {
527         GtkBox *vbox;
528         GtkWidget *checkbutton;
529         
530         vbox = GTK_BOX(gtk_vbox_new (FALSE, 2));
531         checkbutton = gtk_check_button_new_with_label ("Open VIDEO_TS folder");
532         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(checkbutton), checkbutton_active);
533         gtk_box_pack_start (vbox, checkbutton, FALSE, FALSE, 1);
534         gtk_widget_show(checkbutton);
535
536         GtkWidget *combo;
537         GtkBox *hbox;
538         GList *drives, *link;
539         GtkWidget *label, *blank;
540
541         hbox = GTK_BOX(gtk_hbox_new (FALSE, 2));
542         combo = gtk_combo_box_new_text();
543         label = gtk_label_new("Detected DVD devices:");
544         blank = gtk_label_new("");
545         link = drives = dvd_device_list();
546         gtk_combo_box_append_text (GTK_COMBO_BOX(combo), "Not Selected");
547         while (link != NULL)
548         {
549                 gchar *name = (gchar*)link->data;
550                 gtk_combo_box_append_text (GTK_COMBO_BOX(combo), name);
551                 g_free(name);
552                 link = link->next;
553         }
554         g_list_free(drives);
555         gtk_combo_box_set_active (GTK_COMBO_BOX(combo), 0);
556         gtk_box_pack_start (vbox, GTK_WIDGET(hbox), FALSE, FALSE, 1);
557         gtk_widget_show(GTK_WIDGET(hbox));
558         gtk_box_pack_start (hbox, label, FALSE, FALSE, 1);
559         gtk_widget_show(label);
560         gtk_box_pack_start (hbox, combo, FALSE, FALSE, 2);
561         gtk_widget_show(combo);
562         gtk_box_pack_start (hbox, blank, TRUE, TRUE, 1);
563         gtk_widget_show(blank);
564  
565         // Ugly hackish global alert
566         dvd_device_combo = combo;
567         g_signal_connect(combo, "changed", (GCallback)dvd_device_changed_cb, dialog);
568         g_signal_connect(dialog, "selection-changed", (GCallback)chooser_file_selected_cb, combo);
569
570         g_signal_connect(checkbutton, "toggled", (GCallback)source_type_changed_cb, dialog);
571         return GTK_WIDGET(vbox);
572 }
573
574 static void
575 do_scan(signal_user_data_t *ud, const gchar *filename)
576 {
577         if (filename != NULL)
578         {
579                 ghb_settings_set_string(ud->settings, "source", filename);
580                 if (update_source_label(ud, filename))
581                 {
582                         GtkProgressBar *progress;
583                         progress = GTK_PROGRESS_BAR(GHB_WIDGET(ud->builder, "progressbar"));
584                         const gchar *path;
585                         path = ghb_settings_get_string( ud->settings, "source");
586                         gtk_progress_bar_set_fraction (progress, 0);
587                         gtk_progress_bar_set_text (progress, "Scanning ...");
588                         ghb_hb_cleanup(TRUE);
589                         ghb_backend_scan (path, 0);
590                 }
591                 else
592                 {
593                         // TODO: error dialog
594                 }
595         }
596 }
597
598 void
599 source_button_clicked_cb(GtkButton *button, signal_user_data_t *ud)
600 {
601         GtkWidget *dialog;
602         GtkWidget *widget;
603         const gchar *sourcename;
604         gint    response;
605         GtkFileChooserAction action;
606         gboolean checkbutton_active;
607
608         g_debug("source_browse_clicked_cb ()\n");
609         sourcename = ghb_settings_get_string(ud->settings, "source");
610         checkbutton_active = FALSE;
611         if (g_file_test(sourcename, G_FILE_TEST_IS_DIR))
612         {
613                 action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
614                 checkbutton_active = TRUE;
615         }
616         else
617         {
618                 action = GTK_FILE_CHOOSER_ACTION_OPEN;
619         }
620         dialog = gtk_file_chooser_dialog_new ("Select Source",
621                                                                 NULL,
622                                                                 action,
623                                                                 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
624                                                                 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
625                                                                 NULL);
626         widget = source_dialog_extra_widgets(dialog, checkbutton_active);
627         gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER(dialog), widget);
628         gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), sourcename);
629         response = gtk_dialog_run(GTK_DIALOG (dialog));
630         gtk_widget_hide(dialog);
631         if (response == GTK_RESPONSE_ACCEPT)
632         {
633                 char *filename;
634
635                 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
636                 if (filename != NULL)
637                 {
638                         do_scan(ud, filename);
639                         if (strcmp(sourcename, filename) != 0)
640                         {
641                                 ghb_settings_set_string (ud->settings, "default_source", filename);
642                                 ghb_pref_save (ud->settings, "default_source");
643                                 ghb_dvd_set_current (filename, ud);
644                         }
645                         g_free(filename);
646                 }
647         }
648         gtk_widget_destroy(dialog);
649 }
650
651 void
652 dvd_source_activate_cb(GtkAction *action, signal_user_data_t *ud)
653 {
654         const gchar *filename;
655         const gchar *sourcename;
656
657         sourcename = ghb_settings_get_string(ud->settings, "source");
658         filename = gtk_action_get_name(action);
659         do_scan(ud, filename);
660         if (strcmp(sourcename, filename) != 0)
661         {
662                 ghb_settings_set_string (ud->settings, "default_source", filename);
663                 ghb_pref_save (ud->settings, "default_source");
664                 ghb_dvd_set_current (filename, ud);
665         }
666 }
667
668 static void
669 update_destination_extension(signal_user_data_t *ud)
670 {
671         static gchar *containers[] = {".mkv", ".mp4", ".m4v", ".avi", ".ogm", NULL};
672         gchar *filename;
673         const gchar *extension;
674         gint ii;
675         GtkEntry *entry;
676
677         g_debug("update_destination_extension ()\n");
678         extension = ghb_settings_get_string(ud->settings, "container");
679         entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "destination"));
680         filename = g_strdup(gtk_entry_get_text(entry));
681         for (ii = 0; containers[ii] != NULL; ii++)
682         {
683                 if (g_str_has_suffix(filename, containers[ii]))
684                 {
685                         gchar *pos;
686                         gchar *new_name;
687                         
688                         pos = g_strrstr( filename, "." );
689                         if (pos == NULL)
690                         {
691                                 // No period? shouldn't happen
692                                 break;
693                         }
694                         *pos = 0;
695                         if (strcmp(extension, &pos[1]) == 0)
696                         {
697                                 // Extension is already correct
698                                 break;
699                         }
700                         new_name = g_strjoin(".", filename, extension, NULL); 
701                         ghb_ui_update(ud, "destination", new_name);
702                         g_free(new_name);
703                         break;
704                 }
705         }
706         g_free(filename);
707 }
708
709 static gboolean update_default_destination = FALSE;
710
711 void
712 destination_entry_changed_cb(GtkEntry *entry, signal_user_data_t *ud)
713 {
714         gchar *dest;
715         
716         g_debug("destination_entry_changed_cb ()\n");
717         if ((dest = expand_tilde(gtk_entry_get_text(entry))) != NULL)
718         {
719                 gtk_entry_set_text(entry, dest);
720                 g_free(dest);
721         }
722         update_destination_extension(ud);
723         ghb_widget_to_setting(ud->settings, (GtkWidget*)entry);
724         // This signal goes off with ever keystroke, so I'm putting this
725         // update on the timer.
726         update_default_destination = TRUE;
727 }
728
729 void
730 destination_browse_clicked_cb(GtkButton *button, signal_user_data_t *ud)
731 {
732         GtkWidget *dialog;
733         GtkEntry *entry;
734         const char *destname;
735         gchar *basename;
736
737         g_debug("destination_browse_clicked_cb ()\n");
738         destname = ghb_settings_get_string(ud->settings, "destination");
739         dialog = gtk_file_chooser_dialog_new ("Choose Destination",
740                       NULL,
741                       GTK_FILE_CHOOSER_ACTION_SAVE,
742                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
743                       GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
744                       NULL);
745         gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), destname);
746         basename = g_path_get_basename(destname);
747         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), basename);
748         g_free(basename);
749         if (gtk_dialog_run(GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
750         {
751                 char *filename;
752                 
753                 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
754                 entry = (GtkEntry*)GHB_WIDGET(ud->builder, "destination");
755                 if (entry == NULL)
756                 {
757                         g_debug("Failed to find widget: %s\n", "destination");
758                 }
759                 else
760                 {
761                         gtk_entry_set_text(entry, filename);
762                 }
763                 g_free (filename);
764         }
765         gtk_widget_destroy(dialog);
766 }
767
768 gboolean
769 window_destroy_event_cb(GtkWidget *widget, GdkEvent *event, signal_user_data_t *ud)
770 {
771         g_debug("window_destroy_event_cb ()\n");
772         ghb_hb_cleanup(FALSE);
773         gtk_main_quit();
774     return FALSE;
775 }
776
777 gboolean
778 window_delete_event_cb(GtkWidget *widget, GdkEvent *event, signal_user_data_t *ud)
779 {
780         gint state = ghb_get_state();
781         g_debug("window_delete_event_cb ()\n");
782     if (state & GHB_STATE_WORKING)
783     {
784         if (cancel_encode("Closing HandBrake will terminate encoding.\n"))
785         {
786                         ghb_hb_cleanup(FALSE);
787                 gtk_main_quit();
788             return FALSE;
789         }
790         return TRUE;
791     }
792         ghb_hb_cleanup(FALSE);
793         gtk_main_quit();
794     return FALSE;
795 }
796
797 static void
798 update_acodec_combo(signal_user_data_t *ud)
799 {
800         ghb_grey_combo_options (ud->builder);
801 }
802
803 void
804 container_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
805 {
806         g_debug("container_changed_cb ()\n");
807         ghb_widget_to_setting(ud->settings, widget);
808         update_destination_extension(ud);
809         check_depencency(ud, widget);
810         update_acodec_combo(ud);
811         clear_presets_selection(ud);
812         if (ghb_ac3_in_audio_list (ud->audio_settings))
813         {
814                 const gchar *container;
815
816                 container = ghb_settings_get_string(ud->settings, "container");
817                 if (strcmp(container, "mp4") == 0)
818                 {
819                         ghb_ui_update(ud, "container", "m4v");
820                 }
821         }
822 }
823
824 static gchar*
825 get_aspect_string(gint aspect_n, gint aspect_d)
826 {
827         gchar *aspect;
828
829         if (aspect_d < 10)
830         {
831                 aspect = g_strdup_printf("%d:%d", aspect_n, aspect_d);
832         }
833         else
834         {
835                 gdouble aspect_nf = (gdouble)aspect_n / aspect_d;
836                 aspect = g_strdup_printf("%.2f:1", aspect_nf);
837         }
838         return aspect;
839 }
840
841 static gchar*
842 get_rate_string(gint rate_base, gint rate)
843 {
844         gdouble rate_f = (gdouble)rate / rate_base;
845         gchar *rate_s;
846
847         rate_s = g_strdup_printf("%.6g", rate_f);
848         return rate_s;
849 }
850 static void
851 show_title_info(signal_user_data_t *ud, ghb_title_info_t *tinfo)
852 {
853         GtkWidget *widget;
854         gchar *text;
855
856         widget = GHB_WIDGET (ud->builder, "title_duration");
857         if (tinfo->duration != 0)
858         {
859                 text = g_strdup_printf ("%02d:%02d:%02d", tinfo->hours, 
860                                 tinfo->minutes, tinfo->seconds);
861         }
862         else
863         {
864                 text = g_strdup_printf ("Unknown");
865         }
866         gtk_label_set_text (GTK_LABEL(widget), text);
867         g_free(text);
868         widget = GHB_WIDGET (ud->builder, "source_dimensions");
869         text = g_strdup_printf ("%d x %d", tinfo->width, tinfo->height);
870         gtk_label_set_text (GTK_LABEL(widget), text);
871         g_free(text);
872         widget = GHB_WIDGET (ud->builder, "source_aspect");
873         text = get_aspect_string(tinfo->aspect_n, tinfo->aspect_d);
874         gtk_label_set_text (GTK_LABEL(widget), text);
875         g_free(text);
876
877         widget = GHB_WIDGET (ud->builder, "source_frame_rate");
878         text = (gchar*)get_rate_string(tinfo->rate_base, tinfo->rate);
879         gtk_label_set_text (GTK_LABEL(widget), text);
880         g_free(text);
881
882         ghb_ui_update_int (ud, "scale_width", tinfo->width - tinfo->crop[2] - tinfo->crop[3]);
883         // If anamorphic or keep_aspect, the hight will be automatically calculated
884         gboolean keep_aspect = ghb_settings_get_bool(ud->settings, "keep_aspect");
885         gboolean anamorphic = ghb_settings_get_bool(ud->settings, "anamorphic");
886         if (!(keep_aspect || anamorphic))
887                 ghb_ui_update_int (ud, "scale_height", tinfo->height - tinfo->crop[0] - tinfo->crop[1]);
888
889         // Set the limits of cropping.  hb_set_anamorphic_size crashes if
890         // you pass it a cropped width or height == 0.
891         gint bound;
892         bound = tinfo->height / 2 - 2;
893         widget = GHB_WIDGET (ud->builder, "crop_top");
894         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
895         widget = GHB_WIDGET (ud->builder, "crop_bottom");
896         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
897         bound = tinfo->width / 2 - 2;
898         widget = GHB_WIDGET (ud->builder, "crop_left");
899         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
900         widget = GHB_WIDGET (ud->builder, "crop_right");
901         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
902         if (ghb_settings_get_bool (ud->settings, "autocrop"))
903         {
904                 ghb_ui_update_int (ud, "crop_top", tinfo->crop[0]);
905                 ghb_ui_update_int (ud, "crop_bottom", tinfo->crop[1]);
906                 ghb_ui_update_int (ud, "crop_left", tinfo->crop[2]);
907                 ghb_ui_update_int (ud, "crop_right", tinfo->crop[3]);
908         }
909         g_debug("setting max end chapter %d\n", tinfo->num_chapters);
910         widget = GHB_WIDGET (ud->builder, "end_chapter");
911         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, tinfo->num_chapters);
912         gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), tinfo->num_chapters);
913         widget = GHB_WIDGET (ud->builder, "start_chapter");
914         gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), 1);
915 }
916
917 static void
918 adjust_audio_rate_combos(signal_user_data_t *ud)
919 {
920         gint titleindex, audioindex, acodec;
921         ghb_audio_info_t ainfo;
922         GtkWidget *widget;
923         
924         g_debug("adjust_audio_rate_combos ()\n");
925         titleindex = ghb_settings_get_index(ud->settings, "title");
926
927         widget = GHB_WIDGET(ud->builder, "audio_track");
928         audioindex = ghb_widget_int(widget);
929
930         widget = GHB_WIDGET(ud->builder, "audio_codec");
931         acodec = ghb_widget_int(widget);
932
933         if (ghb_get_audio_info (&ainfo, titleindex, audioindex) && ghb_audio_is_passthru (acodec))
934         {
935                 // Set the values for bitrate and samplerate to the input rates
936                 ghb_set_passthru_rate_opts (ud->builder, ainfo.bitrate);
937                 ghb_ui_update_int (ud, "audio_bitrate", ainfo.bitrate);
938                 ghb_ui_update_int (ud, "audio_rate", 0);
939                 ghb_ui_update_int (ud, "audio_mix", 0);
940         }
941         else
942         {
943                 ghb_set_default_rate_opts (ud->builder);
944         }
945 }
946
947 static void
948 set_pref_audio(gint titleindex, signal_user_data_t *ud)
949 {
950         gint acodec, track, ivalue;
951         const gchar *svalue;
952         GtkWidget *button;
953         ghb_audio_info_t ainfo;
954         gchar* drcstr;
955         gint index;
956         GHashTable *track_indicies;
957         gint *iptr;
958         
959         track_indicies = g_hash_table_new(g_int_hash, g_int_equal);
960         // Clear the audio list
961         clear_audio_list(ud);
962         // Find "best" audio based on audio preferences
963         button = GHB_WIDGET (ud->builder, "audio_add");
964         svalue = ghb_settings_get_short_opt(ud->settings, "source_audio_lang");
965         gint acount = ghb_pref_acount();
966         gint ii;
967         gint list_count = 0;
968         for (ii = 0; ii < acount; ii++)
969         {
970                 acodec = ghb_pref_acodec(ii);
971                 iptr = g_hash_table_lookup(track_indicies, &acodec);
972                 if (iptr == NULL)
973                         index = 0;
974                 else
975                         index = *(gint*)iptr;
976
977                 track = ghb_find_audio_track(titleindex, svalue, index);
978                 // Check to see if:
979                 // 1. pref codec is ac3
980                 // 2. source codec is not ac3
981                 // 3. next pref is enabled
982                 if (ghb_get_audio_info (&ainfo, titleindex, track) && 
983                         ghb_audio_is_passthru (acodec))
984                 {
985                         if (!ghb_audio_is_passthru(ainfo.codec))
986                         {
987                                 acodec = ghb_get_default_acodec();
988                                 if ((ii + 1 < acount) || (list_count != 0))
989                                 {
990                                         // Skip this audio
991                                         acodec = 0;
992                                 }
993                         }
994                 }
995                 if (titleindex >= 0 && track < 0)
996                         acodec = 0;
997                 if (acodec != 0)
998                 {
999                         // Add to audio list
1000                         g_signal_emit_by_name(button, "clicked", ud);
1001                         list_count++;
1002                         ghb_ui_update_int(ud, "audio_track", track);
1003                         ghb_ui_update_int(ud, "audio_codec", acodec);
1004                         if (!ghb_audio_is_passthru (acodec))
1005                         {
1006                                 // This gets set autimatically if the codec is passthru
1007                                 ivalue = ghb_pref_bitrate(ii);
1008                                 ghb_ui_update_int(ud, "audio_bitrate", ivalue);
1009                                 ivalue = ghb_pref_rate(ii);
1010                                 ghb_ui_update_int(ud, "audio_rate", ivalue);
1011                                 ivalue = ghb_pref_mix(ii);
1012                                 ivalue = ghb_get_best_mix(titleindex, track, acodec, ivalue);
1013                                 ghb_ui_update_int(ud, "audio_mix", ivalue);
1014                         }
1015                         drcstr = g_strdup_printf("%.2g", ghb_pref_drc(ii));
1016                         ghb_ui_update(ud, "audio_drc", drcstr);
1017                         g_free(drcstr);
1018                         index++;
1019                         g_hash_table_insert(track_indicies, &acodec, &index);
1020                 }
1021         }
1022         g_hash_table_destroy(track_indicies);
1023 }
1024
1025 static gint preview_button_width;
1026 static gint preview_button_height;
1027 static gboolean update_preview = FALSE;
1028
1029 static void
1030 set_preview_image(signal_user_data_t *ud)
1031 {
1032         GtkWidget *widget;
1033         gint preview_width, preview_height, target_height, width, height;
1034
1035         g_debug("set_preview_button_image ()\n");
1036         gint titleindex = ghb_settings_get_int(ud->settings, "title");
1037         if (titleindex < 0) return;
1038         widget = GHB_WIDGET (ud->builder, "preview_frame");
1039         gint frame = ghb_widget_int(widget) - 1;
1040         GdkPixbuf *preview = ghb_get_preview_image (titleindex, frame, ud->settings, TRUE);
1041         if (preview == NULL) return;
1042         widget = GHB_WIDGET (ud->builder, "preview_image");
1043         gtk_image_set_from_pixbuf(GTK_IMAGE(widget), preview);
1044
1045         preview_width = gdk_pixbuf_get_width(preview);
1046         preview_height = gdk_pixbuf_get_height(preview);
1047         gchar *text = g_strdup_printf("%d x %d", preview_width, preview_height);
1048         widget = GHB_WIDGET (ud->builder, "preview_dims");
1049         gtk_label_set_text(GTK_LABEL(widget), text);
1050         g_free(text);
1051         
1052         g_debug("preview %d x %d\n", preview_width, preview_height);
1053         target_height = MIN(preview_button_height - 12, 128);
1054         height = target_height;
1055         width = preview_width * height / preview_height;
1056
1057         if ((height >= 16) && (width >= 16))
1058         {
1059                 GdkPixbuf *scaled_preview;
1060                 scaled_preview = gdk_pixbuf_scale_simple (preview, width, height, GDK_INTERP_NEAREST);
1061                 if (scaled_preview != NULL)
1062                 {
1063                         g_object_unref (preview);
1064                         
1065                         widget = GHB_WIDGET (ud->builder, "preview_button_image");
1066                         gtk_image_set_from_pixbuf(GTK_IMAGE(widget), scaled_preview);
1067                         g_object_unref (scaled_preview);
1068                 }
1069         }
1070 }
1071
1072 void
1073 title_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1074 {
1075         ghb_title_info_t tinfo;
1076         gint titleindex;
1077         const gchar *preset;
1078         
1079         g_debug("title_changed_cb ()\n");
1080         ghb_widget_to_setting(ud->settings, widget);
1081         check_depencency(ud, widget);
1082
1083         titleindex = ghb_settings_get_int(ud->settings, "title");
1084         ghb_update_ui_combo_box (ud->builder, "audio_track", titleindex, FALSE);
1085         ghb_update_ui_combo_box (ud->builder, "subtitle_lang", titleindex, FALSE);
1086         preset = ghb_settings_get_string (ud->settings, "preset");
1087         ghb_update_from_preset(ud, preset, "subtitle_lang");
1088         if (ghb_get_title_info (&tinfo, titleindex))
1089         {
1090                 show_title_info(ud, &tinfo);
1091         }
1092         update_chapter_list (ud);
1093         adjust_audio_rate_combos(ud);
1094         set_pref_audio(titleindex, ud);
1095         if (ghb_settings_get_bool (ud->settings, "vquality_type_target"))
1096         {
1097                 gint bitrate = ghb_calculate_target_bitrate (ud->settings, titleindex);
1098                 ghb_ui_update_int (ud, "video_bitrate", bitrate);
1099         }
1100
1101         // Unfortunately, there is no way to query how many frames were
1102         // actually generated during the scan.  It attempts to make 10.
1103         // If I knew how many were generated, I would adjust the spin
1104         // control range here.
1105         ghb_ui_update_int (ud, "preview_frame", 1);
1106
1107         set_preview_image (ud);
1108 }
1109
1110 void
1111 audio_codec_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1112 {
1113         static gint prev_acodec = 0;
1114         gint acodec, ivalue;
1115         GHashTable *asettings;
1116         
1117         g_debug("audio_codec_changed_cb ()\n");
1118         
1119         acodec = ghb_widget_int(widget);
1120         if (ghb_audio_is_passthru (prev_acodec) && !ghb_audio_is_passthru (acodec))
1121         {
1122                 // Transition from passthru to not, put some audio settings back to 
1123                 // pref settings
1124                 gint titleindex = ghb_settings_get_int(ud->settings, "title");
1125                 gint track = ghb_settings_get_int(ud->settings, "audio_track");
1126
1127                 ivalue = ghb_pref_bitrate(0);
1128                 ghb_ui_update_int (ud, "audio_bitrate", ivalue);
1129                 ivalue = ghb_pref_rate(0);
1130                 ghb_ui_update_int (ud, "audio_rate", ivalue);
1131                 ivalue = ghb_pref_mix(0);
1132                 ivalue = ghb_get_best_mix(titleindex, track, acodec, ivalue);
1133                 ghb_ui_update_int (ud, "audio_mix", ivalue);
1134         }
1135         adjust_audio_rate_combos(ud);
1136         ghb_grey_combo_options (ud->builder);
1137         check_depencency(ud, widget);
1138         prev_acodec = acodec;
1139         asettings = get_selected_asettings(ud);
1140         if (asettings != NULL)
1141         {
1142                 ghb_widget_to_setting(asettings, widget);
1143                 audio_list_refresh_selected(ud);
1144         }
1145         if (ghb_ac3_in_audio_list (ud->audio_settings))
1146         {
1147                 const gchar *container;
1148
1149                 container = ghb_settings_get_string(ud->settings, "container");
1150                 if (strcmp(container, "mp4") == 0)
1151                 {
1152                         ghb_ui_update(ud, "container", "m4v");
1153                 }
1154         }
1155 }
1156
1157 static void audio_list_refresh_selected(signal_user_data_t *ud);
1158 static GHashTable* get_selected_asettings(signal_user_data_t *ud);
1159
1160 void
1161 audio_track_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1162 {
1163         GHashTable *asettings;
1164
1165         g_debug("audio_track_changed_cb ()\n");
1166         adjust_audio_rate_combos(ud);
1167         check_depencency(ud, widget);
1168         ghb_grey_combo_options(ud->builder);
1169         asettings = get_selected_asettings(ud);
1170         if (asettings != NULL)
1171         {
1172                 ghb_widget_to_setting(asettings, widget);
1173                 audio_list_refresh_selected(ud);
1174         }
1175 }
1176
1177 void
1178 audio_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1179 {
1180         GHashTable *asettings;
1181
1182         g_debug("audio_widget_changed_cb ()\n");
1183         check_depencency(ud, widget);
1184         asettings = get_selected_asettings(ud);
1185         if (asettings != NULL)
1186         {
1187                 ghb_widget_to_setting(asettings, widget);
1188                 audio_list_refresh_selected(ud);
1189         }
1190 }
1191
1192 void
1193 generic_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1194 {
1195         g_debug("generic_widget_changed_cb ()\n");
1196         check_depencency(ud, widget);
1197 }
1198
1199 void
1200 setting_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1201 {
1202         ghb_widget_to_setting(ud->settings, widget);
1203         check_depencency(ud, widget);
1204         clear_presets_selection(ud);
1205 }
1206
1207 void
1208 http_opt_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1209 {
1210         ghb_widget_to_setting(ud->settings, widget);
1211         check_depencency(ud, widget);
1212         clear_presets_selection(ud);
1213         ghb_grey_combo_options (ud->builder);
1214 }
1215
1216 void
1217 vcodec_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1218 {
1219         gint vqmin, vqmax;
1220
1221         ghb_widget_to_setting(ud->settings, widget);
1222         check_depencency(ud, widget);
1223         clear_presets_selection(ud);
1224         ghb_vquality_range(ud, &vqmin, &vqmax);
1225         GtkWidget *qp = GHB_WIDGET(ud->builder, "video_quality");
1226         gtk_range_set_range (GTK_RANGE(qp), vqmin, vqmax);
1227 }
1228
1229 void
1230 vfr_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1231 {
1232         //const gchar *name = gtk_widget_get_name(widget);
1233         //g_debug("setting_widget_changed_cb () %s\n", name);
1234         ghb_widget_to_setting(ud->settings, widget);
1235         check_depencency(ud, widget);
1236         clear_presets_selection(ud);
1237         if (ghb_settings_get_bool(ud->settings, "variable_frame_rate"))
1238         {
1239                 ghb_ui_update_int(ud, "framerate", 0);
1240         }
1241 }
1242
1243 void
1244 mirror_cb(GtkWidget *widget, signal_user_data_t *ud)
1245 {
1246         const gchar *name = gtk_widget_get_name(widget);
1247         if (name == NULL) return;
1248         
1249         g_debug("mirror_cb () %s\n", name);
1250         gchar *mirror = g_strdup(name);
1251         gchar *pos = g_strrstr(mirror, "_mirror");
1252         if (pos == NULL) return;
1253         *pos = 0;
1254         gchar *value = ghb_widget_short_opt (widget);
1255         ghb_ui_update (ud, mirror, value);
1256         g_free(mirror);
1257 }
1258
1259 // subtitles have their differ from other settings in that
1260 // the selection is updated automaitcally when the title
1261 // changes.  I don't want the preset selection changed as
1262 // would happen for regular settings.
1263 void
1264 subtitle_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1265 {
1266         const gchar *name = gtk_widget_get_name(widget);
1267         g_debug("subtitle_changed_cb () %s\n", name);
1268         ghb_widget_to_setting(ud->settings, widget);
1269         check_depencency(ud, widget);
1270 }
1271
1272 void
1273 target_size_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1274 {
1275         const gchar *name = gtk_widget_get_name(widget);
1276         g_debug("setting_widget_changed_cb () %s\n", name);
1277         ghb_widget_to_setting(ud->settings, widget);
1278         check_depencency(ud, widget);
1279         clear_presets_selection(ud);
1280         if (ghb_settings_get_bool (ud->settings, "vquality_type_target"))
1281         {
1282                 gint titleindex = ghb_settings_get_int(ud->settings, "title");
1283                 gint bitrate = ghb_calculate_target_bitrate (ud->settings, titleindex);
1284                 ghb_ui_update_int (ud, "video_bitrate", bitrate);
1285         }
1286 }
1287
1288 void
1289 start_chapter_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1290 {
1291         const gchar *name = gtk_widget_get_name(widget);
1292         g_debug("start_chapter_changed_cb () %s\n", name);
1293         ghb_widget_to_setting(ud->settings, widget);
1294         GtkWidget *end_ch = GHB_WIDGET (ud->builder, "end_chapter");
1295         gdouble start, end;
1296         gtk_spin_button_get_range (GTK_SPIN_BUTTON(end_ch), &start, &end);
1297         start = ghb_settings_get_int(ud->settings, "start_chapter");
1298         gtk_spin_button_set_range (GTK_SPIN_BUTTON(end_ch), start, end);
1299         check_depencency(ud, widget);
1300 }
1301
1302 void
1303 end_chapter_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1304 {
1305         const gchar *name = gtk_widget_get_name(widget);
1306         g_debug("end_chapter_changed_cb () %s\n", name);
1307         ghb_widget_to_setting(ud->settings, widget);
1308         GtkWidget *start_ch = GHB_WIDGET (ud->builder, "start_chapter");
1309         gdouble start, end;
1310         gtk_spin_button_get_range (GTK_SPIN_BUTTON(start_ch), &start, &end);
1311         end = ghb_settings_get_int(ud->settings, "end_chapter");
1312         gtk_spin_button_set_range (GTK_SPIN_BUTTON(start_ch), start, end);
1313         check_depencency(ud, widget);
1314 }
1315
1316 void
1317 scale_width_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1318 {
1319         g_debug("scale_width_changed_cb ()\n");
1320         ghb_widget_to_setting(ud->settings, widget);
1321         check_depencency(ud, widget);
1322         ghb_set_scale (ud, GHB_SCALE_KEEP_WIDTH);
1323         update_preview = TRUE;
1324         gchar *text;
1325         gint width = ghb_settings_get_int(ud->settings, "scale_width");
1326         gint height = ghb_settings_get_int(ud->settings, "scale_height");
1327         widget = GHB_WIDGET (ud->builder, "scale_dimensions");
1328         text = g_strdup_printf ("%d x %d", width, height);
1329         gtk_label_set_text (GTK_LABEL(widget), text);
1330         g_free(text);
1331 }
1332
1333 void
1334 scale_height_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1335 {
1336         g_debug("scale_height_changed_cb ()\n");
1337         ghb_widget_to_setting(ud->settings, widget);
1338         check_depencency(ud, widget);
1339         ghb_set_scale (ud, GHB_SCALE_KEEP_HEIGHT);
1340         update_preview = TRUE;
1341         gchar *text;
1342         gint width = ghb_settings_get_int(ud->settings, "scale_width");
1343         gint height = ghb_settings_get_int(ud->settings, "scale_height");
1344         widget = GHB_WIDGET (ud->builder, "scale_dimensions");
1345         text = g_strdup_printf ("%d x %d", width, height);
1346         gtk_label_set_text (GTK_LABEL(widget), text);
1347         g_free(text);
1348 }
1349
1350 void
1351 crop_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1352 {
1353         gint titleindex, crop[4];
1354         ghb_title_info_t tinfo;
1355         
1356         g_debug("crop_changed_cb ()\n");
1357         ghb_widget_to_setting(ud->settings, widget);
1358         check_depencency(ud, widget);
1359         ghb_set_scale (ud, GHB_SCALE_KEEP_NONE);
1360
1361         crop[0] = ghb_settings_get_int(ud->settings, "crop_top");
1362         crop[1] = ghb_settings_get_int(ud->settings, "crop_bottom");
1363         crop[2] = ghb_settings_get_int(ud->settings, "crop_left");
1364         crop[3] = ghb_settings_get_int(ud->settings, "crop_right");
1365         titleindex = ghb_settings_get_index(ud->settings, "title");
1366         if (ghb_get_title_info (&tinfo, titleindex))
1367         {
1368                 gint width, height;
1369                 gchar *text;
1370                 
1371                 width = tinfo.width - crop[2] - crop[3];
1372                 height = tinfo.height - crop[0] - crop[1];
1373                 widget = GHB_WIDGET (ud->builder, "crop_dimensions");
1374                 text = g_strdup_printf ("%d x %d", width, height);
1375                 gtk_label_set_text (GTK_LABEL(widget), text);
1376                 g_free(text);
1377         }
1378         gchar *text;
1379         widget = GHB_WIDGET (ud->builder, "crop_values");
1380         text = g_strdup_printf ("%d:%d:%d:%d", crop[0], crop[1], crop[2], crop[3]);
1381         gtk_label_set_text (GTK_LABEL(widget), text);
1382         g_free(text);
1383         update_preview = TRUE;
1384 }
1385
1386 void
1387 scale_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1388 {
1389         g_debug("scale_changed_cb ()\n");
1390         ghb_widget_to_setting(ud->settings, widget);
1391         check_depencency(ud, widget);
1392         clear_presets_selection(ud);
1393         ghb_set_scale (ud, GHB_SCALE_KEEP_NONE);
1394         update_preview = TRUE;
1395         
1396         gchar *text;
1397         
1398         text = ghb_settings_get_bool(ud->settings, "autocrop") ? "On" : "Off";
1399         widget = GHB_WIDGET (ud->builder, "crop_auto");
1400         gtk_label_set_text (GTK_LABEL(widget), text);
1401         text = ghb_settings_get_bool(ud->settings, "autoscale") ? "On" : "Off";
1402         widget = GHB_WIDGET (ud->builder, "scale_auto");
1403         gtk_label_set_text (GTK_LABEL(widget), text);
1404         text = ghb_settings_get_bool(ud->settings, "anamorphic") ? "On" : "Off";
1405         widget = GHB_WIDGET (ud->builder, "scale_anamorphic");
1406         gtk_label_set_text (GTK_LABEL(widget), text);
1407 }
1408
1409 void
1410 generic_entry_changed_cb(GtkEntry *entry, signal_user_data_t *ud)
1411 {
1412         // Normally (due to user input) I only want to process the entry
1413         // when editing is done and the focus-out signal is sent.
1414         // But... there's always a but.
1415         // If the entry is changed by software, the focus-out signal is not sent.
1416         // The changed signal is sent ... so here we are.
1417         // I don't want to process upon every keystroke, so I prevent processing
1418         // while the widget has focus.
1419         g_debug("generic_entry_changed_cb ()\n");
1420         if (!GTK_WIDGET_HAS_FOCUS((GtkWidget*)entry))
1421         {
1422                 ghb_widget_to_setting(ud->settings, (GtkWidget*)entry);
1423         }
1424 }
1425
1426 gboolean
1427 generic_focus_out_cb(GtkWidget *widget, GdkEventFocus *event, 
1428     signal_user_data_t *ud)
1429 {
1430         g_debug("generic_focus_out_cb ()\n");
1431         ghb_widget_to_setting(ud->settings, widget);
1432         return FALSE;
1433 }
1434
1435 // Flag needed to prevent x264 options processing from chasing its tail
1436 static gboolean ignore_options_update = FALSE;
1437
1438 void
1439 x264_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1440 {
1441         ghb_widget_to_setting(ud->settings, widget);
1442         if (!ignore_options_update)
1443         {
1444                 ignore_options_update = TRUE;
1445                 ghb_x264_opt_update(ud, widget);
1446                 ignore_options_update = FALSE;
1447         }
1448         check_depencency(ud, widget);
1449         clear_presets_selection(ud);
1450 }
1451
1452 void
1453 x264_entry_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1454 {
1455         g_debug("x264_entry_changed_cb ()\n");
1456         if (!ignore_options_update)
1457         {
1458                 GtkWidget *textview;
1459                 textview = GTK_WIDGET(GHB_WIDGET(ud->builder, "x264_options"));
1460                 ghb_widget_to_setting(ud->settings, textview);
1461                 gchar *options;
1462                 options = (gchar*)ghb_settings_get_string(ud->settings, "x264_options");
1463                 ignore_options_update = TRUE;
1464                 ghb_x264_parse_options(ud, options);
1465                 if (!GTK_WIDGET_HAS_FOCUS(textview))
1466                 {
1467                         options = ghb_sanitize_x264opts(ud, options);
1468                         ghb_ui_update(ud, "x264_options", options);
1469                         ghb_x264_parse_options(ud, options);
1470                         g_free(options);
1471                 }
1472                 ignore_options_update = FALSE;
1473         }
1474 }
1475
1476 gboolean
1477 x264_focus_out_cb(GtkWidget *widget, GdkEventFocus *event, 
1478     signal_user_data_t *ud)
1479 {
1480         ghb_widget_to_setting(ud->settings, widget);
1481         gchar *options;
1482         options = (gchar*)ghb_settings_get_string(ud->settings, "x264_options");
1483         options = ghb_sanitize_x264opts(ud, options);
1484         ignore_options_update = TRUE;
1485         if (options != NULL)
1486         {
1487                 ghb_ui_update(ud, "x264_options", options);
1488                 ghb_x264_parse_options(ud, options);
1489                 g_free(options);
1490         }
1491         ignore_options_update = FALSE;
1492         return FALSE;
1493 }
1494
1495 static void
1496 clear_audio_list(signal_user_data_t *ud)
1497 {
1498         GtkTreeView *treeview;
1499         GtkListStore *store;
1500         GSList *link;
1501         
1502         g_debug("clear_audio_list ()\n");
1503         while (ud->audio_settings != NULL)
1504         {
1505                 link = ud->audio_settings;
1506                 ud->audio_settings = g_slist_remove_link(ud->audio_settings, link);
1507                 g_hash_table_destroy((GHashTable*)link->data);
1508                 g_slist_free_1(link);
1509         }
1510         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "audio_list"));
1511         store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview));
1512         gtk_list_store_clear (store);
1513 }
1514
1515 static void
1516 add_to_audio_list(signal_user_data_t *ud, GHashTable *settings)
1517 {
1518         GtkTreeView *treeview;
1519         GtkTreeIter iter;
1520         GtkListStore *store;
1521         GtkTreeSelection *selection;
1522         
1523         g_debug("add_to_audio_list ()\n");
1524         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "audio_list"));
1525         selection = gtk_tree_view_get_selection (treeview);
1526         store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview));
1527
1528         gtk_list_store_append(store, &iter);
1529         gtk_list_store_set(store, &iter, 
1530                 // These are displayed in list
1531                 0, ghb_settings_get_option(settings, "audio_track"),
1532                 1, ghb_settings_get_option(settings, "audio_codec"),
1533                 2, ghb_settings_get_option(settings, "audio_bitrate"),
1534                 3, ghb_settings_get_option(settings, "audio_rate"),
1535                 4, ghb_settings_get_option(settings, "audio_mix"),
1536                 // These are used to set combo box values when a list item is selected
1537                 5, ghb_settings_get_string(settings, "audio_drc"),
1538                 6, ghb_settings_get_short_opt(settings, "audio_track"),
1539                 7, ghb_settings_get_short_opt(settings, "audio_codec"),
1540                 8, ghb_settings_get_short_opt(settings, "audio_bitrate"),
1541                 9, ghb_settings_get_short_opt(settings, "audio_rate"),
1542                 10, ghb_settings_get_short_opt(settings, "audio_mix"),
1543                 -1);
1544         gtk_tree_selection_select_iter(selection, &iter);
1545 }
1546
1547 static void
1548 audio_list_refresh_selected(signal_user_data_t *ud)
1549 {
1550         GtkTreeView *treeview;
1551         GtkTreePath *treepath;
1552         GtkTreeSelection *selection;
1553         GtkTreeModel *store;
1554         GtkTreeIter iter;
1555         gint *indices;
1556         gint row;
1557         GSList *link;
1558         GHashTable *asettings = NULL;
1559         
1560         g_debug("get_selected_asettings ()\n");
1561         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "audio_list"));
1562         selection = gtk_tree_view_get_selection (treeview);
1563         if (gtk_tree_selection_get_selected(selection, &store, &iter))
1564         {
1565         // Get the row number
1566                 treepath = gtk_tree_model_get_path (store, &iter);
1567                 indices = gtk_tree_path_get_indices (treepath);
1568                 g_free(treepath);
1569                 row = indices[0];
1570                 // find audio settings
1571                 if (row < 0) return;
1572                 link = g_slist_nth(ud->audio_settings, row);
1573                 if (link == NULL) return;
1574                 asettings = (GHashTable*)link->data;
1575                 gtk_list_store_set(GTK_LIST_STORE(store), &iter, 
1576                         // These are displayed in list
1577                         0, ghb_settings_get_option(asettings, "audio_track"),
1578                         1, ghb_settings_get_option(asettings, "audio_codec"),
1579                         2, ghb_settings_get_option(asettings, "audio_bitrate"),
1580                         3, ghb_settings_get_option(asettings, "audio_rate"),
1581                         4, ghb_settings_get_option(asettings, "audio_mix"),
1582                         // These are used to set combo box values when a list item is selected
1583                         5, ghb_settings_get_string(asettings, "audio_drc"),
1584                         6, ghb_settings_get_short_opt(asettings, "audio_track"),
1585                         7, ghb_settings_get_short_opt(asettings, "audio_codec"),
1586                         8, ghb_settings_get_short_opt(asettings, "audio_bitrate"),
1587                         9, ghb_settings_get_short_opt(asettings, "audio_rate"),
1588                         10, ghb_settings_get_short_opt(asettings, "audio_mix"),
1589                         -1);
1590         }
1591 }
1592
1593 static GHashTable*
1594 get_selected_asettings(signal_user_data_t *ud)
1595 {
1596         GtkTreeView *treeview;
1597         GtkTreePath *treepath;
1598         GtkTreeSelection *selection;
1599         GtkTreeModel *store;
1600         GtkTreeIter iter;
1601         gint *indices;
1602         gint row;
1603         GSList *link;
1604         GHashTable *asettings = NULL;
1605         
1606         g_debug("get_selected_asettings ()\n");
1607         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "audio_list"));
1608         selection = gtk_tree_view_get_selection (treeview);
1609         if (gtk_tree_selection_get_selected(selection, &store, &iter))
1610         {
1611         // Get the row number
1612                 treepath = gtk_tree_model_get_path (store, &iter);
1613                 indices = gtk_tree_path_get_indices (treepath);
1614                 g_free(treepath);
1615                 row = indices[0];
1616                 // find audio settings
1617                 if (row < 0) return NULL;
1618                 link = g_slist_nth(ud->audio_settings, row);
1619                 if (link == NULL) return NULL;
1620                 asettings = (GHashTable*)link->data;
1621         }
1622         return asettings;
1623 }
1624
1625 void
1626 audio_list_selection_changed_cb(GtkTreeSelection *selection, signal_user_data_t *ud)
1627 {
1628         GtkTreeModel *store;
1629         GtkTreeIter iter;
1630         GtkWidget *widget;
1631         
1632         g_debug("audio_list_selection_changed_cb ()\n");
1633         if (gtk_tree_selection_get_selected(selection, &store, &iter))
1634         {
1635                 const gchar *track, *codec, *bitrate, *sample_rate, *mix, *drc;
1636                 gtk_tree_model_get(store, &iter,
1637                                                    6, &track,
1638                                                    7, &codec,
1639                                                    8, &bitrate,
1640                                                    9, &sample_rate,
1641                                                    10, &mix,
1642                                                    5, &drc,
1643                                                    -1);
1644                 ghb_ui_update(ud, "audio_track", track);
1645                 ghb_ui_update(ud, "audio_codec", codec);
1646                 ghb_ui_update(ud, "audio_bitrate", bitrate);
1647                 ghb_ui_update(ud, "audio_rate", sample_rate);
1648                 ghb_ui_update(ud, "audio_mix", mix);
1649                 ghb_ui_update(ud, "audio_drc", drc);
1650                 widget = GHB_WIDGET (ud->builder, "audio_remove");
1651                 gtk_widget_set_sensitive(widget, TRUE);
1652                 //widget = GHB_WIDGET (ud->builder, "audio_update");
1653                 //gtk_widget_set_sensitive(widget, TRUE);
1654         }
1655         else
1656         {
1657                 widget = GHB_WIDGET (ud->builder, "audio_remove");
1658                 gtk_widget_set_sensitive(widget, FALSE);
1659                 //widget = GHB_WIDGET (ud->builder, "audio_update");
1660                 //gtk_widget_set_sensitive(widget, FALSE);
1661         }
1662 }
1663
1664 void
1665 audio_add_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
1666 {
1667         // Add the current audio settings to the list.
1668         GHashTable *asettings;
1669         GtkWidget *widget;
1670         gint count;
1671         
1672         g_debug("audio_add_clicked_cb ()\n");
1673         // Only allow up to 8 audio entries
1674         asettings = ghb_settings_new();
1675         widget = GHB_WIDGET(ud->builder, "audio_track");
1676         ghb_settings_set(asettings,     "audio_track", ghb_widget_value(widget));
1677         widget = GHB_WIDGET(ud->builder, "audio_codec");
1678         ghb_settings_set(asettings,     "audio_codec", ghb_widget_value(widget));
1679         widget = GHB_WIDGET(ud->builder, "audio_bitrate");
1680         ghb_settings_set(asettings,     "audio_bitrate", ghb_widget_value(widget));
1681         widget = GHB_WIDGET(ud->builder, "audio_rate");
1682         ghb_settings_set(asettings,     "audio_rate", ghb_widget_value(widget));
1683         widget = GHB_WIDGET(ud->builder, "audio_mix");
1684         ghb_settings_set(asettings,     "audio_mix", ghb_widget_value(widget));
1685         widget = GHB_WIDGET(ud->builder, "audio_drc");
1686         ghb_settings_set(asettings,     "audio_drc", ghb_widget_value(widget));
1687
1688         ud->audio_settings = g_slist_append(ud->audio_settings, asettings);
1689         add_to_audio_list(ud, asettings);
1690         count = g_slist_length(ud->audio_settings);
1691         if (count >= 8)
1692         {
1693                 gtk_widget_set_sensitive(xwidget, FALSE);
1694         }
1695 }
1696
1697 void
1698 audio_remove_clicked_cb(GtkWidget *widget, signal_user_data_t *ud)
1699 {
1700         GtkTreeView *treeview;
1701         GtkTreePath *treepath;
1702         GtkTreeSelection *selection;
1703         GtkTreeModel *store;
1704         GtkTreeIter iter, nextIter;
1705         gint *indices;
1706         gint row;
1707         GSList *link;
1708
1709         g_debug("audio_remove_clicked_cb ()\n");
1710         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "audio_list"));
1711         selection = gtk_tree_view_get_selection (treeview);
1712         if (gtk_tree_selection_get_selected(selection, &store, &iter))
1713         {
1714                 nextIter = iter;
1715                 if (!gtk_tree_model_iter_next(store, &nextIter))
1716                 {
1717                         nextIter = iter;
1718                         if (gtk_tree_model_get_iter_first(store, &nextIter))
1719                         {
1720                                 gtk_tree_selection_select_iter (selection, &nextIter);
1721                         }
1722                 }
1723                 else
1724                 {
1725                         gtk_tree_selection_select_iter (selection, &nextIter);
1726                 }
1727         // Get the row number
1728                 treepath = gtk_tree_model_get_path (store, &iter);
1729                 indices = gtk_tree_path_get_indices (treepath);
1730                 g_free(treepath);
1731                 row = indices[0];
1732                 // Remove the selected item
1733                 gtk_list_store_remove (GTK_LIST_STORE(store), &iter);
1734                 // remove from audio settings list
1735                 if (row < 0) return;
1736                 link = g_slist_nth(ud->audio_settings, row);
1737                 if (link == NULL) return;
1738                 ud->audio_settings = g_slist_remove_link(ud->audio_settings, link);
1739                 g_hash_table_destroy((GHashTable*)link->data);
1740                 g_slist_free_1(link);
1741                 widget = GHB_WIDGET (ud->builder, "audio_add");
1742                 gtk_widget_set_sensitive(widget, TRUE);
1743         }
1744 }
1745
1746 static void
1747 audio_list_refresh(signal_user_data_t *ud)
1748 {
1749         GtkTreeView *treeview;
1750         GtkTreeIter iter;
1751         GtkListStore *store;
1752         gboolean done;
1753         
1754         g_debug("audio_list_refresh ()\n");
1755         GSList *link = ud->audio_settings;
1756         if (link == NULL) return;
1757         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "audio_list"));
1758         store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview));
1759         if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter))
1760         {
1761                 do
1762                 {
1763                         GHashTable *asettings;
1764
1765                         asettings = (GHashTable*)link->data;
1766                         gtk_list_store_set(GTK_LIST_STORE(store), &iter, 
1767                                 // These are displayed in list
1768                                 0, ghb_settings_get_option(asettings, "audio_track"),
1769                                 1, ghb_settings_get_option(asettings, "audio_codec"),
1770                                 2, ghb_settings_get_option(asettings, "audio_bitrate"),
1771                                 3, ghb_settings_get_option(asettings, "audio_rate"),
1772                                 4, ghb_settings_get_option(asettings, "audio_mix"),
1773                                 // These are used to set combo box values when a list item is selected
1774                                 5, ghb_settings_get_string(asettings, "audio_drc"),
1775                                 6, ghb_settings_get_short_opt(asettings, "audio_track"),
1776                                 7, ghb_settings_get_short_opt(asettings, "audio_codec"),
1777                                 8, ghb_settings_get_short_opt(asettings, "audio_bitrate"),
1778                                 9, ghb_settings_get_short_opt(asettings, "audio_rate"),
1779                                 10, ghb_settings_get_short_opt(asettings, "audio_mix"),
1780                                 -1);
1781                         done = !gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1782                         link = link->next;
1783                 } while (!done && link);
1784         }
1785 }
1786
1787 void
1788 ghb_presets_list_update(signal_user_data_t *ud)
1789 {
1790         GtkTreeView *treeview;
1791         GtkTreeIter iter;
1792         GtkListStore *store;
1793         gboolean done;
1794         gint ii = 0;
1795         gint index;
1796         gchar **presets;
1797         gchar **descriptions;
1798         gint flags, custom, def;
1799         
1800         g_debug("ghb_presets_list_update ()\n");
1801         presets = ghb_presets_get_names();
1802         descriptions = ghb_presets_get_descriptions();
1803         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
1804         store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview));
1805         if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter))
1806         {
1807                 do
1808                 {
1809                         if ((presets != NULL) && (presets[ii] != NULL))
1810                         {
1811                                 // Update row with settings data
1812                                 g_debug("Updating row\n");
1813                                 flags = ghb_preset_flags(presets[ii], &index);
1814                                 def = flags & PRESET_DEFAULT;
1815                                 custom = flags & PRESET_CUSTOM;
1816                                 gtk_list_store_set(store, &iter, 
1817                                                         0, presets[ii], 
1818                                                         1, def ? 800 : 400, 
1819                                                         2, def ? 2 : 0,
1820                                                         3, custom ? "black" : "blue", 
1821                                                         4, descriptions[ii],
1822                                                         -1);
1823                                 ii++;
1824                                 done = !gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
1825                         }
1826                         else
1827                         {
1828                                 // No more settings data, remove row
1829                                 g_debug("Removing row\n");
1830                                 done = !gtk_list_store_remove(store, &iter);
1831                         }
1832                 } while (!done);
1833         }
1834         while ((presets != NULL) && (presets[ii] != NULL))
1835         {
1836                 // Additional settings, add row
1837                 g_debug("Adding row %s\n", presets[ii]);
1838                 gtk_list_store_append(store, &iter);
1839                 flags = ghb_preset_flags(presets[ii], &index);
1840                 def = flags & PRESET_DEFAULT;
1841                 custom = flags & PRESET_CUSTOM;
1842                 gtk_list_store_set(store, &iter, 0, presets[ii], 
1843                                                         1, def ? 800 : 400, 
1844                                                         2, def ? 2 : 0,
1845                                                         3, custom ? "black" : "blue", 
1846                                                         4, descriptions[ii],
1847                                                         -1);
1848                 ii++;
1849         }
1850         g_strfreev (presets);
1851 }
1852
1853 void
1854 ghb_select_preset(GtkBuilder *builder, const gchar *preset)
1855 {
1856         GtkTreeView *treeview;
1857         GtkTreeSelection *selection;
1858         GtkTreeModel *store;
1859         GtkTreeIter iter;
1860         gchar *tpreset;
1861         gboolean done;
1862         
1863         g_debug("select_preset()\n");
1864         if (preset == NULL) return;
1865         treeview = GTK_TREE_VIEW(GHB_WIDGET(builder, "presets_list"));
1866         selection = gtk_tree_view_get_selection (treeview);
1867         store = gtk_tree_view_get_model (treeview);
1868         if (gtk_tree_model_get_iter_first(store, &iter))
1869         {
1870                 do
1871                 {
1872                         gtk_tree_model_get(store, &iter, 0, &tpreset, -1);
1873                         if (strcmp(preset, tpreset) == 0)
1874                         {
1875                                 gtk_tree_selection_select_iter (selection, &iter);
1876                                 break;
1877                         }
1878                         done = !gtk_tree_model_iter_next(store, &iter);
1879                 } while (!done);
1880         }
1881 }
1882
1883 static void
1884 update_audio_presets(signal_user_data_t *ud)
1885 {
1886         GSList *link = ud->audio_settings;
1887         GHashTable *asettings;
1888         gchar *acodec, *bitrate, *rate, *mix, *drc;
1889         GString *pref_acodec, *pref_bitrate, *pref_rate, *pref_mix, *pref_drc;
1890         pref_acodec = g_string_new("");
1891         pref_bitrate = g_string_new("");
1892         pref_rate = g_string_new("");
1893         pref_mix = g_string_new("");
1894         pref_drc = g_string_new("");
1895         while (link)
1896         {
1897                 gchar *format = link->next ? "%s," : "%s";
1898                 asettings = (GHashTable*)link->data;
1899                 acodec = (gchar*)ghb_settings_get_short_opt(asettings, "audio_codec");
1900                 g_string_append_printf( pref_acodec, format, acodec);
1901                 bitrate = (gchar*)ghb_settings_get_string(asettings, "audio_bitrate");
1902                 g_string_append_printf( pref_bitrate, format, bitrate);
1903                 rate = (gchar*)ghb_settings_get_string(asettings, "audio_rate");
1904                 g_string_append_printf( pref_rate, format, rate);
1905                 mix = (gchar*)ghb_settings_get_short_opt(asettings, "audio_mix");
1906                 g_string_append_printf( pref_mix, format, mix);
1907                 drc = (gchar*)ghb_settings_get_string(asettings, "audio_drc");
1908                 g_string_append_printf( pref_drc, format, drc);
1909                 link = link->next;
1910         }
1911         acodec = g_string_free(pref_acodec, FALSE);
1912         bitrate = g_string_free(pref_bitrate, FALSE);
1913         rate = g_string_free(pref_rate, FALSE);
1914         mix = g_string_free(pref_mix, FALSE);
1915         drc = g_string_free(pref_drc, FALSE);
1916         ghb_settings_set_string(ud->settings, "pref_audio_codec", acodec);
1917         ghb_settings_set_string(ud->settings, "pref_audio_bitrate", bitrate);
1918         ghb_settings_set_string(ud->settings, "pref_audio_rate", rate);
1919         ghb_settings_set_string(ud->settings, "pref_audio_mix", mix);
1920         ghb_settings_set_string(ud->settings, "pref_audio_drc", drc);
1921         g_free(acodec);
1922         g_free(bitrate);
1923         g_free(rate);
1924         g_free(mix);
1925         g_free(drc);
1926 }
1927
1928 void
1929 presets_save_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
1930 {
1931         GtkWidget *dialog;
1932         GtkEntry *entry;
1933         GtkTextView *desc;
1934         GtkResponseType response;
1935         const gchar *preset = "";
1936
1937         g_debug("presets_save_clicked_cb ()\n");
1938         // Construct the audio settings presets from the current audio list
1939         preset = ghb_settings_get_string (ud->settings, "preset");
1940         // Clear the description
1941         desc = GTK_TEXT_VIEW(GHB_WIDGET(ud->builder, "preset_description"));
1942         //gtk_entry_set_text(desc, "");
1943         dialog = GHB_WIDGET(ud->builder, "preset_save_dialog");
1944         entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "preset_name"));
1945         gtk_entry_set_text(entry, preset);
1946         response = gtk_dialog_run(GTK_DIALOG(dialog));
1947         gtk_widget_hide(dialog);
1948         if (response == GTK_RESPONSE_OK)
1949         {
1950                 // save the preset
1951                 const gchar *name = gtk_entry_get_text(entry);
1952                 g_debug("description to settings\n");
1953                 ghb_widget_to_setting(ud->settings, GTK_WIDGET(desc));
1954                 update_audio_presets(ud);
1955                 ghb_settings_save(ud, name);
1956                 ghb_presets_list_update(ud);
1957                 // Make the new preset the selected item
1958                 ghb_select_preset(ud->builder, name);
1959         }
1960 }
1961
1962 void
1963 prefs_dialog_cb(GtkWidget *xwidget, signal_user_data_t *ud)
1964 {
1965         GtkWidget *dialog;
1966         GtkResponseType response;
1967
1968         g_debug("prefs_dialog_cb ()\n");
1969         dialog = GHB_WIDGET(ud->builder, "prefs_dialog");
1970         response = gtk_dialog_run(GTK_DIALOG(dialog));
1971         gtk_widget_hide(dialog);
1972 }
1973
1974 void
1975 presets_remove_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
1976 {
1977         GtkTreeView *treeview;
1978         GtkTreeSelection *selection;
1979         GtkTreeModel *store;
1980         GtkTreeIter iter;
1981         gchar *preset;
1982         GtkResponseType response;
1983
1984         g_debug("presets_remove_clicked_cb ()\n");
1985         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
1986         selection = gtk_tree_view_get_selection (treeview);
1987         if (gtk_tree_selection_get_selected(selection, &store, &iter))
1988         {
1989                 GtkWidget *dialog;
1990
1991                 gtk_tree_model_get(store, &iter, 0, &preset, -1);
1992                 if (!ghb_presets_is_standard(preset))
1993                 {
1994                         dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
1995                                                                         GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
1996                                                                         "Confirm deletion of preset %s.", preset);
1997                         response = gtk_dialog_run(GTK_DIALOG(dialog));
1998                         gtk_widget_destroy (dialog);
1999                         if (response == GTK_RESPONSE_YES)
2000                         {
2001                                 GtkTreeIter nextIter = iter;
2002                                 gchar *nextPreset = NULL;
2003                                 if (!gtk_tree_model_iter_next(store, &nextIter))
2004                                 {
2005                                         if (gtk_tree_model_get_iter_first(store, &nextIter))
2006                                         {
2007                                                 gtk_tree_model_get(store, &nextIter, 0, &nextPreset, -1);
2008                                         }
2009                                 }
2010                                 else
2011                                 {
2012                                         gtk_tree_model_get(store, &nextIter, 0, &nextPreset, -1);
2013                                 }
2014                                 // Remove the selected item
2015                                 // First unselect it so that selecting the new item works properly
2016                                 gtk_tree_selection_unselect_iter (selection, &iter);
2017                                 ghb_presets_remove(ud->settings, preset);
2018                                 ghb_presets_list_update(ud);
2019                                 ghb_select_preset(ud->builder, nextPreset);
2020                         }
2021                 }
2022                 else
2023                 {
2024                         dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
2025                                                                         GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
2026                                                                         "Can not delete standard preset %s.", preset);
2027                         response = gtk_dialog_run(GTK_DIALOG(dialog));
2028                         gtk_widget_destroy (dialog);
2029                 }
2030         }
2031 }
2032
2033 static void
2034 preset_update_title_deps(signal_user_data_t *ud, ghb_title_info_t *tinfo)
2035 {
2036         GtkWidget *widget;
2037
2038         ghb_ui_update_int (ud, "scale_width", tinfo->width - tinfo->crop[2] - tinfo->crop[3]);
2039         // If anamorphic or keep_aspect, the hight will be automatically calculated
2040         gboolean keep_aspect = ghb_settings_get_bool(ud->settings, "keep_aspect");
2041         gboolean anamorphic = ghb_settings_get_bool(ud->settings, "anamorphic");
2042         if (!(keep_aspect || anamorphic))
2043                 ghb_ui_update_int (ud, "scale_height", tinfo->height - tinfo->crop[0] - tinfo->crop[1]);
2044
2045         // Set the limits of cropping.  hb_set_anamorphic_size crashes if
2046         // you pass it a cropped width or height == 0.
2047         gint bound;
2048         bound = tinfo->height / 2 - 2;
2049         widget = GHB_WIDGET (ud->builder, "crop_top");
2050         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
2051         widget = GHB_WIDGET (ud->builder, "crop_bottom");
2052         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
2053         bound = tinfo->width / 2 - 2;
2054         widget = GHB_WIDGET (ud->builder, "crop_left");
2055         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
2056         widget = GHB_WIDGET (ud->builder, "crop_right");
2057         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
2058         if (ghb_settings_get_bool (ud->settings, "autocrop"))
2059         {
2060                 ghb_ui_update_int (ud, "crop_top", tinfo->crop[0]);
2061                 ghb_ui_update_int (ud, "crop_bottom", tinfo->crop[1]);
2062                 ghb_ui_update_int (ud, "crop_left", tinfo->crop[2]);
2063                 ghb_ui_update_int (ud, "crop_right", tinfo->crop[3]);
2064         }
2065 }
2066
2067 void
2068 presets_list_selection_changed_cb(GtkTreeSelection *selection, signal_user_data_t *ud)
2069 {
2070         GtkTreeModel *store;
2071         GtkTreeIter iter;
2072         gchar *preset;
2073         GtkWidget *widget;
2074         gboolean sensitive = FALSE;
2075         ghb_title_info_t tinfo;
2076         
2077         g_debug("presets_list_selection_changed_cb ()\n");
2078         if (gtk_tree_selection_get_selected(selection, &store, &iter))
2079         {
2080                 gtk_tree_model_get(store, &iter, 0, &preset, -1);
2081                 if (!ghb_presets_is_standard(preset))
2082                 {
2083                         sensitive = TRUE;
2084                 }
2085                 ud->dont_clear_presets = TRUE;
2086                 // Temporarily set the video_quality range to (0,100)
2087                 // This is needed so the video_quality value does not get
2088                 // truncated when set.  The range will be readjusted below
2089                 GtkWidget *qp = GHB_WIDGET(ud->builder, "video_quality");
2090                 gtk_range_set_range (GTK_RANGE(qp), 0, 100);
2091                 // Clear the audio list prior to changing the preset.  Existing audio
2092                 // can cause the container extension to be automatically changed when
2093                 // it shouldn't be
2094                 clear_audio_list(ud);
2095                 ghb_set_preset(ud, preset);
2096                 gint titleindex = ghb_settings_get_int(ud->settings, "title");
2097                 set_pref_audio(titleindex, ud);
2098                 ud->dont_clear_presets = FALSE;
2099                 if (ghb_get_title_info (&tinfo, titleindex))
2100                 {
2101                         preset_update_title_deps(ud, &tinfo);
2102                 }
2103                 ghb_set_scale (ud, GHB_SCALE_KEEP_NONE);
2104
2105                 gint vqmin, vqmax;
2106                 ghb_vquality_range(ud, &vqmin, &vqmax);
2107                 gtk_range_set_range (GTK_RANGE(qp), vqmin, vqmax);
2108         }
2109         else
2110         {
2111                 g_debug("No selection???  Perhaps unselected.\n");
2112         }
2113         widget = GHB_WIDGET (ud->builder, "presets_remove");
2114         gtk_widget_set_sensitive(widget, sensitive);
2115 }
2116
2117 void
2118 queue_list_selection_changed_cb(GtkTreeSelection *selection, signal_user_data_t *ud)
2119 {
2120         GtkTreeModel *store;
2121         GtkTreeIter iter, piter;
2122         
2123         g_debug("queue_list_selection_changed_cb ()\n");
2124         // A queue entry is made up of a parent and multiple
2125         // children that are visible when expanded.  When and entry
2126         // is selected, I want the parent to be selected.
2127         // This is purely cosmetic.
2128         if (gtk_tree_selection_get_selected(selection, &store, &iter))
2129         {
2130                 if (gtk_tree_model_iter_parent (store, &piter, &iter))
2131                 {
2132                         GtkTreePath *path;
2133                         GtkTreeView *treeview;
2134                         
2135                         gtk_tree_selection_select_iter (selection, &piter);
2136                         path = gtk_tree_model_get_path (store, &piter);
2137                         treeview = gtk_tree_selection_get_tree_view (selection);
2138                         // Make the parent visible in scroll window if it is not.
2139                         gtk_tree_view_scroll_to_cell (treeview, path, NULL, FALSE, 0, 0);
2140                         g_free(path);
2141                 }
2142         }
2143 }
2144
2145 static void
2146 add_to_queue_list(signal_user_data_t *ud, job_settings_t *item)
2147 {
2148         GtkTreeView *treeview;
2149         GtkTreeIter iter;
2150         GtkTreeStore *store;
2151         gchar *info;
2152         gint num_pass = 1;
2153         gint ii;
2154         GtkTreeIter citer;
2155         
2156         g_debug("update_queue_list ()\n");
2157         if (item == NULL) return;
2158         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
2159         store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
2160                 
2161         gint title = ghb_settings_get_int(item->settings, "title");
2162         gint start_chapter = ghb_settings_get_int(item->settings, "start_chapter");
2163         gint end_chapter = ghb_settings_get_int(item->settings, "end_chapter");
2164         gboolean pass2 = ghb_settings_get_bool(item->settings, "two_pass");
2165         const gchar *vol_name = ghb_settings_get_string(item->settings, "volume_label");
2166         if (vol_name == NULL)
2167                 vol_name = "No Title";
2168         info = g_strdup_printf 
2169                 (
2170                  "<big><b>%s</b></big> (Title %d, Chapters %d through %d, %d Video %s)",
2171                  vol_name, title+1, start_chapter, end_chapter, 
2172                  pass2 ? 2:1, pass2 ? "Passes":"Pass");
2173
2174         gtk_tree_store_append(store, &iter, NULL);
2175         gtk_tree_store_set(store, &iter, 0, "hb-queue-job", 1, info, 2, "hb-queue-delete", -1);
2176         g_free(info);
2177         
2178         const gchar *vcodec = ghb_settings_get_option(item->settings, "video_codec");
2179         const gchar *container = ghb_settings_get_option(item->settings, "container");
2180         const gchar *acodec = ghb_settings_get_option(item->settings, "audio_codec");
2181         const gchar *dest = ghb_settings_get_string(item->settings, "destination");
2182         const gchar *preset = ghb_settings_get_string(item->settings, "preset");
2183         info = g_strdup_printf 
2184                 (
2185                  "<b>Preset:</b> %s\n"
2186                  "<b>Format:</b> %s Container, %s Video + %s Audio\n"
2187                  "<b>Destination:</b> %s",
2188                  preset, container, vcodec, acodec, dest);
2189
2190         gtk_tree_store_append(store, &citer, &iter);
2191         gtk_tree_store_set(store, &citer, 1, info, -1);
2192         g_free(info);
2193
2194         gint width = ghb_settings_get_int(item->settings, "scale_width");
2195         gint height = ghb_settings_get_int(item->settings, "scale_height");
2196         gboolean anamorphic = ghb_settings_get_bool(item->settings, "anamorphic");
2197         gboolean round_dim = ghb_settings_get_bool(item->settings, "round_dimensions");
2198         gboolean keep_aspect = ghb_settings_get_bool(item->settings, "keep_aspect");
2199         gchar *aspect_desc;
2200         if (anamorphic)
2201         {
2202                 if (round_dim)
2203                 {
2204                         aspect_desc = "(Anamorphic)";
2205                 }
2206                 else
2207                 {
2208                         aspect_desc = "(Strict Anamorphic)";
2209                 }
2210         }
2211         else
2212         {
2213                 if (keep_aspect)
2214                 {
2215                         aspect_desc = "(Aspect Preserved)";
2216                 }
2217                 else
2218                 {
2219                         aspect_desc = "(Aspect Lost)";
2220                 }
2221         }
2222         gboolean vqtype = ghb_settings_get_bool(item->settings, "vquality_type_constant");
2223         gint vqvalue = 0;
2224         gchar *vq_desc = "Error";
2225         if (!vqtype)
2226         {
2227                 vqtype = ghb_settings_get_bool(item->settings, "vquality_type_target");
2228                 if (!vqtype)
2229                 {
2230                         // Has to be bitrate
2231                         vqvalue = ghb_settings_get_int(item->settings, "video_bitrate");
2232                         vq_desc = "kbps";
2233                 }
2234                 else
2235                 {
2236                         // Target file size
2237                         vqvalue = ghb_settings_get_int(item->settings, "video_target");
2238                         vq_desc = "MB";
2239                 }
2240         }
2241         else
2242         {
2243                 // Constant quality
2244                 vqvalue = ghb_settings_get_int(item->settings, "video_quality");
2245                 vq_desc = "% Constant Quality";
2246         }
2247         const gchar *fps = ghb_settings_get_string(item->settings, "framerate");
2248         const gchar *vcodec_abbr = ghb_settings_get_short_opt(item->settings, "video_codec");
2249         gchar *extra_opts;
2250         if (strcmp(vcodec_abbr, "x264") == 0)
2251         {
2252                 gchar *x264opts = ghb_build_x264opts_string(item->settings);
2253                 g_debug("xopts (%s)\n", x264opts);
2254                 extra_opts = g_strdup_printf ("\n<b>x264 Options:</b> %s", x264opts);
2255                 g_free(x264opts);
2256         }
2257         else
2258         {
2259                 extra_opts = g_strdup("");
2260         }
2261         gboolean turbo = ghb_settings_get_bool (item->settings, "turbo");
2262         gchar *turbo_desc = "\n<b>Turbo:</b> Off";;
2263         if (turbo)
2264         {
2265                 turbo_desc = "\n<b>Turbo:</b> On";
2266         }
2267         num_pass = pass2 ? 2 : 1;
2268         for (ii = 0; ii < num_pass; ii++)
2269         {
2270                 gboolean final = (ii == (num_pass - 1));
2271                 GString *pass = g_string_new("");
2272                 g_string_append_printf( pass,
2273                         "<b>%s Pass</b>\n"
2274                         "<b>Picture:</b> %d x %d %s\n"
2275                         "<b>Video:</b> %s, %d %s, %s fps"
2276                         "%s",
2277                          ii ? "2nd":"1st", width, height, aspect_desc,
2278                          vcodec, vqvalue, vq_desc, fps, 
2279                          final ? extra_opts : turbo_desc);
2280
2281                 if (final)
2282                 {
2283                         // Add the audios
2284                         GSList *link = item->audio_settings;
2285                         while (link)
2286                         {
2287                                 GHashTable *asettings = (GHashTable*)link->data;
2288                                 const gchar *acodec = ghb_settings_get_option(asettings, "audio_codec");
2289                                 const gchar *bitrate = ghb_settings_get_string(asettings, "audio_bitrate");
2290                                 const gchar *samplerate = ghb_settings_get_string(asettings, "audio_rate");
2291                                 gint track = ghb_settings_get_int(asettings, "audio_track");
2292                                 const gchar *mix = ghb_settings_get_option(asettings, "audio_mix");
2293                                 g_string_append_printf(pass,
2294                                         "\n<b>Audio:</b> %s, %s kbps, %s kHz, Track %d: %s",
2295                                          acodec, bitrate, samplerate, track+1, mix);
2296                                 link = link->next;
2297                         }
2298                 }
2299                 info = g_string_free(pass, FALSE);
2300                 gtk_tree_store_append(store, &citer, &iter);
2301                 gtk_tree_store_set(store, &citer, 0, ii ? "hb-queue-pass2" : "hb-queue-pass1", 1, info, -1);
2302                 g_free(info);
2303         }
2304         g_free(extra_opts);
2305 }
2306
2307 gboolean
2308 ghb_message_dialog(GtkMessageType type, const gchar *message, const gchar *no, const gchar *yes)
2309 {
2310         GtkWidget *dialog;
2311         GtkResponseType response;
2312                         
2313         // Toss up a warning dialog
2314         dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
2315                                                         type, GTK_BUTTONS_NONE,
2316                                                         message);
2317         gtk_dialog_add_buttons( GTK_DIALOG(dialog), 
2318                                                    no, GTK_RESPONSE_NO,
2319                                                    yes, GTK_RESPONSE_YES, NULL);
2320         response = gtk_dialog_run(GTK_DIALOG(dialog));
2321         gtk_widget_destroy (dialog);
2322         if (response == GTK_RESPONSE_NO)
2323         {
2324                 return FALSE;
2325         }
2326         return TRUE;
2327 }
2328
2329 static gint64
2330 estimate_file_size(signal_user_data_t *ud)
2331 {
2332         ghb_title_info_t tinfo;
2333         gint duration;
2334         gint bitrate;
2335         gint64 size;
2336         gint titleindex = ghb_settings_get_int(ud->settings, "title");
2337         if (titleindex < 0) return 0;
2338                         
2339         if (!ghb_get_title_info(&tinfo, titleindex)) return 0;
2340         duration = ((tinfo.hours*60)+tinfo.minutes)*60+tinfo.seconds;
2341         bitrate = ghb_guess_bitrate(ud->settings);
2342         size = (gint64)duration * (gint64)bitrate/8;
2343         return size;
2344 }
2345
2346 #define DISK_FREE_THRESH        (1024L*1024L*1024L*3)
2347
2348 static gboolean
2349 validate_settings(signal_user_data_t *ud)
2350 {
2351         // Check to see if the dest file exists or is
2352         // already in the queue
2353         gchar *message;
2354         gint titleindex = ghb_settings_get_int(ud->settings, "title");
2355         if (titleindex < 0) return FALSE;
2356         const gchar *dest = ghb_settings_get_string(ud->settings, "destination");
2357         GSList *link = ud->queue;
2358         while (link != NULL)
2359         {
2360                 job_settings_t *item;
2361                 const gchar *filename;
2362                 item = (job_settings_t*)link->data;
2363                 filename = ghb_settings_get_string(item->settings, "destination");
2364                 if (strcmp(dest, filename) == 0)
2365                 {
2366                         message = g_strdup_printf(
2367                                                 "Destination: %s\n\n"
2368                                                 "Another queued job has specified the same destination.\n"
2369                                                 "Do you want to overwrite?",
2370                                                 dest);
2371                         if (!ghb_message_dialog(GTK_MESSAGE_QUESTION, message, "Cancel", "Overwrite"))
2372                         {
2373                                 g_free(message);
2374                                 return FALSE;
2375                         }
2376                         g_free(message);
2377                         break;
2378                 }
2379                 link = link->next;
2380         }
2381         gchar *destdir = g_path_get_dirname(dest);
2382         if (!g_file_test(destdir, G_FILE_TEST_IS_DIR))
2383         {
2384                 message = g_strdup_printf(
2385                                         "Destination: %s\n\n"
2386                                         "This is not a valid directory.",
2387                                         destdir);
2388                 ghb_message_dialog(GTK_MESSAGE_ERROR, message, "Cancel", NULL);
2389                 g_free(message);
2390                 g_free(destdir);
2391                 return FALSE;
2392         }
2393         if (g_access(destdir, R_OK|W_OK) != 0)
2394         {
2395                 message = g_strdup_printf(
2396                                         "Destination: %s\n\n"
2397                                         "Can not read or write the directory.",
2398                                         destdir);
2399                 ghb_message_dialog(GTK_MESSAGE_ERROR, message, "Cancel", NULL);
2400                 g_free(message);
2401                 g_free(destdir);
2402                 return FALSE;
2403         }
2404         GFile *gfile;
2405         GFileInfo *info;
2406         guint64 size;
2407         gchar *resolved = ghb_resolve_symlink(destdir);
2408
2409         gfile = g_file_new_for_path(resolved);
2410         info = g_file_query_filesystem_info(gfile, 
2411                                                 G_FILE_ATTRIBUTE_FILESYSTEM_FREE, NULL, NULL);
2412         if (info != NULL)
2413         {
2414                 if (g_file_info_has_attribute(info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE))
2415                 {
2416                         size = g_file_info_get_attribute_uint64(info, 
2417                                                                         G_FILE_ATTRIBUTE_FILESYSTEM_FREE);
2418                         
2419                         gint64 fsize = estimate_file_size(ud);
2420                         if (size < fsize)
2421                         {
2422                                 message = g_strdup_printf(
2423                                                         "Destination filesystem is almost full: %uM free\n\n"
2424                                                         "Encode may be incomplete if you proceed.\n",
2425                                                         (guint)(size / (1024L*1024L)));
2426                                 if (!ghb_message_dialog(GTK_MESSAGE_QUESTION, message, "Cancel", "Proceed"))
2427                                 {
2428                                         g_free(message);
2429                                         return FALSE;
2430                                 }
2431                                 g_free(message);
2432                         }
2433                 }
2434                 g_object_unref(info);
2435         }
2436         g_object_unref(gfile);
2437         g_free(resolved);
2438         g_free(destdir);
2439         if (g_file_test(dest, G_FILE_TEST_EXISTS))
2440         {
2441                 message = g_strdup_printf(
2442                                         "Destination: %s\n\n"
2443                                         "File already exhists.\n"
2444                                         "Do you want to overwrite?",
2445                                         dest);
2446                 if (!ghb_message_dialog(GTK_MESSAGE_QUESTION, message, "Cancel", "Overwrite"))
2447                 {
2448                         g_free(message);
2449                         return FALSE;
2450                 }
2451                 g_free(message);
2452                 g_unlink(dest);
2453         }
2454         // Validate video quality is in a reasonable range
2455         if (!ghb_validate_vquality(ud->settings))
2456         {
2457                 return FALSE;
2458         }
2459         // Validate audio settings
2460         if (!ghb_validate_audio(ud))
2461         {
2462                 return FALSE;
2463         }
2464         // Validate video settings
2465         if (!ghb_validate_video(ud))
2466         {
2467                 return FALSE;
2468         }
2469         // Validate container settings
2470         if (!ghb_validate_container(ud))
2471         {
2472                 return FALSE;
2473         }
2474         // Validate filter settings
2475         if (!ghb_validate_filters(ud))
2476         {
2477                 return FALSE;
2478         }
2479         audio_list_refresh(ud);
2480         return TRUE;
2481 }
2482
2483 static gboolean
2484 queue_add(signal_user_data_t *ud)
2485 {
2486         // Add settings to the queue
2487         job_settings_t *queue_item;
2488         GSList *link;
2489         static gint unique_id = 0;
2490         
2491         g_debug("queue_add ()\n");
2492         if (!validate_settings(ud))
2493         {
2494                 return FALSE;
2495         }
2496         // Make a copy of current settings to be used for the new job
2497         queue_item = g_malloc(sizeof(job_settings_t));
2498         queue_item->settings = ghb_settings_dup(ud->settings);
2499         queue_item->audio_settings = NULL;
2500         link = ud->audio_settings;
2501         while (link != NULL)
2502         {
2503                 GHashTable *asettings;
2504                 asettings = ghb_settings_dup((GHashTable*)link->data);
2505                 queue_item->audio_settings =
2506                         g_slist_append(queue_item->audio_settings, asettings);
2507                 link = g_slist_next(link);
2508         }
2509         queue_item->chapter_list = g_strdupv(ud->chapter_list);
2510         ud->queue = g_slist_append(ud->queue, queue_item);
2511         add_to_queue_list(ud, queue_item);
2512         ghb_add_job (queue_item, unique_id);
2513         queue_item->unique_id = unique_id;
2514         queue_item->status = GHB_QUEUE_PENDING;
2515         unique_id++;
2516         return TRUE;
2517 }
2518
2519 void
2520 queue_add_clicked_cb(GtkWidget *widget, signal_user_data_t *ud)
2521 {
2522         g_debug("queue_add_clicked_cb ()\n");
2523         queue_add(ud);
2524 }
2525
2526 static gboolean
2527 cancel_encode(const gchar *extra_msg)
2528 {
2529         GtkWidget *dialog;
2530         GtkResponseType response;
2531         
2532         if (extra_msg == NULL) extra_msg = "";
2533         // Toss up a warning dialog
2534         dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
2535                                 GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE,
2536                                 "%sYour movie will be lost if you don't continue encoding.",
2537                                 extra_msg);
2538         gtk_dialog_add_buttons( GTK_DIALOG(dialog), 
2539                                                    "Continue Encoding", GTK_RESPONSE_NO,
2540                                                    "Stop Encoding", GTK_RESPONSE_YES, NULL);
2541         response = gtk_dialog_run(GTK_DIALOG(dialog));
2542         gtk_widget_destroy (dialog);
2543         if (response == GTK_RESPONSE_NO) return FALSE;
2544         ghb_stop_queue();
2545         return TRUE;
2546 }
2547
2548 void
2549 queue_remove_clicked_cb(GtkWidget *widget, gchar *path, signal_user_data_t *ud)
2550 {
2551         GtkTreeView *treeview;
2552         GtkTreePath *treepath;
2553         GtkTreeModel *store;
2554         GtkTreeIter iter;
2555         gint row;
2556         GSList *link;
2557         gint *indices;
2558         job_settings_t *queue_item;
2559         gint unique_id;
2560
2561         g_debug("queue_remove_clicked_cb ()\n");
2562         g_debug("ud %p\n", ud);
2563         g_debug("ud->builder %p\n", ud->builder);
2564
2565         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
2566         store = gtk_tree_view_get_model(treeview);
2567         treepath = gtk_tree_path_new_from_string (path);
2568         if (gtk_tree_model_get_iter(store, &iter, treepath))
2569         {
2570                 // Find the entry in the queue
2571                 indices = gtk_tree_path_get_indices (treepath);
2572                 row = indices[0];
2573                 // Can only free the treepath After getting what I need from
2574                 // indices since this points into treepath somewhere.
2575                 gtk_tree_path_free (treepath);
2576                 if (row < 0) return;
2577                 link = g_slist_nth(ud->queue, row);
2578                 if (link == NULL) return;
2579                 queue_item = (job_settings_t*)link->data;
2580                 if (queue_item->status == GHB_QUEUE_RUNNING)
2581                 {
2582                         // Ask if wants to stop encode.
2583                         if (!cancel_encode(NULL))
2584                         {
2585                                 return;
2586                         }
2587                 }
2588                 // Remove the selected item
2589                 g_debug(" should be removing from treestore\n");
2590                 gtk_tree_store_remove(GTK_TREE_STORE(store), &iter);
2591                 // Remove the corresponding item from the queue list
2592                 ud->queue = g_slist_remove_link(ud->queue, link);
2593                 g_slist_free_1(link);
2594                 g_hash_table_destroy(queue_item->settings);
2595                 link = queue_item->audio_settings;
2596                 while (link != NULL)
2597                 {
2598                         GSList *nextlink;
2599                         g_hash_table_destroy((GHashTable*)link->data);
2600                         nextlink = g_slist_next(link);
2601                         g_slist_free_1(link);
2602                         link = nextlink;
2603                 }
2604                 g_strfreev (queue_item->chapter_list);
2605                 unique_id = queue_item->unique_id;
2606                 g_free(queue_item);
2607                 ghb_remove_job(unique_id);
2608         }
2609         else
2610         {       
2611                 gtk_tree_path_free (treepath);
2612         }
2613 }
2614
2615 static gint
2616 find_queue_item(GSList *queue, gint unique_id, job_settings_t **job)
2617 {
2618         job_settings_t *js;
2619         gint index = -1;
2620         
2621         while (queue != NULL)
2622         {
2623                 index++;
2624                 js = (job_settings_t*)queue->data;
2625                 if (js->unique_id == unique_id)
2626                 {
2627                         *job = js;
2628                         return index;
2629                 }
2630                 queue = queue->next;
2631         }
2632         *job = NULL;
2633         return index;
2634 }
2635
2636 static void
2637 queue_buttons_grey(signal_user_data_t *ud, gboolean working)
2638 {
2639         GtkWidget *widget;
2640         GtkAction *action;
2641         gint titleindex = ghb_settings_get_int(ud->settings, "title");
2642         gboolean title_ok = (titleindex >= 0);
2643         widget = GHB_WIDGET (ud->builder, "queue_start1");
2644         gtk_widget_set_sensitive (widget, !working && title_ok);
2645         widget = GHB_WIDGET (ud->builder, "queue_start2");
2646         gtk_widget_set_sensitive (widget, !working && title_ok);
2647         action = GHB_ACTION (ud->builder, "queue_start_menu");
2648         gtk_action_set_sensitive (action, !working && title_ok);
2649         widget = GHB_WIDGET (ud->builder, "queue_pause1");
2650         gtk_widget_set_sensitive (widget, working);
2651         widget = GHB_WIDGET (ud->builder, "queue_pause2");
2652         gtk_widget_set_sensitive (widget, working);
2653         action = GHB_ACTION (ud->builder, "queue_pause_menu");
2654         gtk_action_set_sensitive (action, working);
2655         widget = GHB_WIDGET (ud->builder, "queue_stop");
2656         gtk_widget_set_sensitive (widget, working);
2657         action = GHB_ACTION (ud->builder, "queue_stop_menu");
2658         gtk_action_set_sensitive (action, working);
2659 }
2660
2661 void queue_start_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud);
2662
2663 static void
2664 ghb_backend_events(signal_user_data_t *ud)
2665 {
2666         ghb_status_t status;
2667         gchar *status_str;
2668         GtkProgressBar *progress;
2669         gint titleindex;
2670         job_settings_t *js;
2671         static gint current_id = -1;
2672         gint index;
2673         GtkTreeView *treeview;
2674         GtkTreeStore *store;
2675         GtkTreeIter iter;
2676         static gint working = 0;
2677         static gboolean work_started = FALSE;
2678         
2679         ghb_track_status();
2680         ghb_get_status(&status);
2681         progress = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "progressbar"));
2682         if (status.state & GHB_STATE_SCANNING)
2683         {
2684                 status_str = g_strdup_printf ("Scanning title %d of %d...", 
2685                                                                   status.title_cur, status.title_count );
2686                 gtk_progress_bar_set_text (progress, status_str);
2687                 g_free(status_str);
2688                 if (status.title_count > 0)
2689                 {
2690                         gtk_progress_bar_set_fraction (progress, 
2691                                 (gdouble)status.title_cur / status.title_count);
2692                 }
2693         }
2694         else if (status.state & GHB_STATE_SCANDONE)
2695         {
2696                 status_str = g_strdup_printf ("Scan done"); 
2697                 gtk_progress_bar_set_text (progress, status_str);
2698                 g_free(status_str);
2699                 gtk_progress_bar_set_fraction (progress, 1.0);
2700
2701                 ghb_title_info_t tinfo;
2702                         
2703                 ghb_update_ui_combo_box(ud->builder, "title", 0, FALSE);
2704                 titleindex = ghb_longest_title();
2705                 ghb_ui_update_int(ud, "title", titleindex);
2706
2707                 // Are there really any titles.
2708                 if (!ghb_get_title_info(&tinfo, titleindex))
2709                 {
2710                         GtkProgressBar *progress;
2711                         progress = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "progressbar"));
2712                         gtk_progress_bar_set_fraction (progress, 0);
2713                         gtk_progress_bar_set_text (progress, "No Source");
2714                 }
2715                 ghb_clear_state(GHB_STATE_SCANDONE);
2716                 queue_buttons_grey(ud, (0 != (status.state & GHB_STATE_WORKING)));
2717         }
2718         else if (status.state & GHB_STATE_WORKING)
2719         {
2720                 if(status.seconds > -1)
2721                 {
2722                         status_str= g_strdup_printf(
2723                                 "Encoding: task %d of %d, %.2f %%"
2724                                 " (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)",
2725                                 status.job_cur, status.job_count, 
2726                                 100.0 * status.progress,
2727                                 status.rate_cur, status.rate_avg, status.hours, 
2728                                 status.minutes, status.seconds );
2729                 }
2730                 else
2731                 {
2732                         status_str= g_strdup_printf(
2733                                 "Encoding: task %d of %d, %.2f %%",
2734                                 status.job_cur, status.job_count, 
2735                                 100.0 * status.progress );
2736                 }
2737                 gtk_progress_bar_set_text (progress, status_str);
2738                 gtk_progress_bar_set_fraction (progress, status.progress);
2739                 g_free(status_str);
2740         }
2741         else if (status.state & GHB_STATE_WORKDONE)
2742         {
2743                 work_started = FALSE;
2744                 queue_buttons_grey(ud, FALSE);
2745                 index = find_queue_item(ud->queue, current_id, &js);
2746                 treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
2747                 store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
2748                 switch( status.error )
2749                 {
2750                         case GHB_ERROR_NONE:
2751                                 gtk_progress_bar_set_text( progress, "Rip done!" );
2752                                 if (js != NULL)
2753                                 {
2754                                         js->status = GHB_QUEUE_DONE;
2755                                         gchar *path = g_strdup_printf ("%d", index);
2756                                         if (gtk_tree_model_get_iter_from_string ( GTK_TREE_MODEL(store), &iter, path))
2757                                         {
2758                                                 gtk_tree_store_set(store, &iter, 0, "hb-complete", -1);
2759                                         }
2760                                         g_free(path);
2761                                 }
2762                                 break;
2763                         case GHB_ERROR_CANCELED:
2764                                 gtk_progress_bar_set_text( progress, "Rip canceled." );
2765                                 if (js != NULL)
2766                                 {
2767                                         js->status = GHB_QUEUE_CANCELED;
2768                                         gchar *path = g_strdup_printf ("%d", index);
2769                                         if (gtk_tree_model_get_iter_from_string ( GTK_TREE_MODEL(store), &iter, path))
2770                                         {
2771                                                 gtk_tree_store_set(store, &iter, 0, "hb-canceled", -1);
2772                                         }
2773                                         g_free(path);
2774                                 }
2775                                 break;
2776                         default:
2777                                 gtk_progress_bar_set_text( progress, "Rip failed.");
2778                                 if (js != NULL)
2779                                 {
2780                                         js->status = GHB_QUEUE_CANCELED;
2781                                         gchar *path = g_strdup_printf ("%d", index);
2782                                         if (gtk_tree_model_get_iter_from_string ( GTK_TREE_MODEL(store), &iter, path))
2783                                         {
2784                                                 gtk_tree_store_set(store, &iter, 0, "hb-canceled", -1);
2785                                         }
2786                                         g_free(path);
2787                                 }
2788                 }
2789                 current_id = -1;
2790                 gtk_progress_bar_set_fraction (progress, 1.0);
2791                 ghb_clear_state(GHB_STATE_WORKDONE);
2792         }
2793         else if (status.state & GHB_STATE_PAUSED)
2794         {
2795                 status_str = g_strdup_printf ("Paused"); 
2796                 gtk_progress_bar_set_text (progress, status_str);
2797                 g_free(status_str);
2798         }
2799         else if (status.state & GHB_STATE_MUXING)
2800         {
2801                 gtk_progress_bar_set_text(progress, "Muxing: this may take awhile...");
2802         }
2803         if (status.state & GHB_STATE_WORKING)
2804         {
2805                 if (!work_started)
2806                 {
2807                         work_started = TRUE;
2808                         queue_buttons_grey(ud, TRUE);
2809                 }
2810                 if (status.unique_id != current_id)
2811                 {
2812                         index = find_queue_item(ud->queue, current_id, &js);
2813                         if (js != NULL)
2814                         {
2815                                 js->status = GHB_QUEUE_DONE;
2816                                 treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
2817                                 store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
2818                                 gchar *path = g_strdup_printf ("%d", index);
2819                                 if (gtk_tree_model_get_iter_from_string ( GTK_TREE_MODEL(store), &iter, path))
2820                                 {
2821                                         gtk_tree_store_set(store, &iter, 0, "hb-complete", -1);
2822                                 }
2823                                 g_free(path);
2824                         }
2825
2826                         index = find_queue_item(ud->queue, status.unique_id, &js);
2827                         if (js != NULL)
2828                         {
2829                                 js->status = GHB_QUEUE_RUNNING;
2830                                 current_id = status.unique_id;
2831                         }
2832                 }
2833                 index = find_queue_item(ud->queue, status.unique_id, &js);
2834                 if (index >= 0)
2835                 {
2836                         gchar working_icon[] = "hb-working0";
2837                         working_icon[10] = '0' + working;
2838                         working = (working+1) % 6;
2839                         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
2840                         store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
2841                         gchar *path = g_strdup_printf ("%d", index);
2842                         if (gtk_tree_model_get_iter_from_string ( GTK_TREE_MODEL(store), &iter, path))
2843                         {
2844                                 gtk_tree_store_set(store, &iter, 0, working_icon, -1);
2845                         }
2846                         g_free(path);
2847                 }
2848         }
2849 }
2850
2851 gboolean
2852 ghb_timer_cb(gpointer data)
2853 {
2854         signal_user_data_t *ud = (signal_user_data_t*)data;
2855
2856         ghb_backend_events(ud);
2857         if (update_default_destination)
2858         {
2859                 const gchar *dest = ghb_settings_get_string(ud->settings, "destination");
2860                 gchar *dest_dir = g_path_get_dirname (dest);
2861                 const gchar *def_dest = ghb_settings_get_string(ud->settings, "destination_dir");
2862                 if (strcmp(dest_dir, def_dest) != 0)
2863                 {
2864                         ghb_settings_set_string (ud->settings, "destination_dir", dest_dir);
2865                         ghb_pref_save (ud->settings, "destination_dir");
2866                 }
2867                 update_default_destination = FALSE;
2868         }
2869         if (update_preview)
2870         {
2871                 set_preview_image (ud);
2872                 update_preview = FALSE;
2873         }
2874         return TRUE;
2875 }
2876
2877 gboolean
2878 ghb_log_cb(GIOChannel *source, GIOCondition cond, gpointer data)
2879 {
2880         gchar *text = NULL;
2881         gsize length;
2882         GtkTextView *textview;
2883         GtkTextBuffer *buffer;
2884         GtkTextIter iter;
2885         GtkTextMark *mark;
2886         GError *gerror = NULL;
2887         GIOStatus status;
2888         
2889         signal_user_data_t *ud = (signal_user_data_t*)data;
2890
2891         status = g_io_channel_read_line (source, &text, &length, NULL, &gerror);
2892         if (text != NULL)
2893         {
2894                 GdkWindow *window;
2895                 gint width, height;
2896                 gint x, y;
2897                 gboolean bottom = FALSE;
2898
2899                 textview = GTK_TEXT_VIEW(GHB_WIDGET (ud->builder, "activity_view"));
2900                 buffer = gtk_text_view_get_buffer (textview);
2901                 // I would like to auto-scroll the window when the scrollbar
2902                 // is at the bottom, 
2903                 // must determining whether the insert point is at
2904                 // the bottom of the window 
2905                 window = gtk_text_view_get_window(textview, GTK_TEXT_WINDOW_TEXT);
2906                 if (window != NULL)
2907                 {
2908                         gdk_drawable_get_size(GDK_DRAWABLE(window), &width, &height);
2909                         gtk_text_view_window_to_buffer_coords(textview, 
2910                                 GTK_TEXT_WINDOW_TEXT, width, height, &x, &y);
2911                         gtk_text_view_get_iter_at_location(textview, &iter, x, y);
2912                         if (gtk_text_iter_is_end(&iter))
2913                         {
2914                                 bottom = TRUE;
2915                         }
2916                 }
2917                 else
2918                 {
2919                         // If the window isn't available, assume bottom
2920                         bottom = TRUE;
2921                 }
2922                 gtk_text_buffer_get_end_iter(buffer, &iter);
2923                 gtk_text_buffer_insert(buffer, &iter, text, -1);
2924                 if (bottom)
2925                 {
2926                         //gtk_text_view_scroll_to_iter(textview, &iter, 0, FALSE, 0, 0);
2927                         mark = gtk_text_buffer_get_insert (buffer);
2928                         gtk_text_view_scroll_mark_onscreen(textview, mark);
2929                 }
2930                 g_io_channel_write_chars (ud->activity_log, text, length, &length, NULL);
2931                 g_free(text);
2932         }
2933         if (status != G_IO_STATUS_NORMAL)
2934         {
2935                 // This should never happen, but if it does I would get into an
2936                 // infinite loop.  Returning false removes this callback.
2937                 g_warning("Error while reading activity from pipe\n");
2938                 if (gerror != NULL)
2939                 {
2940                         g_warning("%s\n", gerror->message);
2941                         g_error_free (gerror);
2942                 }
2943                 return FALSE;
2944         }
2945         if (gerror != NULL)
2946                 g_error_free (gerror);
2947         return TRUE;
2948 }
2949
2950 void
2951 about_activate_cb(GtkWidget *xwidget, signal_user_data_t *ud)
2952 {
2953         GtkWidget *widget = GHB_WIDGET (ud->builder, "hb_about");
2954         gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(widget), ghb_version());
2955         gtk_widget_show (widget);
2956 }
2957
2958 void
2959 guide_activate_cb(GtkWidget *xwidget, signal_user_data_t *ud)
2960 {
2961         gboolean result;
2962         char *argv[] = 
2963                 {"xdg-open","http://trac.handbrake.fr/wiki/HandBrakeGuide",NULL,NULL};
2964         result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
2965                                 NULL, NULL, NULL);
2966         if (result) return;
2967
2968         argv[0] = "gnome-open";
2969         result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
2970                                 NULL, NULL, NULL);
2971         if (result) return;
2972
2973         argv[0] = "kfmclient";
2974         argv[1] = "exec";
2975         argv[2] = "http://trac.handbrake.fr/wiki/HandBrakeGuide";
2976         result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
2977                                 NULL, NULL, NULL);
2978         if (result) return;
2979
2980         argv[0] = "firefox";
2981         argv[1] = "http://trac.handbrake.fr/wiki/HandBrakeGuide";
2982         argv[2] = NULL;
2983         result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
2984                                 NULL, NULL, NULL);
2985         if (result) return;
2986 }
2987
2988 void
2989 hb_about_response_cb(GtkWidget *widget, gint response, signal_user_data_t *ud)
2990 {
2991         gtk_widget_hide (widget);
2992 }
2993
2994 void
2995 show_activity_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
2996 {
2997         GtkWidget *widget = GHB_WIDGET (ud->builder, "activity_window");
2998         gtk_widget_show (widget);
2999 }
3000
3001 void
3002 show_queue_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
3003 {
3004         GtkWidget *widget = GHB_WIDGET (ud->builder, "queue_window");
3005         gtk_widget_show (widget);
3006 }
3007
3008 void
3009 show_presets_toggled_cb(GtkToggleButton *button, signal_user_data_t *ud)
3010 {
3011         GtkWidget *widget;
3012         GtkWindow *hb_window;
3013         
3014         g_debug("show_presets_clicked_cb ()\n");
3015         widget = GHB_WIDGET (ud->builder, "presets_frame");
3016         if (gtk_toggle_button_get_active(button))
3017         {
3018                 gtk_widget_show_now(widget);
3019         }
3020         else
3021         {
3022                 gtk_widget_hide(widget);
3023                 hb_window = GTK_WINDOW(GHB_WIDGET (ud->builder, "hb_window"));
3024                 gtk_window_resize(hb_window, 16, 16);
3025         }
3026         ghb_widget_to_setting(ud->settings, GTK_WIDGET(button));
3027         ghb_pref_save(ud->settings, "show_presets");
3028 }
3029
3030 void
3031 presets_frame_size_allocate_cb(GtkWidget *widget, GtkAllocation *allocation, signal_user_data_t *ud)
3032 {
3033         GtkTreeView *treeview;
3034         GtkTreeSelection *selection;
3035         GtkTreeModel *store;
3036         GtkTreeIter iter;
3037         
3038         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "presets_list"));
3039         selection = gtk_tree_view_get_selection(treeview);
3040         if (gtk_tree_selection_get_selected(selection, &store, &iter))
3041         {
3042                 GtkTreePath *path;
3043                 path = gtk_tree_model_get_path (store, &iter);
3044                 // Make the parent visible in scroll window if it is not.
3045                 gtk_tree_view_scroll_to_cell (treeview, path, NULL, FALSE, 0, 0);
3046                 g_free(path);
3047         }
3048 }
3049
3050 static void
3051 update_chapter_list(signal_user_data_t *ud)
3052 {
3053         GtkTreeView *treeview;
3054         GtkTreeIter iter;
3055         GtkListStore *store;
3056         gboolean done;
3057         gchar **chapters;
3058         gint titleindex, ii;
3059         
3060         g_debug("update_chapter_list ()\n");
3061         titleindex = ghb_settings_get_index(ud->settings, "title");
3062         chapters = ghb_get_chapters(titleindex);
3063         if (ud->chapter_list != NULL)
3064                 g_strfreev (ud->chapter_list);
3065         ud->chapter_list = chapters;
3066         
3067         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "chapters_list"));
3068         store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview));
3069         ii = 0;
3070         if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter))
3071         {
3072                 do
3073                 {
3074                         if (chapters != NULL && chapters[ii])
3075                         {
3076                                 // Update row with settings data
3077                                 g_debug("Updating row\n");
3078                                 gtk_list_store_set(store, &iter, 
3079                                         0, ii+1,
3080                                         1, chapters[ii],
3081                                         2, TRUE,
3082                                         -1);
3083                                 ii++;
3084                                 done = !gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
3085                         }
3086                         else
3087                         {
3088                                 // No more settings data, remove row
3089                                 g_debug("Removing row\n");
3090                                 done = !gtk_list_store_remove(store, &iter);
3091                         }
3092                 } while (!done);
3093         }
3094         while (chapters != NULL && chapters[ii])
3095         {
3096                 // Additional settings, add row
3097                 g_debug("Adding row\n");
3098                 g_debug("%d -- %s\n", ii, chapters[ii]);
3099                 gtk_list_store_append(store, &iter);
3100                 gtk_list_store_set(store, &iter, 
3101                         0, ii+1,
3102                         1, chapters[ii],
3103                         2, TRUE,
3104                         -1);
3105                 ii++;
3106         }
3107 }
3108
3109 void
3110 chapter_edited_cb(GtkCellRendererText *cell, gchar *path, gchar *text, signal_user_data_t *ud)
3111 {
3112         GtkTreePath *treepath;
3113         GtkListStore *store;
3114         GtkTreeView *treeview;
3115         GtkTreeViewColumn *column;
3116         GtkTreeIter iter;
3117         gint index;
3118         
3119         g_debug("chapter_edited_cb ()\n");
3120         g_debug("path (%s)\n", path);
3121         g_debug("text (%s)\n", text);
3122         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "chapters_list"));
3123         store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview));
3124         treepath = gtk_tree_path_new_from_string (path);
3125         gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, treepath);
3126         gtk_tree_path_free (treepath);
3127         gtk_list_store_set(store, &iter, 
3128                 1, text,
3129                 2, TRUE,
3130                 -1);
3131         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 0, &index, -1);
3132         g_free(ud->chapter_list[index-1]);
3133         ud->chapter_list[index-1] = g_strdup(text);
3134         if (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter))
3135         {
3136                 column = gtk_tree_view_get_column(treeview, 1);
3137                 treepath = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &iter);
3138                 gtk_tree_view_set_cursor(treeview, treepath, column, TRUE);
3139                 gtk_tree_path_free (treepath);
3140         }
3141 }
3142
3143 void
3144 chapter_list_selection_changed_cb(GtkTreeSelection *selection, signal_user_data_t *ud)
3145 {
3146         GtkTreeModel *store;
3147         GtkTreeIter iter;
3148         
3149         g_debug("chapter_list_selection_changed_cb ()\n");
3150         if (gtk_tree_selection_get_selected(selection, &store, &iter))
3151         {
3152                 // What to do??
3153         }
3154 }
3155
3156 void
3157 queue_list_size_allocate_cb(GtkWidget *widget, GtkAllocation *allocation, GtkCellRenderer *cell)
3158 {
3159         GtkTreeViewColumn *column;
3160         gint width;
3161         
3162         column = gtk_tree_view_get_column (GTK_TREE_VIEW(widget), 0);
3163         width = gtk_tree_view_column_get_width(column);
3164         g_debug("col width %d alloc width %d\n", width, allocation->width);
3165         // Set new wrap-width.  Shave a little off to accomidate the icons
3166         // that share this column.
3167         if (width >= 564) // Don't allow below a certain size
3168                 g_object_set(cell, "wrap-width", width-70, NULL);
3169 }
3170
3171 void
3172 preview_button_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
3173 {
3174         gint titleindex = ghb_settings_get_int(ud->settings, "title");
3175         if (titleindex < 0) return;
3176         g_debug("titleindex %d\n", titleindex);
3177
3178         GtkWidget *widget = GHB_WIDGET (ud->builder, "preview_window");
3179         gtk_widget_show (widget);
3180 }
3181
3182 void
3183 preview_frame_value_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3184 {
3185         set_preview_image(ud);
3186 }
3187
3188 void
3189 preview_button_size_allocate_cb(GtkWidget *widget, GtkAllocation *allocation, signal_user_data_t *ud)
3190 {
3191         g_debug("-------------------------------allocate %d x %d\n", allocation->width, allocation->height);
3192         if (preview_button_width == allocation->width &&
3193                 preview_button_height == allocation->height)
3194         {
3195                 // Nothing to do. Bug out.
3196                 g_debug("nothing to do\n");
3197                 return;
3198         }
3199         g_debug("-------------------------------prev allocate %d x %d\n", preview_button_width, preview_button_height);
3200         preview_button_width = allocation->width;
3201         preview_button_height = allocation->height;
3202         set_preview_image(ud);
3203 }
3204
3205 void
3206 queue_start_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
3207 {
3208         GSList *link = ud->queue;
3209         job_settings_t *job;
3210         gboolean running = FALSE;
3211         while (link != NULL)
3212         {
3213                 job = (job_settings_t*)link->data;
3214                 if ((job->status == GHB_QUEUE_RUNNING) || 
3215                         (job->status == GHB_QUEUE_PENDING))
3216                 {
3217                         running = TRUE;
3218                         break;
3219                 }
3220                 link = link->next;
3221         }
3222         if (!running)
3223         {
3224                 // The queue has no running or pending jobs.
3225                 // Add current settings to the queue, then run.
3226                 if (!queue_add(ud))
3227                         return;
3228         }
3229         ghb_start_queue();
3230 }
3231
3232 void
3233 queue_stop_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
3234 {
3235         cancel_encode(NULL);
3236 }
3237
3238 void
3239 queue_pause_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
3240 {
3241         ghb_pause_queue();
3242 }
3243
3244 void
3245 presets_default_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
3246 {
3247         ghb_set_preset_default(ud->settings);
3248         ghb_presets_list_update(ud);
3249 }
3250
3251 void
3252 debug_log_handler(const gchar *domain, GLogLevelFlags flags, const gchar *msg, gpointer data)
3253 {
3254         signal_user_data_t *ud = (signal_user_data_t*)data;
3255         
3256         if (ud->debug)
3257         {
3258                 printf("%s: %s\n", domain, msg);
3259         }
3260 }
3261
3262 static void
3263 set_visible(GtkWidget *widget, gboolean visible)
3264 {
3265         if (visible)
3266         {
3267                 gtk_widget_show_now(widget);
3268         }
3269         else
3270         {
3271                 gtk_widget_hide(widget);
3272         }
3273 }
3274
3275 void
3276 ghb_hbfd(signal_user_data_t *ud, gboolean hbfd)
3277 {
3278         GtkWidget *widget;
3279         g_debug("ghb_hbfd\n");
3280         widget = GHB_WIDGET(ud->builder, "queue_pause1");
3281         set_visible(widget, !hbfd);
3282         widget = GHB_WIDGET(ud->builder, "queue_add");
3283         set_visible(widget, !hbfd);
3284         widget = GHB_WIDGET(ud->builder, "show_queue");
3285         set_visible(widget, !hbfd);
3286         widget = GHB_WIDGET(ud->builder, "show_activity");
3287         set_visible(widget, !hbfd);
3288
3289         widget = GHB_WIDGET(ud->builder, "chapter_box");
3290         set_visible(widget, !hbfd);
3291         widget = GHB_WIDGET(ud->builder, "container_box");
3292         set_visible(widget, !hbfd);
3293         widget = GHB_WIDGET(ud->builder, "settings_box");
3294         set_visible(widget, !hbfd);
3295         widget = GHB_WIDGET(ud->builder, "presets_save");
3296         set_visible(widget, !hbfd);
3297         widget = GHB_WIDGET(ud->builder, "presets_remove");
3298         set_visible(widget, !hbfd);
3299         widget = GHB_WIDGET(ud->builder, "presets_default");
3300         set_visible(widget, !hbfd);
3301         widget = GHB_WIDGET (ud->builder, "hb_window");
3302         gtk_window_resize(GTK_WINDOW(widget), 16, 16);
3303
3304 }
3305
3306 void
3307 hbfd_toggled_cb(GtkWidget *widget, signal_user_data_t *ud)
3308 {
3309         g_debug("hbfd_toggled_cb\n");
3310         ghb_widget_to_setting (ud->settings, widget);
3311         gboolean hbfd = ghb_settings_get_bool(ud->settings, "hbfd");
3312         ghb_hbfd(ud, hbfd);
3313         ghb_pref_save(ud->settings, "hbfd");
3314 }
3315
3316 void
3317 pref_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3318 {
3319         g_debug("pref_changed_cb\n");
3320         ghb_widget_to_setting (ud->settings, widget);
3321         const gchar *name = gtk_widget_get_name(widget);
3322         ghb_pref_save(ud->settings, name);
3323 }
3324
3325 void
3326 ghb_file_menu_add_dvd(signal_user_data_t *ud)
3327 {
3328         GList *link, *drives;
3329
3330         GtkActionGroup *agroup = GTK_ACTION_GROUP(
3331                 gtk_builder_get_object(ud->builder, "actiongroup1"));
3332         GtkUIManager *ui = GTK_UI_MANAGER(
3333                 gtk_builder_get_object(ud->builder, "uimanager1"));
3334         guint merge_id = gtk_ui_manager_new_merge_id(ui);
3335
3336         link = drives = dvd_device_list();
3337         while (link != NULL)
3338         {
3339                 gchar *name = (gchar*)link->data;
3340                 // Create action for this drive
3341                 GtkAction *action = gtk_action_new(name, name,
3342                         "Scan this DVD source", "gtk-cdrom");
3343                 // Add action to action group
3344                 gtk_action_group_add_action_with_accel(agroup, action, "");
3345                 // Add to ui manager
3346                 gtk_ui_manager_add_ui(ui, merge_id, 
3347                         "ui/menubar1/menuitem1/quit1", name, name,
3348                         GTK_UI_MANAGER_AUTO, TRUE);
3349                 // Connect signal to action (menu item)
3350                 g_signal_connect(action, "activate", 
3351                         (GCallback)dvd_source_activate_cb, ud);
3352                 g_free(name);
3353                 link = link->next;
3354         }
3355         g_list_free(drives);
3356
3357         // Add separator
3358         gtk_ui_manager_add_ui(ui, merge_id, 
3359                 "ui/menubar1/menuitem1/quit1", "", NULL,
3360                 GTK_UI_MANAGER_AUTO, TRUE);
3361 }
3362
3363 gboolean ghb_is_cd(GDrive *gd);
3364
3365 static GList*
3366 dvd_device_list()
3367 {
3368         GVolumeMonitor *gvm;
3369         GList *drives, *link;
3370         GList *dvd_devices = NULL;
3371         
3372         gvm = g_volume_monitor_get ();
3373         drives = g_volume_monitor_get_connected_drives (gvm);
3374         link = drives;
3375         while (link != NULL)
3376         {
3377                 GDrive *gd;
3378                 
3379                 gd = (GDrive*)link->data;
3380                 if (ghb_is_cd(gd))
3381                 {
3382                         gchar *device;
3383                         device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
3384                         dvd_devices = g_list_append(dvd_devices, (gpointer)device);
3385                 }
3386                 g_object_unref (gd);
3387                 link = link->next;
3388         }
3389         g_list_free(drives);
3390         return dvd_devices;
3391 }
3392
3393
3394 static DBusConnection *dbus_connection = NULL;
3395 static LibHalContext *hal_ctx;
3396
3397 gboolean
3398 ghb_is_cd(GDrive *gd)
3399 {
3400         gchar *device;
3401         LibHalDrive *halDrive;
3402         LibHalDriveType dtype;
3403
3404         device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
3405         halDrive = libhal_drive_from_device_file (hal_ctx, device);
3406         dtype = libhal_drive_get_type(halDrive);
3407         g_free(device);
3408         return (dtype == LIBHAL_DRIVE_TYPE_CDROM);
3409 }
3410
3411 void
3412 drive_changed_cb(GVolumeMonitor *gvm, GDrive *gd, signal_user_data_t *ud)
3413 {
3414         gchar *device;
3415         gint state = ghb_get_state();
3416         static gboolean first_time = TRUE;
3417
3418         if (ud->current_dvd_device == NULL) return;
3419         // A drive change event happens when the program initially starts
3420         // and I don't want to automatically scan at that time.
3421         if (first_time)
3422         {
3423                 first_time = FALSE;
3424                 return;
3425         }
3426         if (state != GHB_STATE_IDLE) return;
3427         device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
3428         
3429         // DVD insertion detected.  Scan it.
3430         if (strcmp(device, ud->current_dvd_device) == 0)
3431         {
3432                 if (g_drive_has_media (gd))
3433                 {
3434                         GtkProgressBar *progress;
3435                         progress = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "progressbar"));
3436                         gtk_progress_bar_set_text (progress, "Scanning ...");
3437                         gtk_progress_bar_set_fraction (progress, 0);
3438                         update_source_label(ud, device);
3439                         ghb_hb_cleanup(TRUE);
3440                         ghb_backend_scan(device, 0);
3441                 }
3442                 else
3443                 {
3444                         ghb_hb_cleanup(TRUE);
3445                         ghb_backend_scan("/dev/null", 0);
3446                 }
3447         }
3448         g_free(device);
3449 }
3450
3451
3452 static gboolean
3453 dbus_init (void)
3454 {
3455     DBusError error;
3456
3457     if (dbus_connection != NULL)
3458         return TRUE;
3459
3460     dbus_error_init (&error);
3461     if (!(dbus_connection = dbus_bus_get (DBUS_BUS_SYSTEM, &error))) {
3462         g_debug ("could not get system bus: %s\n", error.message);
3463         dbus_error_free (&error);
3464         return FALSE;
3465     }
3466
3467     //dbus_connection_setup_with_g_main (dbus_connection, NULL);
3468     //dbus_connection_set_exit_on_disconnect (dbus_connection, FALSE);
3469     //dbus_connection_add_filter (dbus_connection, gvm_dbus_filter_function, NULL, NULL);
3470
3471     return TRUE;
3472 }
3473
3474 void
3475 ghb_hal_init()
3476 {
3477     DBusError error;
3478     char **devices;
3479     int nr;
3480
3481     if (!dbus_init ())
3482         return;
3483
3484     if (!(hal_ctx = libhal_ctx_new ())) {
3485         g_warning ("failed to create a HAL context!");
3486         return;
3487     }
3488
3489     libhal_ctx_set_dbus_connection (hal_ctx, dbus_connection);
3490     dbus_error_init (&error);
3491     if (!libhal_ctx_init (hal_ctx, &error)) {
3492         g_warning ("libhal_ctx_init failed: %s", error.message ? error.message : "unknown");
3493         dbus_error_free (&error);
3494         libhal_ctx_free (hal_ctx);
3495         return;
3496     }
3497
3498     /*
3499      * Do something to ping the HAL daemon - the above functions will
3500      * succeed even if hald is not running, so long as DBUS is.  But we
3501      * want to exit silently if hald is not running, to behave on
3502      * pre-2.6 systems.
3503      */
3504     if (!(devices = libhal_get_all_devices (hal_ctx, &nr, &error))) {
3505         g_warning ("seems that HAL is not running: %s", error.message ? error.message : "unknown");
3506         dbus_error_free (&error);
3507
3508         libhal_ctx_shutdown (hal_ctx, NULL);
3509         libhal_ctx_free (hal_ctx);
3510         return;
3511     }
3512
3513     libhal_free_string_array (devices);
3514
3515     //gvm_hal_claim_branch ("/org/freedesktop/Hal/devices/local");
3516 }
3517
3518 gboolean 
3519 tweak_setting_cb(
3520         GtkWidget *widget, 
3521         GdkEventButton *event, 
3522         signal_user_data_t *ud)
3523 {
3524         const gchar *name;
3525         gchar *tweak_name;
3526         gboolean ret = FALSE;
3527         gboolean allow_tweaks;
3528
3529         g_debug("press %d %d", event->type, event->button);
3530         allow_tweaks = ghb_settings_get_bool (ud->settings, "allow_tweaks");
3531         if (allow_tweaks && event->type == GDK_BUTTON_PRESS && event->button == 3)
3532         { // Its a right mouse click
3533                 GtkWidget *dialog;
3534                 GtkEntry *entry;
3535                 GtkResponseType response;
3536                 const gchar *tweak = NULL;
3537
3538                 name = gtk_widget_get_name(widget);
3539                 if (g_str_has_prefix(name, "tweak_"))
3540                 {
3541                         tweak_name = g_strdup(name);
3542                 }
3543                 else
3544                 {
3545                         tweak_name = g_strdup_printf("tweak_%s", name);
3546                 }
3547
3548                 tweak = ghb_settings_get_string (ud->settings, tweak_name);
3549                 dialog = GHB_WIDGET(ud->builder, "tweak_dialog");
3550                 gtk_window_set_title(GTK_WINDOW(dialog), tweak_name);
3551                 entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "tweak_setting"));
3552                 if (tweak)
3553                         gtk_entry_set_text(entry, tweak);
3554                 response = gtk_dialog_run(GTK_DIALOG(dialog));
3555                 gtk_widget_hide(dialog);
3556                 if (response == GTK_RESPONSE_OK)
3557                 {
3558                         tweak = gtk_entry_get_text(entry);
3559                         if (ghb_validate_filter_string(tweak, -1))
3560                                 ghb_settings_set_string(ud->settings, tweak_name, tweak);
3561                         else
3562                         {
3563                                 gchar *message;
3564                                 message = g_strdup_printf(
3565                                                         "Invalid Settings:\n%s",
3566                                                         tweak);
3567                                 ghb_message_dialog(GTK_MESSAGE_ERROR, message, "Cancel", NULL);
3568                                 g_free(message);
3569                         }
3570                 }
3571                 g_free(tweak_name);
3572                 ret = TRUE;
3573         }
3574         return ret;
3575 }
3576