OSDN Git Service

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