OSDN Git Service

LinGui: Fix a problem with individual activity logs.
[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         const 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->job_activity_log)
1777                         g_io_channel_unref(ud->job_activity_log);
1778                 ud->job_activity_log = NULL;
1779                 if (!ud->cancel_encode)
1780                         ud->current_job = ghb_start_next_job(ud, FALSE);
1781                 else
1782                         ud->current_job = NULL;
1783                 if (js)
1784                         ghb_settings_set_int(js, "job_status", qstatus);
1785                 ghb_save_queue(ud->queue);
1786                 ud->cancel_encode = FALSE;
1787         }
1788         else if (status.queue.state & GHB_STATE_MUXING)
1789         {
1790                 gtk_progress_bar_set_text(progress, "Muxing: this may take awhile...");
1791         }
1792         if (status.queue.state & GHB_STATE_SCANNING)
1793         {
1794                 // This needs to be in scanning and working since scanning
1795                 // happens fast enough that it can be missed
1796                 if (!work_started)
1797                 {
1798                         work_started = TRUE;
1799                         ghb_queue_buttons_grey(ud, TRUE);
1800                 }
1801         }
1802         if (status.queue.state & GHB_STATE_WORKING)
1803         {
1804                 // This needs to be in scanning and working since scanning
1805                 // happens fast enough that it can be missed
1806                 if (!work_started)
1807                 {
1808                         work_started = TRUE;
1809                         ghb_queue_buttons_grey(ud, TRUE);
1810                 }
1811                 index = find_queue_job(ud->queue, status.queue.unique_id, &js);
1812                 if (status.queue.unique_id != 0 && index >= 0)
1813                 {
1814                         gchar working_icon[] = "hb-working0";
1815                         working_icon[10] = '0' + working;
1816                         working = (working+1) % 6;
1817                         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
1818                         store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
1819                         gchar *path = g_strdup_printf ("%d", index);
1820                         if (gtk_tree_model_get_iter_from_string(
1821                                         GTK_TREE_MODEL(store), &iter, path))
1822                         {
1823                                 gtk_tree_store_set(store, &iter, 0, working_icon, -1);
1824                         }
1825                         g_free(path);
1826                 }
1827                 GtkLabel *label;
1828                 gchar *status_str;
1829
1830                 status_str = working_status_string(ud, &status.queue);
1831                 label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_status"));
1832                 gtk_label_set_text (label, status_str);
1833                 g_free(status_str);
1834         }
1835         if (status.scan.state & GHB_STATE_WORKING)
1836         {
1837                 GtkProgressBar *live_progress;
1838                 live_progress = GTK_PROGRESS_BAR(
1839                         GHB_WIDGET(ud->builder, "live_encode_progress"));
1840                 status_str = working_status_string(ud, &status.scan);
1841                 gtk_progress_bar_set_text (live_progress, status_str);
1842                 gtk_progress_bar_set_fraction (live_progress, status.scan.progress);
1843                 g_free(status_str);
1844         }
1845         if (status.scan.state & GHB_STATE_WORKDONE)
1846         {
1847                 switch( status.scan.error )
1848                 {
1849                         case GHB_ERROR_NONE:
1850                         {
1851                                 ghb_live_encode_done(ud, TRUE);
1852                         } break;
1853                         default:
1854                         {
1855                                 ghb_live_encode_done(ud, FALSE);
1856                         } break;
1857                 }
1858                 ghb_clear_scan_state(GHB_STATE_WORKDONE);
1859         }
1860 }
1861
1862 gboolean
1863 ghb_timer_cb(gpointer data)
1864 {
1865         signal_user_data_t *ud = (signal_user_data_t*)data;
1866
1867         ghb_live_preview_progress(ud);
1868         ghb_backend_events(ud);
1869         if (update_default_destination)
1870         {
1871                 gchar *dest, *dest_dir, *def_dest;
1872                 dest = ghb_settings_get_string(ud->settings, "destination");
1873                 dest_dir = g_path_get_dirname (dest);
1874                 def_dest = ghb_settings_get_string(ud->settings, "destination_dir");
1875                 if (strcmp(dest_dir, def_dest) != 0)
1876                 {
1877                         ghb_settings_set_string (ud->settings, "destination_dir", dest_dir);
1878                         ghb_pref_save (ud->settings, "destination_dir");
1879                 }
1880                 g_free(dest);
1881                 g_free(dest_dir);
1882                 g_free(def_dest);
1883                 update_default_destination = FALSE;
1884         }
1885         if (update_preview)
1886         {
1887                 ghb_set_preview_image (ud);
1888                 update_preview = FALSE;
1889         }
1890         return TRUE;
1891 }
1892
1893 gboolean
1894 ghb_log_cb(GIOChannel *source, GIOCondition cond, gpointer data)
1895 {
1896         gchar *text = NULL;
1897         gsize length;
1898         GtkTextView *textview;
1899         GtkTextBuffer *buffer;
1900         GtkTextIter iter;
1901         GtkTextMark *mark;
1902         GError *gerror = NULL;
1903         GIOStatus status;
1904         
1905         signal_user_data_t *ud = (signal_user_data_t*)data;
1906
1907         status = g_io_channel_read_line (source, &text, &length, NULL, &gerror);
1908         if (text != NULL)
1909         {
1910                 GdkWindow *window;
1911                 gint width, height;
1912                 gint x, y;
1913                 gboolean bottom = FALSE;
1914
1915                 textview = GTK_TEXT_VIEW(GHB_WIDGET (ud->builder, "activity_view"));
1916                 buffer = gtk_text_view_get_buffer (textview);
1917                 // I would like to auto-scroll the window when the scrollbar
1918                 // is at the bottom, 
1919                 // must determine whether the insert point is at
1920                 // the bottom of the window 
1921                 window = gtk_text_view_get_window(textview, GTK_TEXT_WINDOW_TEXT);
1922                 if (window != NULL)
1923                 {
1924                         gdk_drawable_get_size(GDK_DRAWABLE(window), &width, &height);
1925                         gtk_text_view_window_to_buffer_coords(textview, 
1926                                 GTK_TEXT_WINDOW_TEXT, width, height, &x, &y);
1927                         gtk_text_view_get_iter_at_location(textview, &iter, x, y);
1928                         if (gtk_text_iter_is_end(&iter))
1929                         {
1930                                 bottom = TRUE;
1931                         }
1932                 }
1933                 else
1934                 {
1935                         // If the window isn't available, assume bottom
1936                         bottom = TRUE;
1937                 }
1938                 gtk_text_buffer_get_end_iter(buffer, &iter);
1939                 gtk_text_buffer_insert(buffer, &iter, text, -1);
1940                 if (bottom)
1941                 {
1942                         gtk_text_buffer_get_end_iter(buffer, &iter);
1943                         mark = gtk_text_buffer_create_mark(buffer, NULL, &iter, FALSE);
1944                         gtk_text_view_scroll_mark_onscreen(textview, mark);
1945                         gtk_text_buffer_delete_mark(buffer, mark);
1946                 }
1947                 g_io_channel_write_chars (ud->activity_log, text, 
1948                                                                 length, &length, NULL);
1949                 g_io_channel_flush(ud->activity_log, NULL);
1950                 if (ud->job_activity_log)
1951                 {
1952                         g_io_channel_write_chars (ud->job_activity_log, text, 
1953                                                                         length, &length, NULL);
1954                         g_io_channel_flush(ud->job_activity_log, NULL);
1955                 }
1956                 g_free(text);
1957         }
1958         if (status != G_IO_STATUS_NORMAL)
1959         {
1960                 // This should never happen, but if it does I would get into an
1961                 // infinite loop.  Returning false removes this callback.
1962                 g_warning("Error while reading activity from pipe");
1963                 if (gerror != NULL)
1964                 {
1965                         g_warning("%s", gerror->message);
1966                         g_error_free (gerror);
1967                 }
1968                 return FALSE;
1969         }
1970         if (gerror != NULL)
1971                 g_error_free (gerror);
1972         return TRUE;
1973 }
1974
1975 void
1976 show_activity_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
1977 {
1978         GtkWidget *widget = GHB_WIDGET (ud->builder, "activity_window");
1979         gtk_widget_show (widget);
1980 }
1981
1982 void
1983 ghb_log(gchar *log, ...)
1984 {
1985         va_list args;
1986         time_t _now;
1987     struct tm *now;
1988         gchar fmt[362];
1989
1990         _now = time(NULL);
1991         now = localtime( &_now );
1992         snprintf(fmt, 362, "[%02d:%02d:%02d] lingui: %s\n", 
1993                         now->tm_hour, now->tm_min, now->tm_sec, log);
1994         va_start(args, log);
1995         vfprintf(stderr, fmt, args);
1996         va_end(args);
1997 }
1998
1999 void
2000 about_activate_cb(GtkWidget *xwidget, signal_user_data_t *ud)
2001 {
2002         GtkWidget *widget = GHB_WIDGET (ud->builder, "hb_about");
2003         gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(widget), ghb_version());
2004         gtk_widget_show (widget);
2005 }
2006
2007 static void
2008 browse_url(const gchar *url)
2009 {
2010         gboolean result;
2011         char *argv[] = 
2012                 {"xdg-open",NULL,NULL,NULL};
2013         argv[1] = (gchar*)url;
2014         result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
2015                                 NULL, NULL, NULL);
2016         if (result) return;
2017
2018         argv[0] = "gnome-open";
2019         result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
2020                                 NULL, NULL, NULL);
2021         if (result) return;
2022
2023         argv[0] = "kfmclient";
2024         argv[1] = "exec";
2025         argv[2] = "http://trac.handbrake.fr/wiki/HandBrakeGuide";
2026         result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
2027                                 NULL, NULL, NULL);
2028         if (result) return;
2029
2030         argv[0] = "firefox";
2031         argv[1] = "http://trac.handbrake.fr/wiki/HandBrakeGuide";
2032         argv[2] = NULL;
2033         result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
2034                                 NULL, NULL, NULL);
2035 }
2036
2037 void
2038 guide_activate_cb(GtkWidget *xwidget, signal_user_data_t *ud)
2039 {
2040         browse_url("http://trac.handbrake.fr/wiki/HandBrakeGuide");
2041 }
2042
2043 void
2044 hb_about_response_cb(GtkWidget *widget, gint response, signal_user_data_t *ud)
2045 {
2046         gtk_widget_hide (widget);
2047 }
2048
2049 void
2050 show_queue_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
2051 {
2052         GtkWidget *widget = GHB_WIDGET (ud->builder, "queue_window");
2053         gtk_widget_show (widget);
2054 }
2055
2056 void
2057 show_presets_toggled_cb(GtkWidget *action, signal_user_data_t *ud)
2058 {
2059         GtkWidget *widget;
2060         GtkWindow *hb_window;
2061         
2062         g_debug("show_presets_clicked_cb ()");
2063         widget = GHB_WIDGET (ud->builder, "presets_frame");
2064         ghb_widget_to_setting(ud->settings, action);
2065         if (ghb_settings_get_boolean(ud->settings, "show_presets"))
2066         {
2067                 gtk_widget_show_now(widget);
2068         }
2069         else
2070         {
2071                 gtk_widget_hide(widget);
2072                 hb_window = GTK_WINDOW(GHB_WIDGET (ud->builder, "hb_window"));
2073                 gtk_window_resize(hb_window, 16, 16);
2074         }
2075         ghb_pref_save(ud->settings, "show_presets");
2076 }
2077
2078 static void
2079 update_chapter_list(signal_user_data_t *ud)
2080 {
2081         GtkTreeView *treeview;
2082         GtkTreeIter iter;
2083         GtkListStore *store;
2084         gboolean done;
2085         GValue *chapters;
2086         gint titleindex, ii;
2087         gint count;
2088         
2089         g_debug("update_chapter_list ()");
2090         titleindex = ghb_settings_combo_int(ud->settings, "title");
2091         chapters = ghb_get_chapters(titleindex);
2092         count = ghb_array_len(chapters);
2093         if (chapters)
2094                 ghb_settings_set_value(ud->settings, "chapter_list", chapters);
2095         
2096         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "chapters_list"));
2097         store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview));
2098         ii = 0;
2099         if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter))
2100         {
2101                 do
2102                 {
2103
2104                         if (ii < count)
2105                         {
2106                                 gchar *chapter;
2107
2108                                 // Update row with settings data
2109                                 g_debug("Updating row");
2110                                 chapter = ghb_value_string(ghb_array_get_nth(chapters, ii));
2111                                 gtk_list_store_set(store, &iter, 
2112                                         0, ii+1,
2113                                         1, chapter,
2114                                         2, TRUE,
2115                                         -1);
2116                                 g_free(chapter);
2117                                 ii++;
2118                                 done = !gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
2119                         }
2120                         else
2121                         {
2122                                 // No more settings data, remove row
2123                                 g_debug("Removing row");
2124                                 done = !gtk_list_store_remove(store, &iter);
2125                         }
2126                 } while (!done);
2127         }
2128         while (ii < count)
2129         {
2130                 gchar *chapter;
2131
2132                 // Additional settings, add row
2133                 g_debug("Adding row");
2134                 chapter = ghb_value_string(ghb_array_get_nth(chapters, ii));
2135                 gtk_list_store_append(store, &iter);
2136                 gtk_list_store_set(store, &iter, 
2137                         0, ii+1,
2138                         1, chapter,
2139                         2, TRUE,
2140                         -1);
2141                 g_free(chapter);
2142                 ii++;
2143         }
2144 }
2145
2146 static gint chapter_edit_key = 0;
2147
2148 gboolean
2149 chapter_keypress_cb(
2150         GhbCellRendererText *cell,
2151         GdkEventKey *event,
2152         signal_user_data_t *ud)
2153 {
2154         chapter_edit_key = event->keyval;
2155         return FALSE;
2156 }
2157
2158 void
2159 chapter_edited_cb(
2160         GhbCellRendererText *cell, 
2161         gchar *path, 
2162         gchar *text, 
2163         signal_user_data_t *ud)
2164 {
2165         GtkTreePath *treepath;
2166         GtkListStore *store;
2167         GtkTreeView *treeview;
2168         GtkTreeIter iter;
2169         gint index;
2170         gint *pi;
2171         gint row;
2172         
2173         g_debug("chapter_edited_cb ()");
2174         g_debug("path (%s)", path);
2175         g_debug("text (%s)", text);
2176         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "chapters_list"));
2177         store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview));
2178         treepath = gtk_tree_path_new_from_string (path);
2179         pi = gtk_tree_path_get_indices(treepath);
2180         row = pi[0];
2181         gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, treepath);
2182         gtk_list_store_set(store, &iter, 
2183                 1, text,
2184                 2, TRUE,
2185                 -1);
2186         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 0, &index, -1);
2187
2188         const GValue *chapters;
2189         GValue *chapter;
2190
2191         chapters = ghb_settings_get_value(ud->settings, "chapter_list");
2192         chapter = ghb_array_get_nth(chapters, index-1);
2193         g_value_set_string(chapter, text);
2194         if ((chapter_edit_key == GDK_Return || chapter_edit_key == GDK_Down) &&
2195                 gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter))
2196         {
2197                 GtkTreeViewColumn *column;
2198
2199                 gtk_tree_path_next(treepath);
2200                 // When a cell has been edited, I want to advance to the
2201                 // next cell and start editing it automaitcally.
2202                 // Unfortunately, we may not be in a state here where
2203                 // editing is allowed.  This happens when the user selects
2204                 // a new cell with the mouse instead of just hitting enter.
2205                 // Some kind of Gtk quirk.  widget_editable==NULL assertion.
2206                 // Editing is enabled again once the selection event has been
2207                 // processed.  So I'm queueing up a callback to be called
2208                 // when things go idle.  There, I will advance to the next
2209                 // cell and initiate editing.
2210                 //
2211                 // Now, you might be asking why I don't catch the keypress
2212                 // event and determine what action to take based on that.
2213                 // The Gtk developers in their infinite wisdom have made the 
2214                 // actual GtkEdit widget being used a private member of
2215                 // GtkCellRendererText, so it can not be accessed to hang a
2216                 // signal handler off of.  And they also do not propagate the
2217                 // keypress signals in any other way.  So that information is lost.
2218                 //g_idle_add((GSourceFunc)next_cell, ud);
2219                 //
2220                 // Keeping the above comment for posterity.
2221                 // I got industrious and made my own CellTextRendererText that
2222                 // passes on the key-press-event. So now I have much better
2223                 // control of this.
2224                 column = gtk_tree_view_get_column(treeview, 1);
2225                 gtk_tree_view_set_cursor(treeview, treepath, column, TRUE);
2226         }
2227         else if (chapter_edit_key == GDK_Up && row > 0)
2228         {
2229                 GtkTreeViewColumn *column;
2230                 gtk_tree_path_prev(treepath);
2231                 column = gtk_tree_view_get_column(treeview, 1);
2232                 gtk_tree_view_set_cursor(treeview, treepath, column, TRUE);
2233         }
2234         gtk_tree_path_free (treepath);
2235 }
2236
2237 void
2238 chapter_list_selection_changed_cb(GtkTreeSelection *selection, signal_user_data_t *ud)
2239 {
2240         g_debug("chapter_list_selection_changed_cb ()");
2241         //chapter_selection_changed = TRUE;
2242 }
2243
2244 void
2245 debug_log_handler(const gchar *domain, GLogLevelFlags flags, const gchar *msg, gpointer data)
2246 {
2247         signal_user_data_t *ud = (signal_user_data_t*)data;
2248         
2249         if (ud->debug)
2250         {
2251                 printf("%s: %s\n", domain, msg);
2252         }
2253 }
2254
2255 static void
2256 set_visible(GtkWidget *widget, gboolean visible)
2257 {
2258         if (visible)
2259         {
2260                 gtk_widget_show_now(widget);
2261         }
2262         else
2263         {
2264                 gtk_widget_hide(widget);
2265         }
2266 }
2267
2268 void
2269 ghb_hbfd(signal_user_data_t *ud, gboolean hbfd)
2270 {
2271         GtkWidget *widget;
2272         g_debug("ghb_hbfd");
2273         widget = GHB_WIDGET(ud->builder, "queue_pause1");
2274         set_visible(widget, !hbfd);
2275         widget = GHB_WIDGET(ud->builder, "queue_add");
2276         set_visible(widget, !hbfd);
2277         widget = GHB_WIDGET(ud->builder, "show_queue");
2278         set_visible(widget, !hbfd);
2279         widget = GHB_WIDGET(ud->builder, "show_activity");
2280         set_visible(widget, !hbfd);
2281
2282         widget = GHB_WIDGET(ud->builder, "chapter_box");
2283         set_visible(widget, !hbfd);
2284         widget = GHB_WIDGET(ud->builder, "container_box");
2285         set_visible(widget, !hbfd);
2286         widget = GHB_WIDGET(ud->builder, "settings_box");
2287         set_visible(widget, !hbfd);
2288         widget = GHB_WIDGET(ud->builder, "presets_save");
2289         set_visible(widget, !hbfd);
2290         widget = GHB_WIDGET(ud->builder, "presets_remove");
2291         set_visible(widget, !hbfd);
2292         widget = GHB_WIDGET(ud->builder, "presets_default");
2293         set_visible(widget, !hbfd);
2294         widget = GHB_WIDGET (ud->builder, "hb_window");
2295         gtk_window_resize(GTK_WINDOW(widget), 16, 16);
2296
2297 }
2298
2299 void
2300 hbfd_toggled_cb(GtkWidget *widget, signal_user_data_t *ud)
2301 {
2302         g_debug("hbfd_toggled_cb");
2303         ghb_widget_to_setting (ud->settings, widget);
2304         gboolean hbfd = ghb_settings_get_boolean(ud->settings, "hbfd");
2305         ghb_hbfd(ud, hbfd);
2306         ghb_pref_save(ud->settings, "hbfd");
2307 }
2308
2309 void
2310 pref_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
2311 {
2312         g_debug("pref_changed_cb");
2313         ghb_widget_to_setting (ud->settings, widget);
2314         ghb_check_dependency(ud, widget);
2315         const gchar *name = gtk_widget_get_name(widget);
2316         ghb_pref_save(ud->settings, name);
2317 }
2318
2319 void
2320 tweaks_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
2321 {
2322         g_debug("tweaks_changed_cb");
2323         ghb_widget_to_setting (ud->settings, widget);
2324         const gchar *name = gtk_widget_get_name(widget);
2325         ghb_pref_save(ud->settings, name);
2326
2327         gboolean tweaks = ghb_settings_get_boolean(ud->settings, "allow_tweaks");
2328         widget = GHB_WIDGET(ud->builder, "PictureDeinterlace");
2329         tweaks ? gtk_widget_hide(widget) : gtk_widget_show(widget);
2330         widget = GHB_WIDGET(ud->builder, "tweak_PictureDeinterlace");
2331         !tweaks ? gtk_widget_hide(widget) : gtk_widget_show(widget);
2332
2333         widget = GHB_WIDGET(ud->builder, "PictureDenoise");
2334         tweaks ? gtk_widget_hide(widget) : gtk_widget_show(widget);
2335         widget = GHB_WIDGET(ud->builder, "tweak_PictureDenoise");
2336         !tweaks ? gtk_widget_hide(widget) : gtk_widget_show(widget);
2337         if (tweaks)
2338         {
2339                 const GValue *value;
2340                 value = ghb_settings_get_value(ud->settings, "PictureDeinterlace");
2341                 ghb_ui_update(ud, "tweak_PictureDeinterlace", value);
2342                 value = ghb_settings_get_value(ud->settings, "PictureDenoise");
2343                 ghb_ui_update(ud, "tweak_PictureDenoise", value);
2344         }
2345         else
2346         {
2347                 const GValue *value;
2348                 value = ghb_settings_get_value(ud->settings, "tweak_PictureDeinterlace");
2349                 ghb_ui_update(ud, "PictureDeinterlace", value);
2350                 value = ghb_settings_get_value(ud->settings, "tweak_PictureDenoise");
2351                 ghb_ui_update(ud, "PictureDenoise", value);
2352         }
2353 }
2354
2355 void
2356 hbfd_feature_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
2357 {
2358         g_debug("hbfd_feature_changed_cb");
2359         ghb_widget_to_setting (ud->settings, widget);
2360         const gchar *name = gtk_widget_get_name(widget);
2361         ghb_pref_save(ud->settings, name);
2362
2363         gboolean hbfd = ghb_settings_get_boolean(ud->settings, "hbfd_feature");
2364         GtkAction *action;
2365         if (hbfd)
2366         {
2367                 const GValue *val;
2368                 val = ghb_settings_get_value(ud->settings, "hbfd");
2369                 ghb_ui_update(ud, "hbfd", val);
2370         }
2371         action = GHB_ACTION (ud->builder, "hbfd");
2372         gtk_action_set_visible(action, hbfd);
2373 }
2374
2375 void
2376 ghb_file_menu_add_dvd(signal_user_data_t *ud)
2377 {
2378         GList *link, *drives;
2379
2380         GtkActionGroup *agroup = GTK_ACTION_GROUP(
2381                 gtk_builder_get_object(ud->builder, "actiongroup1"));
2382         GtkUIManager *ui = GTK_UI_MANAGER(
2383                 gtk_builder_get_object(ud->builder, "uimanager1"));
2384         guint merge_id = gtk_ui_manager_new_merge_id(ui);
2385
2386         link = drives = dvd_device_list();
2387         while (link != NULL)
2388         {
2389                 gchar *name = (gchar*)link->data;
2390                 // Create action for this drive
2391                 GtkAction *action = gtk_action_new(name, name,
2392                         "Scan this DVD source", "gtk-cdrom");
2393                 // Add action to action group
2394                 gtk_action_group_add_action_with_accel(agroup, action, "");
2395                 // Add to ui manager
2396                 gtk_ui_manager_add_ui(ui, merge_id, 
2397                         "ui/menubar1/menuitem1/quit1", name, name,
2398                         GTK_UI_MANAGER_AUTO, TRUE);
2399                 // Connect signal to action (menu item)
2400                 g_signal_connect(action, "activate", 
2401                         (GCallback)dvd_source_activate_cb, ud);
2402                 g_free(name);
2403                 link = link->next;
2404         }
2405         g_list_free(drives);
2406
2407         // Add separator
2408         gtk_ui_manager_add_ui(ui, merge_id, 
2409                 "ui/menubar1/menuitem1/quit1", "", NULL,
2410                 GTK_UI_MANAGER_AUTO, TRUE);
2411 }
2412
2413 gboolean ghb_is_cd(GDrive *gd);
2414
2415 static GList*
2416 dvd_device_list()
2417 {
2418         GVolumeMonitor *gvm;
2419         GList *drives, *link;
2420         GList *dvd_devices = NULL;
2421         
2422         gvm = g_volume_monitor_get ();
2423         drives = g_volume_monitor_get_connected_drives (gvm);
2424         link = drives;
2425         while (link != NULL)
2426         {
2427                 GDrive *gd;
2428                 
2429                 gd = (GDrive*)link->data;
2430                 if (ghb_is_cd(gd))
2431                 {
2432                         gchar *device;
2433                         device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
2434                         dvd_devices = g_list_append(dvd_devices, (gpointer)device);
2435                 }
2436                 g_object_unref (gd);
2437                 link = link->next;
2438         }
2439         g_list_free(drives);
2440         return dvd_devices;
2441 }
2442
2443
2444 static DBusConnection *dbus_connection = NULL;
2445 static LibHalContext *hal_ctx;
2446
2447 gboolean
2448 ghb_is_cd(GDrive *gd)
2449 {
2450         gchar *device;
2451         LibHalDrive *halDrive;
2452         LibHalDriveType dtype;
2453
2454         device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
2455         halDrive = libhal_drive_from_device_file (hal_ctx, device);
2456         dtype = libhal_drive_get_type(halDrive);
2457         libhal_drive_free(halDrive);
2458         g_free(device);
2459         return (dtype == LIBHAL_DRIVE_TYPE_CDROM);
2460 }
2461
2462 void
2463 drive_changed_cb(GVolumeMonitor *gvm, GDrive *gd, signal_user_data_t *ud)
2464 {
2465         gchar *device;
2466         gint state = ghb_get_scan_state();
2467         static gboolean first_time = TRUE;
2468
2469         if (state != GHB_STATE_IDLE) return;
2470         if (ud->current_dvd_device == NULL) return;
2471         // A drive change event happens when the program initially starts
2472         // and I don't want to automatically scan at that time.
2473         if (first_time)
2474         {
2475                 first_time = FALSE;
2476                 return;
2477         }
2478         device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
2479         
2480         // DVD insertion detected.  Scan it.
2481         if (strcmp(device, ud->current_dvd_device) == 0)
2482         {
2483                 if (g_drive_has_media (gd))
2484                 {
2485                         GtkProgressBar *progress;
2486                         progress = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "progressbar"));
2487                         gtk_progress_bar_set_text (progress, "Scanning ...");
2488                         gtk_progress_bar_set_fraction (progress, 0);
2489                         update_source_label(ud, device);
2490                         ghb_hb_cleanup(TRUE);
2491                         prune_logs(ud);
2492                         gint preview_count;
2493                         preview_count = ghb_settings_get_int(ud->settings, "preview_count");
2494                         ghb_backend_scan(device, 0, preview_count);
2495                 }
2496                 else
2497                 {
2498                         ghb_hb_cleanup(TRUE);
2499                         prune_logs(ud);
2500                         ghb_backend_scan("/dev/null", 0, 1);
2501                 }
2502         }
2503         g_free(device);
2504 }
2505
2506
2507 static gboolean
2508 dbus_init (void)
2509 {
2510         DBusError error;
2511
2512         if (dbus_connection != NULL)
2513                 return TRUE;
2514
2515         dbus_error_init (&error);
2516         if (!(dbus_connection = dbus_bus_get (DBUS_BUS_SYSTEM, &error))) {
2517                 g_debug ("could not get system bus: %s", error.message);
2518                 dbus_error_free (&error);
2519                 return FALSE;
2520         }
2521
2522         //dbus_connection_setup_with_g_main (dbus_connection, NULL);
2523         //dbus_connection_set_exit_on_disconnect (dbus_connection, FALSE);
2524         //dbus_connection_add_filter (dbus_connection, gvm_dbus_filter_function, NULL, NULL);
2525
2526         return TRUE;
2527 }
2528
2529 void
2530 ghb_hal_init()
2531 {
2532         DBusError error;
2533         char **devices;
2534         int nr;
2535
2536         if (!dbus_init ())
2537                 return;
2538
2539         if (!(hal_ctx = libhal_ctx_new ())) {
2540                 g_warning ("failed to create a HAL context!");
2541                 return;
2542         }
2543
2544         libhal_ctx_set_dbus_connection (hal_ctx, dbus_connection);
2545         dbus_error_init (&error);
2546         if (!libhal_ctx_init (hal_ctx, &error)) {
2547                 g_warning ("libhal_ctx_init failed: %s", error.message ? error.message : "unknown");
2548                 dbus_error_free (&error);
2549                 libhal_ctx_free (hal_ctx);
2550                 return;
2551         }
2552
2553         /*
2554          * Do something to ping the HAL daemon - the above functions will
2555          * succeed even if hald is not running, so long as DBUS is.  But we
2556          * want to exit silently if hald is not running, to behave on
2557          * pre-2.6 systems.
2558          */
2559         if (!(devices = libhal_get_all_devices (hal_ctx, &nr, &error))) {
2560                 g_warning ("seems that HAL is not running: %s", error.message ? error.message : "unknown");
2561                 dbus_error_free (&error);
2562
2563                 libhal_ctx_shutdown (hal_ctx, NULL);
2564                 libhal_ctx_free (hal_ctx);
2565                 return;
2566         }
2567
2568         libhal_free_string_array (devices);
2569
2570         //gvm_hal_claim_branch ("/org/freedesktop/Hal/devices/local");
2571 }
2572
2573 gboolean 
2574 tweak_setting_cb(
2575         GtkWidget *widget, 
2576         GdkEventButton *event, 
2577         signal_user_data_t *ud)
2578 {
2579         const gchar *name;
2580         gchar *tweak_name;
2581         gboolean ret = FALSE;
2582         gboolean allow_tweaks;
2583
2584         g_debug("press %d %d", event->type, event->button);
2585         allow_tweaks = ghb_settings_get_boolean(ud->settings, "allow_tweaks");
2586         if (allow_tweaks && event->type == GDK_BUTTON_PRESS && event->button == 3)
2587         { // Its a right mouse click
2588                 GtkWidget *dialog;
2589                 GtkEntry *entry;
2590                 GtkResponseType response;
2591                 gchar *tweak = NULL;
2592
2593                 name = gtk_widget_get_name(widget);
2594                 if (g_str_has_prefix(name, "tweak_"))
2595                 {
2596                         tweak_name = g_strdup(name);
2597                 }
2598                 else
2599                 {
2600                         tweak_name = g_strdup_printf("tweak_%s", name);
2601                 }
2602
2603                 tweak = ghb_settings_get_string (ud->settings, tweak_name);
2604                 dialog = GHB_WIDGET(ud->builder, "tweak_dialog");
2605                 gtk_window_set_title(GTK_WINDOW(dialog), tweak_name);
2606                 entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "tweak_setting"));
2607                 if (tweak)
2608                 {
2609                         gtk_entry_set_text(entry, tweak);
2610                         g_free(tweak);
2611                 }
2612                 response = gtk_dialog_run(GTK_DIALOG(dialog));
2613                 gtk_widget_hide(dialog);
2614                 if (response == GTK_RESPONSE_OK)
2615                 {
2616                         tweak = (gchar*)gtk_entry_get_text(entry);
2617                         if (ghb_validate_filter_string(tweak, -1))
2618                                 ghb_settings_set_string(ud->settings, tweak_name, tweak);
2619                         else
2620                         {
2621                                 gchar *message;
2622                                 message = g_strdup_printf(
2623                                                         "Invalid Settings:\n%s",
2624                                                         tweak);
2625                                 ghb_message_dialog(GTK_MESSAGE_ERROR, message, "Cancel", NULL);
2626                                 g_free(message);
2627                         }
2628                 }
2629                 g_free(tweak_name);
2630                 ret = TRUE;
2631         }
2632         return ret;
2633 }
2634
2635 gboolean 
2636 easter_egg_cb(
2637         GtkWidget *widget, 
2638         GdkEventButton *event, 
2639         signal_user_data_t *ud)
2640 {
2641         g_debug("press %d %d", event->type, event->button);
2642         if (event->type == GDK_3BUTTON_PRESS && event->button == 1)
2643         { // Its a tripple left mouse button click
2644                 GtkWidget *widget;
2645                 widget = GHB_WIDGET(ud->builder, "allow_tweaks");
2646                 gtk_widget_show(widget);
2647                 widget = GHB_WIDGET(ud->builder, "hbfd_feature");
2648                 gtk_widget_show(widget);
2649         }
2650         else if (event->type == GDK_BUTTON_PRESS && event->button == 1)
2651         {
2652                 GtkWidget *widget;
2653                 widget = GHB_WIDGET(ud->builder, "allow_tweaks");
2654                 gtk_widget_hide(widget);
2655                 widget = GHB_WIDGET(ud->builder, "hbfd_feature");
2656                 gtk_widget_hide(widget);
2657         }
2658         return FALSE;
2659 }
2660
2661 gchar*
2662 format_deblock_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud)
2663 {
2664         if (val < 5.0)
2665         {
2666                 return g_strdup_printf("Off");
2667         }
2668         else
2669         {
2670                 return g_strdup_printf("%d", (gint)val);
2671         }
2672 }
2673
2674 gchar*
2675 format_vquality_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud)
2676 {
2677         if (ghb_settings_get_boolean(ud->settings, "directqp"))
2678         {
2679                 gint vcodec = ghb_settings_combo_int(ud->settings, "VideoEncoder");
2680                 // Only x264 and ffmpeg currently support direct qp/crf entry
2681                 if (vcodec != HB_VCODEC_X264 && vcodec != HB_VCODEC_FFMPEG)
2682                 {
2683                         val *= 100;
2684                         return g_strdup_printf("%.1f", val);
2685                 }
2686                 return g_strdup_printf("%d", (gint)val);
2687         }
2688         else
2689         {
2690                 val *= 100;
2691                 return g_strdup_printf("%.1f", val);
2692         }
2693 }
2694
2695 static void
2696 html_link_cb(GtkHTML *html, const gchar *url, signal_user_data_t *ud)
2697 {
2698         browse_url(url);
2699 }
2700
2701 static gpointer check_stable_update(signal_user_data_t *ud);
2702 static gboolean stable_update_lock = FALSE;
2703
2704 static void
2705 process_appcast(signal_user_data_t *ud)
2706 {
2707         gchar *description = NULL, *build = NULL, *version = NULL, *msg;
2708         GtkWidget *html, *window, *dialog, *label;
2709         gint    response, ibuild = 0, skip;
2710
2711         if (ud->appcast == NULL || ud->appcast_len < 15 || 
2712                 strncmp(&(ud->appcast[9]), "200 OK", 6))
2713         {
2714                 if (!stable_update_lock && HB_BUILD % 100)
2715                         g_idle_add((GSourceFunc)check_stable_update, ud);
2716                 goto done;
2717         }
2718         ghb_appcast_parse(ud->appcast, &description, &build, &version);
2719         if (build)
2720                 ibuild = g_strtod(build, NULL);
2721         skip = ghb_settings_get_int(ud->settings, "update_skip_version");
2722         if (description == NULL || build == NULL || version == NULL 
2723                 || ibuild <= HB_BUILD || skip == ibuild)
2724         {
2725                 if (!stable_update_lock && HB_BUILD % 100)
2726                         g_thread_create((GThreadFunc)check_stable_update, ud, FALSE, NULL);
2727                 goto done;
2728         }
2729         msg = g_strdup_printf("HandBrake %s/%s is now available (you have %s/%d).",
2730                         version, build, HB_VERSION, HB_BUILD);
2731         label = GHB_WIDGET(ud->builder, "update_message");
2732         gtk_label_set_text(GTK_LABEL(label), msg);
2733         html = gtk_html_new_from_string(description, -1);
2734         g_signal_connect(html, "link_clicked", G_CALLBACK(html_link_cb), ud);
2735         window = GHB_WIDGET(ud->builder, "update_scroll");
2736         gtk_container_add(GTK_CONTAINER(window), html);
2737         // Show it
2738         dialog = GHB_WIDGET(ud->builder, "update_dialog");
2739         gtk_widget_set_size_request(html, 420, 240);
2740         gtk_widget_show(html);
2741         response = gtk_dialog_run(GTK_DIALOG(dialog));
2742         gtk_widget_hide(dialog);
2743         gtk_widget_destroy(html);
2744         if (response == GTK_RESPONSE_OK)
2745         {
2746                 // Skip
2747                 ghb_settings_set_int(ud->settings, "update_skip_version", ibuild);
2748                 ghb_pref_save(ud->settings, "update_skip_version");
2749         }
2750         g_free(msg);
2751
2752 done:
2753         if (description) g_free(description);
2754         if (build) g_free(build);
2755         if (version) g_free(version);
2756         g_free(ud->appcast);
2757         ud->appcast_len = 0;
2758         ud->appcast = NULL;
2759 }
2760
2761 void
2762 ghb_net_close(GIOChannel *ioc)
2763 {
2764         gint fd;
2765
2766         g_debug("ghb_net_close");
2767         if (ioc == NULL) return;
2768         fd = g_io_channel_unix_get_fd(ioc);
2769         close(fd);
2770         g_io_channel_unref(ioc);
2771 }
2772
2773 gboolean
2774 ghb_net_recv_cb(GIOChannel *ioc, GIOCondition cond, gpointer data)
2775 {
2776         gchar buf[2048];
2777         gsize len;
2778         GError *gerror = NULL;
2779         GIOStatus status;
2780         
2781         g_debug("ghb_net_recv_cb");
2782         signal_user_data_t *ud = (signal_user_data_t*)data;
2783
2784         status = g_io_channel_read_chars (ioc, buf, 2048, &len, &gerror);
2785         if ((status == G_IO_STATUS_NORMAL || status == G_IO_STATUS_EOF) &&
2786                 len > 0)
2787         {
2788                 gint new_len = ud->appcast_len + len;
2789                 ud->appcast = g_realloc(ud->appcast, new_len + 1);
2790                 memcpy(&(ud->appcast[ud->appcast_len]), buf, len);
2791                 ud->appcast_len = new_len;
2792         }
2793         if (status == G_IO_STATUS_EOF)
2794         {
2795                 ud->appcast[ud->appcast_len] = 0;
2796                 ghb_net_close(ioc);
2797                 process_appcast(ud);
2798                 return FALSE;
2799         }
2800         return TRUE;
2801 }
2802
2803 GIOChannel*
2804 ghb_net_open(signal_user_data_t *ud, gchar *address, gint port)
2805 {
2806         GIOChannel *ioc;
2807         gint fd;
2808
2809         struct sockaddr_in   sock;
2810         struct hostent     * host;
2811
2812         g_debug("ghb_net_open");
2813         if( !( host = gethostbyname( address ) ) )
2814         {
2815                 g_warning( "gethostbyname failed (%s)", address );
2816                 return NULL;
2817         }
2818
2819         memset( &sock, 0, sizeof( struct sockaddr_in ) );
2820         sock.sin_family = host->h_addrtype;
2821         sock.sin_port   = htons( port );
2822         memcpy( &sock.sin_addr, host->h_addr, host->h_length );
2823
2824         fd = socket(host->h_addrtype, SOCK_STREAM, 0);
2825         if( fd < 0 )
2826         {
2827                 g_debug( "socket failed" );
2828                 return NULL;
2829         }
2830
2831         if(connect(fd, (struct sockaddr*)&sock, sizeof(struct sockaddr_in )) < 0 )
2832         {
2833                 g_debug( "connect failed" );
2834                 return NULL;
2835         }
2836         ioc = g_io_channel_unix_new(fd);
2837         g_io_channel_set_encoding (ioc, NULL, NULL);
2838         g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
2839         g_io_add_watch (ioc, G_IO_IN, ghb_net_recv_cb, (gpointer)ud );
2840
2841         return ioc;
2842 }
2843
2844 gpointer
2845 ghb_check_update(signal_user_data_t *ud)
2846 {
2847         gchar *query;
2848         gsize len;
2849         GIOChannel *ioc;
2850         GError *gerror = NULL;
2851
2852         g_debug("ghb_check_update");
2853         if (HB_BUILD % 100)
2854         {
2855         query = 
2856                 "GET /appcast_unstable.xml HTTP/1.0\r\nHost: handbrake.fr\r\n\r\n";
2857         }
2858         else
2859         {
2860                 stable_update_lock = TRUE;
2861         query = "GET /appcast.xml HTTP/1.0\r\nHost: handbrake.fr\r\n\r\n";
2862         }
2863         ioc = ghb_net_open(ud, "handbrake.fr", 80);
2864         if (ioc == NULL)
2865                 return NULL;
2866
2867         g_io_channel_write_chars(ioc, query, strlen(query), &len, &gerror);
2868         g_io_channel_flush(ioc, &gerror);
2869         // This function is initiated by g_idle_add.  Must return false
2870         // so that it is not called again
2871         return NULL;
2872 }
2873
2874 static gpointer
2875 check_stable_update(signal_user_data_t *ud)
2876 {
2877         gchar *query;
2878         gsize len;
2879         GIOChannel *ioc;
2880         GError *gerror = NULL;
2881
2882         g_debug("check_stable_update");
2883         stable_update_lock = TRUE;
2884         query = "GET /appcast.xml HTTP/1.0\r\nHost: handbrake.fr\r\n\r\n";
2885         ioc = ghb_net_open(ud, "handbrake.fr", 80);
2886         if (ioc == NULL)
2887                 return NULL;
2888
2889         g_io_channel_write_chars(ioc, query, strlen(query), &len, &gerror);
2890         g_io_channel_flush(ioc, &gerror);
2891         // This function is initiated by g_idle_add.  Must return false
2892         // so that it is not called again
2893         return NULL;
2894 }
2895