OSDN Git Service

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