OSDN Git Service

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