OSDN Git Service

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