OSDN Git Service

LinGui:
[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 <fcntl.h>
20 #include <sys/stat.h>
21
22 #if !defined(_WIN32)
23 #include <poll.h>
24 #include <libhal-storage.h>
25 #include <dbus/dbus-glib.h>
26 #include <dbus/dbus-glib-lowlevel.h>
27
28 #include <netinet/in.h>
29 #include <netdb.h>
30 #include <gtkhtml/gtkhtml.h>
31 #include <libnotify/notify.h>
32 #else
33 #define WINVER 0x0500
34 #include <winsock2.h>
35 #include <dbt.h>
36 #endif
37
38 #include <gtk/gtk.h>
39 #include <gdk/gdkkeysyms.h>
40 #include <glib/gstdio.h>
41 #include <gio/gio.h>
42
43 #include "hb.h"
44 #include "callbacks.h"
45 #include "queuehandler.h"
46 #include "audiohandler.h"
47 #include "resources.h"
48 #include "settings.h"
49 #include "presets.h"
50 #include "preview.h"
51 #include "values.h"
52 #include "plist.h"
53 #include "appcast.h"
54 #include "hb-backend.h"
55 #include "ghb-dvd.h"
56 #include "ghbcellrenderertext.h"
57
58 static void update_chapter_list(signal_user_data_t *ud);
59 static GList* dvd_device_list();
60 static void prune_logs(signal_user_data_t *ud);
61 void ghb_notify_done(signal_user_data_t *ud);
62
63 // This is a dependency map used for greying widgets
64 // that are dependent on the state of another widget.
65 // The enable_value comes from the values that are
66 // obtained from ghb_widget_value().  For combo boxes
67 // you will have to look further to combo box options
68 // maps in hb-backend.c
69
70 GValue *dep_map;
71 GValue *rev_map;
72
73 void
74 ghb_init_dep_map()
75 {
76         dep_map = ghb_resource_get("widget-deps");
77         rev_map = ghb_resource_get("widget-reverse-deps");
78 }
79
80 static gboolean
81 dep_check(signal_user_data_t *ud, const gchar *name, gboolean *out_hide)
82 {
83         GtkWidget *widget;
84         GObject *dep_object;
85         gint ii;
86         gint count;
87         gboolean result = TRUE;
88         GValue *array, *data;
89         gchar *widget_name;
90         
91         g_debug("dep_check () %s", name);
92
93         if (rev_map == NULL) return TRUE;
94         array = ghb_dict_lookup(rev_map, name);
95         count = ghb_array_len(array);
96         *out_hide = FALSE;
97         for (ii = 0; ii < count; ii++)
98         {
99                 data = ghb_array_get_nth(array, ii);
100                 widget_name = ghb_value_string(ghb_array_get_nth(data, 0));
101                 widget = GHB_WIDGET(ud->builder, widget_name);
102                 dep_object = gtk_builder_get_object(ud->builder, name);
103                 g_free(widget_name);
104                 if (!GTK_WIDGET_SENSITIVE(widget))
105                         continue;
106                 if (dep_object == NULL)
107                 {
108                         g_message("Failed to find widget");
109                 }
110                 else
111                 {
112                         gchar *value;
113                         gint jj = 0;
114                         gchar **values;
115                         gboolean sensitive = FALSE;
116                         gboolean die, hide;
117
118                         die = ghb_value_boolean(ghb_array_get_nth(data, 2));
119                         hide = ghb_value_boolean(ghb_array_get_nth(data, 3));
120                         value = ghb_value_string(ghb_array_get_nth(data, 1));
121                         values = g_strsplit(value, "|", 10);
122                         g_free(value);
123
124                         if (widget)
125                                 value = ghb_widget_string(widget);
126                         else
127                                 value = ghb_settings_get_string(ud->settings, name);
128                         while (values && values[jj])
129                         {
130                                 if (values[jj][0] == '>')
131                                 {
132                                         gdouble dbl = g_strtod (&values[jj][1], NULL);
133                                         gdouble dvalue = ghb_widget_double(widget);
134                                         if (dvalue > dbl)
135                                         {
136                                                 sensitive = TRUE;
137                                                 break;
138                                         }
139                                 }
140                                 else if (values[jj][0] == '<')
141                                 {
142                                         gdouble dbl = g_strtod (&values[jj][1], NULL);
143                                         gdouble dvalue = ghb_widget_double(widget);
144                                         if (dvalue < dbl)
145                                         {
146                                                 sensitive = TRUE;
147                                                 break;
148                                         }
149                                 }
150                                 if (strcmp(values[jj], value) == 0)
151                                 {
152                                         sensitive = TRUE;
153                                         break;
154                                 }
155                                 jj++;
156                         }
157                         sensitive = die ^ sensitive;
158                         if (!sensitive)
159                         {
160                                 result = FALSE;
161                                 *out_hide |= hide;
162                         }
163                         g_strfreev (values);
164                         g_free(value);
165                 }
166         }
167         return result;
168 }
169
170 void
171 ghb_check_dependency(signal_user_data_t *ud, GtkWidget *widget)
172 {
173         GObject *dep_object;
174         const gchar *name;
175         GValue *array, *data;
176         gint count, ii;
177         gchar *dep_name;
178         GType type;
179
180         type = GTK_WIDGET_TYPE(widget);
181         if (type == GTK_TYPE_COMBO_BOX || type == GTK_TYPE_COMBO_BOX_ENTRY)
182                 if (gtk_combo_box_get_active(GTK_COMBO_BOX(widget)) < 0) return;
183
184         name = gtk_widget_get_name(widget);
185         g_debug("ghb_check_dependency () %s", name);
186
187         if (dep_map == NULL) return;
188         array = ghb_dict_lookup(dep_map, name);
189         count = ghb_array_len(array);
190         for (ii = 0; ii < count; ii++)
191         {
192                 gboolean sensitive;
193                 gboolean hide;
194
195                 data = ghb_array_get_nth(array, ii);
196                 dep_name = ghb_value_string(data);
197                 dep_object = gtk_builder_get_object(ud->builder, dep_name);
198                 if (dep_object == NULL)
199                 {
200                         g_message("Failed to find dependent widget %s", dep_name);
201                         g_free(dep_name);
202                         continue;
203                 }
204                 sensitive = dep_check(ud, dep_name, &hide);
205                 g_free(dep_name);
206                 if (GTK_IS_ACTION(dep_object))
207                 {
208                         gtk_action_set_sensitive(GTK_ACTION(dep_object), sensitive);
209                         gtk_action_set_visible(GTK_ACTION(dep_object), sensitive || !hide);
210                 }
211                 else
212                 {
213                         gtk_widget_set_sensitive(GTK_WIDGET(dep_object), sensitive);
214                         if (!sensitive && hide)
215                         {
216                                 gtk_widget_hide(GTK_WIDGET(dep_object));
217                         }
218                         else
219                         {
220                                 gtk_widget_show_now(GTK_WIDGET(dep_object));
221                         }
222                 }
223         }
224 }
225
226 void
227 ghb_check_all_depencencies(signal_user_data_t *ud)
228 {
229         GHashTableIter iter;
230         gchar *dep_name;
231         GValue *value;
232         GObject *dep_object;
233
234         g_debug("ghb_check_all_depencencies ()");
235         if (rev_map == NULL) return;
236         ghb_dict_iter_init(&iter, rev_map);
237         // middle (void*) cast prevents gcc warning "defreferencing type-punned
238         // pointer will break strict-aliasing rules"
239         while (g_hash_table_iter_next(
240                         &iter, (gpointer*)(void*)&dep_name, (gpointer*)(void*)&value))
241         {
242                 gboolean sensitive;
243                 gboolean hide;
244
245                 dep_object = gtk_builder_get_object (ud->builder, dep_name);
246                 if (dep_object == NULL)
247                 {
248                         g_message("Failed to find dependent widget %s", dep_name);
249                         continue;
250                 }
251                 sensitive = dep_check(ud, dep_name, &hide);
252                 if (GTK_IS_ACTION(dep_object))
253                 {
254                         gtk_action_set_sensitive(GTK_ACTION(dep_object), sensitive);
255                         gtk_action_set_visible(GTK_ACTION(dep_object), sensitive || !hide);
256                 }
257                 else
258                 {
259                         gtk_widget_set_sensitive(GTK_WIDGET(dep_object), sensitive);
260                         if (!sensitive && hide)
261                         {
262                                 gtk_widget_hide(GTK_WIDGET(dep_object));
263                         }
264                         else
265                         {
266                                 gtk_widget_show_now(GTK_WIDGET(dep_object));
267                         }
268                 }
269         }
270 }
271
272 G_MODULE_EXPORT void
273 on_quit1_activate(GtkMenuItem *quit, signal_user_data_t *ud)
274 {
275         gint state = ghb_get_queue_state();
276         g_debug("on_quit1_activate ()");
277         if (state & GHB_STATE_WORKING)
278         {
279                 if (ghb_cancel_encode("Closing HandBrake will terminate encoding.\n"))
280                 {
281                         ghb_hb_cleanup(FALSE);
282                         prune_logs(ud);
283                         gtk_main_quit();
284                         return;
285                 }
286                 return;
287         }
288         ghb_hb_cleanup(FALSE);
289         prune_logs(ud);
290         gtk_main_quit();
291 }
292
293 gboolean
294 uppers_and_unders(const gchar *str)
295 {
296         if (str == NULL) return FALSE;
297         while (*str)
298         {
299                 if (*str == ' ')
300                 {
301                         return FALSE;
302                 }
303                 if (*str >= 'a' && *str <= 'z')
304                 {
305                         return FALSE;
306                 }
307                 str++;
308         }
309         return TRUE;
310 }
311
312 enum
313 {
314         CAMEL_FIRST_UPPER,
315         CAMEL_OTHER
316 };
317
318 void
319 camel_convert(gchar *str)
320 {
321         gint state = CAMEL_OTHER;
322         
323         if (str == NULL) return;
324         while (*str)
325         {
326                 if (*str == '_') *str = ' ';
327                 switch (state)
328                 {
329                         case CAMEL_OTHER:
330                         {
331                                 if (*str >= 'A' && *str <= 'Z')
332                                         state = CAMEL_FIRST_UPPER;
333                                 else
334                                         state = CAMEL_OTHER;
335                                 
336                         } break;
337                         case CAMEL_FIRST_UPPER:
338                         {
339                                 if (*str >= 'A' && *str <= 'Z')
340                                         *str = *str - 'A' + 'a';
341                                 else
342                                         state = CAMEL_OTHER;
343                         } break;
344                 }
345                 str++;
346         }
347 }
348
349 #if defined(_WIN32)
350 static gchar*
351 get_dvd_device_name(gchar *device)
352 {
353         return g_strdup(device);
354 }
355 #else
356 static gchar*
357 get_dvd_device_name(GDrive *gd)
358 {
359         return g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
360 }
361 #endif
362
363 #if defined(_WIN32)
364 static gchar*
365 get_dvd_volume_name(const gchar *drive)
366 {
367         gchar *result;
368         gchar vname[51], fsname[51];
369         if (GetVolumeInformation(drive, vname, 50, NULL, NULL, NULL, fsname, 51))
370         {
371                 result = g_strdup_printf("%s (%s)", vname, drive);
372         }
373         else
374         {
375                 result = g_strdup_printf("%s", drive);
376         }
377         return result;
378 }
379 #else
380 static gchar*
381 get_dvd_volume_name(GDrive *gd)
382 {
383         gchar *label;
384         gchar *result;
385         gchar *drive;
386
387         drive = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
388         if (g_drive_has_media (gd))
389         {
390                 label = ghb_dvd_volname (drive);
391                 if (label != NULL)
392                 {
393                         if (uppers_and_unders(label))
394                         {
395                                 camel_convert(label);
396                         }
397                         result = g_strdup_printf("%s - %s", drive, label);
398                         g_free(label);
399                 }
400                 else
401                 {
402                         result = g_strdup_printf("%s", drive);
403                 }
404         }
405         else
406         {
407                 result = g_strdup_printf("%s", drive);
408         }
409         g_free(drive);
410         return result;
411 }
412 #endif
413
414
415 static void
416 set_destination(signal_user_data_t *ud)
417 {
418         g_debug("set_destination");
419         if (ghb_settings_get_boolean(ud->settings, "use_source_name"))
420         {
421                 GString *str = g_string_new("");
422                 gchar *vol_name, *filename, *extension;
423                 gchar *new_name;
424                 gint title;
425                 
426                 filename = ghb_settings_get_string(ud->settings, "dest_file");
427                 extension = ghb_settings_get_string(ud->settings, "FileFormat");
428                 vol_name = ghb_settings_get_string(ud->settings, "volume_label");
429                 g_string_append_printf(str, "%s", vol_name);
430                 title = ghb_settings_combo_int(ud->settings, "title");
431                 if (title >= 0)
432                 {
433                         if (ghb_settings_get_boolean(
434                                         ud->settings, "title_no_in_destination"))
435                         {
436
437                                 title = ghb_settings_combo_int(ud->settings, "title");
438                                 g_string_append_printf(str, " - %d", title+1);
439                         }
440                         if (ghb_settings_get_boolean(
441                                         ud->settings, "chapters_in_destination"))
442                         {
443                                 gint start, end;
444
445                                 if (!ghb_settings_get_boolean(
446                                                 ud->settings, "title_no_in_destination"))
447                                 {
448                                         g_string_append_printf(str, " -");
449                                 }
450                                 start = ghb_settings_get_int(ud->settings, "start_chapter");
451                                 end = ghb_settings_get_int(ud->settings, "end_chapter");
452                                 if (start == end)
453                                         g_string_append_printf(str, " Ch %d", start);
454                                 else
455                                         g_string_append_printf(str, " Ch %d-%d", start, end);
456                         }
457                 }
458                 g_string_append_printf(str, ".%s", extension);
459                 new_name = g_string_free(str, FALSE);
460                 ghb_ui_update(ud, "dest_file", ghb_string_value(new_name));
461                 g_free(filename);
462                 g_free(extension);
463                 g_free(vol_name);
464                 g_free(new_name);
465         }
466 }
467
468 static gchar*
469 get_file_label(const gchar *filename)
470 {
471         static gchar *containers[] = 
472                 {".vob", ".mpg", ".m2ts", ".mkv", ".mp4", ".m4v", ".avi", ".ogm", NULL};
473         gchar *base;
474         gint ii;
475
476         base = g_path_get_basename(filename);
477         for (ii = 0; containers[ii] != NULL; ii++)
478         {
479                 if (g_str_has_suffix(base, containers[ii]))
480                 {
481                         gchar *pos;
482                         pos = strrchr(base, '.');
483                         *pos = 0;
484                         break;
485                 }
486         }
487         return base;
488 }
489
490 static gchar*
491 resolve_drive_name(gchar *filename)
492 {
493 #if defined(_WIN32)
494         if (filename[1] == ':')
495         {
496                 gchar drive[4];
497                 gchar *name;
498                 gint dtype;
499
500                 g_strlcpy(drive, filename, 4);
501                 dtype = GetDriveType(drive);
502                 if (dtype == DRIVE_CDROM)
503                 {
504                         gchar vname[51], fsname[51];
505                         GetVolumeInformation(drive, vname, 50, NULL, 
506                                                                 NULL, NULL, fsname, 50);
507                         name = g_strdup(vname);
508                         return name;
509                 }
510         }
511         return NULL;
512 #else
513         return NULL;
514 #endif
515 }
516
517 static gboolean
518 update_source_label(signal_user_data_t *ud, const gchar *source)
519 {
520         gchar *label = NULL;
521         gint len;
522         gchar **path;
523         gchar *start;
524         gchar *filename = g_strdup(source);
525         
526         len = strlen(filename);
527         if (filename[len-1] == G_DIR_SEPARATOR) filename[len-1] = 0;
528         if (g_file_test(filename, G_FILE_TEST_IS_DIR))
529         {
530                 // Skip dos drive letters
531                 start = strchr(filename, ':');
532                 label = resolve_drive_name(filename);
533                 if (label != NULL)
534                 {
535                         if (uppers_and_unders(label))
536                         {
537                                 camel_convert(label);
538                         }
539                 }
540                 else
541                 {
542                         if (start != NULL)
543                                 start++;
544                         else
545                                 start = filename;
546                         
547                         path = g_strsplit(start, G_DIR_SEPARATOR_S, -1);
548                         len = g_strv_length (path);
549                         if ((len > 1) && (strcmp("VIDEO_TS", path[len-1]) == 0))
550                         {
551                                 label = g_strdup(path[len-2]);
552                                 if (uppers_and_unders(label))
553                                 {
554                                         camel_convert(label);
555                                 }
556                         }
557                         else if (len > 0)
558                         {
559                                 if (path[len-1][0] != 0)
560                                 {
561                                         label = g_strdup(path[len-1]);
562                                         if (uppers_and_unders(label))
563                                         {
564                                                 camel_convert(label);
565                                         }
566                                 }
567                                 else
568                                         label = g_strdup("new_video");
569                         }
570                         else
571                                 label = g_strdup("new_video");
572                         g_strfreev (path);
573                 }
574         }
575         else
576         {
577                 // Is regular file or block dev.
578                 // Check to see if it is a dvd image
579                 label = ghb_dvd_volname (filename);
580                 if (label == NULL)
581                 {
582                         label = get_file_label(filename);
583                 }
584                 else
585                 {
586                         if (uppers_and_unders(label))
587                         {
588                                 camel_convert(label);
589                         }
590                 }
591         }
592         g_free(filename);
593         GtkWidget *widget = GHB_WIDGET (ud->builder, "source_title");
594         if (label != NULL)
595         {
596                 gtk_label_set_text (GTK_LABEL(widget), label);
597                 ghb_settings_set_string(ud->settings, "volume_label", label);
598                 g_free(label);
599                 set_destination(ud);
600         }
601         else
602         {
603                 label = "No Title Found";
604                 gtk_label_set_text (GTK_LABEL(widget), label);
605                 ghb_settings_set_string(ud->settings, "volume_label", label);
606                 return FALSE;
607         }
608         return TRUE;
609 }
610
611 G_MODULE_EXPORT void
612 chooser_file_selected_cb(GtkFileChooser *dialog, signal_user_data_t *ud)
613 {
614         const gchar *name = gtk_file_chooser_get_filename (dialog);
615         GtkTreeModel *store;
616         GtkTreeIter iter;
617         const gchar *device;
618         gboolean foundit = FALSE;
619         GtkComboBox *combo;
620         
621         if (name == NULL) return;
622         combo = GTK_COMBO_BOX(GHB_WIDGET(ud->builder, "source_device"));
623         store = gtk_combo_box_get_model(combo);
624         if (gtk_tree_model_get_iter_first(store, &iter))
625         {
626                 do
627                 {
628                         gtk_tree_model_get(store, &iter, 0, &device, -1);
629                         if (strcmp(name, device) == 0)
630                         {
631                                 foundit = TRUE;
632                                 break;
633                         }
634                 } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter));
635         }
636         if (foundit)
637                 gtk_combo_box_set_active_iter (combo, &iter);
638         else
639                 gtk_combo_box_set_active (combo, 0);
640 }
641
642 G_MODULE_EXPORT void
643 dvd_device_changed_cb(GtkComboBox *combo, signal_user_data_t *ud)
644 {
645         GtkWidget *dialog;
646         gint ii;
647
648         ii = gtk_combo_box_get_active (combo);
649         if (ii > 0)
650         {
651                 const gchar *device, *name;
652
653                 dialog = GHB_WIDGET(ud->builder, "source_dialog");
654                 device = gtk_combo_box_get_active_text (combo);
655                 name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog));
656                 if (name == NULL || strcmp(name, device) != 0)
657                         gtk_file_chooser_select_filename (GTK_FILE_CHOOSER(dialog), device);
658         }
659 }
660
661 G_MODULE_EXPORT void
662 source_type_changed_cb(GtkToggleButton *toggle, signal_user_data_t *ud)
663 {
664         gchar *folder;
665         GtkFileChooser *chooser;
666         GtkWidget *dvd_device_combo;
667         
668         g_debug("source_type_changed_cb ()");
669         chooser = GTK_FILE_CHOOSER(GHB_WIDGET(ud->builder, "source_dialog"));
670         dvd_device_combo = GHB_WIDGET(ud->builder, "source_device");
671         folder = gtk_file_chooser_get_current_folder (chooser);
672         if (gtk_toggle_button_get_active (toggle))
673         {
674                 gtk_file_chooser_set_action (chooser, 
675                                                                         GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
676                 gtk_widget_set_sensitive (dvd_device_combo, FALSE);
677                 gtk_combo_box_set_active (GTK_COMBO_BOX(dvd_device_combo), 0);
678         }
679         else
680         {
681                 gtk_file_chooser_set_action (chooser, GTK_FILE_CHOOSER_ACTION_OPEN);
682                 gtk_widget_set_sensitive (dvd_device_combo, TRUE);
683         }
684         if (folder != NULL)
685         {
686                 gtk_file_chooser_set_current_folder(chooser, folder);
687                 g_free(folder);
688         }
689 }
690
691 static void
692 source_dialog_extra_widgets(
693         signal_user_data_t *ud,
694         GtkWidget *dialog, 
695         gboolean checkbutton_active)
696 {
697         GtkToggleButton *checkbutton;
698         GtkComboBox *combo;
699         GList *drives, *link;
700         
701         checkbutton = GTK_TOGGLE_BUTTON(
702                 GHB_WIDGET(ud->builder, "source_folder_flag"));
703         gtk_toggle_button_set_active(checkbutton, checkbutton_active);
704         combo = GTK_COMBO_BOX(GHB_WIDGET(ud->builder, "source_device"));
705         gtk_list_store_clear(GTK_LIST_STORE(
706                                                 gtk_combo_box_get_model(combo)));
707
708         link = drives = dvd_device_list();
709         gtk_combo_box_append_text (combo, "Not Selected");
710         while (link != NULL)
711         {
712                 gchar *name = get_dvd_volume_name(link->data);
713                 gtk_combo_box_append_text(combo, name);
714                 g_free(name);
715                 g_object_unref(link->data);
716                 link = link->next;
717         }
718         g_list_free(drives);
719 }
720
721 extern GValue *ghb_queue_edit_settings;
722 static gchar *last_scan_file = NULL;
723
724 void
725 ghb_do_scan(
726         signal_user_data_t *ud, 
727         const gchar *filename, 
728         gint titlenum, 
729         gboolean force)
730 {
731         if (!force && last_scan_file != NULL &&
732                 strcmp(last_scan_file, filename) == 0)
733         {
734                 if (ghb_queue_edit_settings)
735                 {
736                         gint jstatus;
737
738                         jstatus = ghb_settings_get_int(ghb_queue_edit_settings, "job_status");
739                         ghb_settings_to_ui(ud, ghb_queue_edit_settings);
740                         ghb_set_audio(ud, ghb_queue_edit_settings);
741                         if (jstatus == GHB_QUEUE_PENDING)
742                         {
743                                 ghb_value_free(ghb_queue_edit_settings);
744                         }
745                         ghb_queue_edit_settings = NULL;
746                 }
747                 return;
748         }
749         if (last_scan_file != NULL)
750                 g_free(last_scan_file);
751         last_scan_file = NULL;
752         if (filename != NULL)
753         {
754                 last_scan_file = g_strdup(filename);
755                 ghb_settings_set_string(ud->settings, "source", filename);
756                 if (update_source_label(ud, filename))
757                 {
758                         GtkProgressBar *progress;
759                         progress = GTK_PROGRESS_BAR(GHB_WIDGET(ud->builder, "progressbar"));
760                         gchar *path;
761                         path = ghb_settings_get_string( ud->settings, "source");
762                         gtk_progress_bar_set_fraction (progress, 0);
763                         gtk_progress_bar_set_text (progress, "Scanning ...");
764                         prune_logs(ud);
765                         gint preview_count;
766                         preview_count = ghb_settings_get_int(ud->settings, "preview_count");
767                         ghb_backend_scan(path, titlenum, preview_count);
768                         g_free(path);
769                 }
770                 else
771                 {
772                         // TODO: error dialog
773                 }
774         }
775 }
776
777 static gboolean 
778 update_source_name(gpointer data)
779 {
780         signal_user_data_t *ud = (signal_user_data_t*)data;
781         GtkWidget *dialog;
782         gchar *sourcename;
783
784         sourcename = ghb_settings_get_string(ud->settings, "source");
785         dialog = GHB_WIDGET(ud->builder, "source_dialog");
786         gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), sourcename);
787         g_free(sourcename);
788         return FALSE;
789 }
790
791 static void
792 do_source_dialog(GtkButton *button, gboolean single, signal_user_data_t *ud)
793 {
794         GtkWidget *dialog;
795         gchar *sourcename;
796         gint    response;
797         GtkFileChooserAction action;
798         gboolean checkbutton_active;
799
800         g_debug("source_browse_clicked_cb ()");
801         sourcename = ghb_settings_get_string(ud->settings, "source");
802         checkbutton_active = FALSE;
803         if (g_file_test(sourcename, G_FILE_TEST_IS_DIR))
804         {
805                 action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
806                 checkbutton_active = TRUE;
807         }
808         else
809         {
810                 action = GTK_FILE_CHOOSER_ACTION_OPEN;
811         }
812         GtkWidget *widget;
813         widget = GHB_WIDGET(ud->builder, "single_title_box");
814         if (single)
815                 gtk_widget_show(widget);
816         else
817                 gtk_widget_hide(widget);
818         dialog = GHB_WIDGET(ud->builder, "source_dialog");
819         source_dialog_extra_widgets(ud, dialog, checkbutton_active);
820         gtk_file_chooser_set_action(GTK_FILE_CHOOSER(dialog), action);
821         // Updating the filename in the file chooser dialog doesn't seem
822         // to work unless the dialog is running for some reason.
823         // So handle it in an "idle" event.
824         //gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), sourcename);
825         g_idle_add((GSourceFunc)update_source_name, ud);
826         response = gtk_dialog_run(GTK_DIALOG (dialog));
827         gtk_widget_hide(dialog);
828         if (response == GTK_RESPONSE_ACCEPT)
829         {
830                 char *filename;
831
832                 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
833                 if (filename != NULL)
834                 {
835                         gint titlenum;
836
837                         if (single)
838                                 titlenum = ghb_settings_get_int(ud->settings, "single_title");
839                         else
840                                 titlenum = 0;
841                         ghb_do_scan(ud, filename, titlenum, TRUE);
842                         if (strcmp(sourcename, filename) != 0)
843                         {
844                                 ghb_settings_set_string (ud->settings, 
845                                                                                 "default_source", filename);
846                                 ghb_pref_save (ud->settings, "default_source");
847                                 ghb_dvd_set_current (filename, ud);
848                         }
849                         g_free(filename);
850                 }
851         }
852         g_free(sourcename);
853 }
854
855 G_MODULE_EXPORT void
856 source_button_clicked_cb(GtkButton *button, signal_user_data_t *ud)
857 {
858         do_source_dialog(button, FALSE, ud);
859 }
860
861 G_MODULE_EXPORT void
862 single_title_source_cb(GtkButton *button, signal_user_data_t *ud)
863 {
864         do_source_dialog(button, TRUE, ud);
865 }
866
867 G_MODULE_EXPORT void
868 dvd_source_activate_cb(GtkAction *action, signal_user_data_t *ud)
869 {
870         const gchar *filename;
871         gchar *sourcename;
872
873         sourcename = ghb_settings_get_string(ud->settings, "source");
874         filename = gtk_action_get_name(action);
875         ghb_do_scan(ud, filename, 0, TRUE);
876         if (strcmp(sourcename, filename) != 0)
877         {
878                 ghb_settings_set_string (ud->settings, "default_source", filename);
879                 ghb_pref_save (ud->settings, "default_source");
880                 ghb_dvd_set_current (filename, ud);
881         }
882         g_free(sourcename);
883 }
884
885 static void
886 update_destination_extension(signal_user_data_t *ud)
887 {
888         static gchar *containers[] = {".mkv", ".mp4", ".m4v", ".avi", ".ogm", NULL};
889         gchar *filename;
890         gchar *extension;
891         gint ii;
892         GtkEntry *entry;
893
894         g_debug("update_destination_extension ()");
895         extension = ghb_settings_get_string(ud->settings, "FileFormat");
896         entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "dest_file"));
897         filename = g_strdup(gtk_entry_get_text(entry));
898         for (ii = 0; containers[ii] != NULL; ii++)
899         {
900                 if (g_str_has_suffix(filename, containers[ii]))
901                 {
902                         gchar *pos;
903                         gchar *new_name;
904                         
905                         pos = g_strrstr( filename, "." );
906                         if (pos == NULL)
907                         {
908                                 // No period? shouldn't happen
909                                 break;
910                         }
911                         *pos = 0;
912                         if (strcmp(extension, &pos[1]) == 0)
913                         {
914                                 // Extension is already correct
915                                 break;
916                         }
917                         new_name = g_strjoin(".", filename, extension, NULL); 
918                         ghb_ui_update(ud, "dest_file", ghb_string_value(new_name));
919                         g_free(new_name);
920                         break;
921                 }
922         }
923         g_free(extension);
924         g_free(filename);
925 }
926
927 static void
928 destination_select_title(GtkEntry *entry)
929 {
930         const gchar *dest;
931         gint start, end;
932
933         dest = gtk_entry_get_text(entry);
934         for (end = strlen(dest)-1; end > 0; end--)
935         {
936                 if (dest[end] == '.')
937                 {
938                         break;
939                 }
940         }
941         for (start = end; start >= 0; start--)
942         {
943                 if (dest[start] == G_DIR_SEPARATOR)
944                 {
945                         start++;
946                         break;
947                 }
948         }
949         if (start < 0) start = 0;
950         if (start < end)
951         {
952                 gtk_editable_select_region(GTK_EDITABLE(entry), start, end);
953         }
954 }
955
956 G_MODULE_EXPORT gboolean
957 destination_grab_cb(
958         GtkEntry *entry, 
959         signal_user_data_t *ud)
960 {
961         destination_select_title(entry);
962         return FALSE;
963 }
964
965 static gboolean update_default_destination = FALSE;
966
967 G_MODULE_EXPORT void
968 dest_dir_set_cb(GtkFileChooserButton *dest_chooser, signal_user_data_t *ud)
969 {
970         gchar *dest_file, *dest_dir, *dest;
971         
972         g_debug("dest_dir_set_cb ()");
973         ghb_widget_to_setting(ud->settings, (GtkWidget*)dest_chooser);
974         dest_file = ghb_settings_get_string(ud->settings, "dest_file");
975         dest_dir = ghb_settings_get_string(ud->settings, "dest_dir");
976         dest = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dest_dir, dest_file);
977         ghb_settings_set_string(ud->settings, "destination", dest);
978         g_free(dest_file);
979         g_free(dest_dir);
980         g_free(dest);
981         update_default_destination = TRUE;
982 }
983
984 G_MODULE_EXPORT void
985 dest_file_changed_cb(GtkEntry *entry, signal_user_data_t *ud)
986 {
987         gchar *dest_file, *dest_dir, *dest;
988         
989         g_debug("dest_file_changed_cb ()");
990         update_destination_extension(ud);
991         ghb_widget_to_setting(ud->settings, (GtkWidget*)entry);
992         // This signal goes off with ever keystroke, so I'm putting this
993         // update on the timer.
994         dest_file = ghb_settings_get_string(ud->settings, "dest_file");
995         dest_dir = ghb_settings_get_string(ud->settings, "dest_dir");
996         dest = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dest_dir, dest_file);
997         ghb_settings_set_string(ud->settings, "destination", dest);
998         g_free(dest_file);
999         g_free(dest_dir);
1000         g_free(dest);
1001         update_default_destination = TRUE;
1002 }
1003
1004 G_MODULE_EXPORT void
1005 destination_browse_clicked_cb(GtkButton *button, signal_user_data_t *ud)
1006 {
1007         GtkWidget *dialog;
1008         GtkEntry *entry;
1009         gchar *destname;
1010         gchar *basename;
1011
1012         g_debug("destination_browse_clicked_cb ()");
1013         destname = ghb_settings_get_string(ud->settings, "destination");
1014         dialog = gtk_file_chooser_dialog_new ("Choose Destination",
1015                                           NULL,
1016                                           GTK_FILE_CHOOSER_ACTION_SAVE,
1017                                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1018                                           GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1019                                           NULL);
1020         gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), destname);
1021         basename = g_path_get_basename(destname);
1022         g_free(destname);
1023         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), basename);
1024         g_free(basename);
1025         if (gtk_dialog_run(GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
1026         {
1027                 char *filename, *dirname;
1028                 GtkFileChooser *dest_chooser;
1029                 
1030                 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
1031                 basename = g_path_get_basename(filename);
1032                 dirname = g_path_get_dirname(filename);
1033                 entry = (GtkEntry*)GHB_WIDGET(ud->builder, "dest_file");
1034                 gtk_entry_set_text(entry, basename);
1035                 dest_chooser = GTK_FILE_CHOOSER(GHB_WIDGET(ud->builder, "dest_dir"));
1036                 gtk_file_chooser_set_filename(dest_chooser, dirname);
1037                 g_free (dirname);
1038                 g_free (basename);
1039                 g_free (filename);
1040         }
1041         gtk_widget_destroy(dialog);
1042 }
1043
1044 G_MODULE_EXPORT gboolean
1045 window_destroy_event_cb(GtkWidget *widget, GdkEvent *event, signal_user_data_t *ud)
1046 {
1047         g_debug("window_destroy_event_cb ()");
1048         ghb_hb_cleanup(FALSE);
1049         prune_logs(ud);
1050         gtk_main_quit();
1051         return FALSE;
1052 }
1053
1054 G_MODULE_EXPORT gboolean
1055 window_delete_event_cb(GtkWidget *widget, GdkEvent *event, signal_user_data_t *ud)
1056 {
1057         gint state = ghb_get_queue_state();
1058         g_debug("window_delete_event_cb ()");
1059         if (state & GHB_STATE_WORKING)
1060         {
1061                 if (ghb_cancel_encode("Closing HandBrake will terminate encoding.\n"))
1062                 {
1063                         ghb_hb_cleanup(FALSE);
1064                         prune_logs(ud);
1065                         gtk_main_quit();
1066                         return FALSE;
1067                 }
1068                 return TRUE;
1069         }
1070         ghb_hb_cleanup(FALSE);
1071         prune_logs(ud);
1072         gtk_main_quit();
1073         return FALSE;
1074 }
1075
1076 static void
1077 update_acodec_combo(signal_user_data_t *ud)
1078 {
1079         ghb_grey_combo_options (ud->builder);
1080 }
1081
1082 G_MODULE_EXPORT void
1083 container_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1084 {
1085         const GValue *audio_list;
1086         gboolean markers;
1087
1088         g_debug("container_changed_cb ()");
1089         ghb_widget_to_setting(ud->settings, widget);
1090         update_destination_extension(ud);
1091         ghb_check_dependency(ud, widget);
1092         update_acodec_combo(ud);
1093         ghb_clear_presets_selection(ud);
1094         ghb_live_reset(ud);
1095
1096         audio_list = ghb_settings_get_value(ud->settings, "audio_list");
1097         if (ghb_ac3_in_audio_list (audio_list))
1098         {
1099                 gchar *container;
1100
1101                 container = ghb_settings_get_string(ud->settings, "FileFormat");
1102                 if (strcmp(container, "mp4") == 0)
1103                 {
1104                         ghb_ui_update(ud, "FileFormat", ghb_string_value("m4v"));
1105                 }
1106                 g_free(container);
1107         }
1108         markers = ghb_settings_get_boolean(ud->settings, "ChapterMarkers");
1109         if (markers)
1110         {
1111                 gchar *container;
1112
1113                 container = ghb_settings_get_string(ud->settings, "FileFormat");
1114                 if (strcmp(container, "mp4") == 0)
1115                 {
1116                         ghb_ui_update(ud, "FileFormat", ghb_string_value("m4v"));
1117                 }
1118                 g_free(container);
1119         }
1120 }
1121
1122 static gchar*
1123 get_aspect_string(gint aspect_n, gint aspect_d)
1124 {
1125         gchar *aspect;
1126
1127         if (aspect_d < 10)
1128         {
1129                 aspect = g_strdup_printf("%d:%d", aspect_n, aspect_d);
1130         }
1131         else
1132         {
1133                 gdouble aspect_nf = (gdouble)aspect_n / aspect_d;
1134                 aspect = g_strdup_printf("%.2f:1", aspect_nf);
1135         }
1136         return aspect;
1137 }
1138
1139 static gchar*
1140 get_rate_string(gint rate_base, gint rate)
1141 {
1142         gdouble rate_f = (gdouble)rate / rate_base;
1143         gchar *rate_s;
1144
1145         rate_s = g_strdup_printf("%.6g", rate_f);
1146         return rate_s;
1147 }
1148 static void
1149 show_title_info(signal_user_data_t *ud, ghb_title_info_t *tinfo)
1150 {
1151         GtkWidget *widget;
1152         gchar *text;
1153
1154         ud->dont_clear_presets = TRUE;
1155         widget = GHB_WIDGET (ud->builder, "title_duration");
1156         if (tinfo->duration != 0)
1157         {
1158                 text = g_strdup_printf ("%02d:%02d:%02d", tinfo->hours, 
1159                                 tinfo->minutes, tinfo->seconds);
1160         }
1161         else
1162         {
1163                 text = g_strdup_printf ("Unknown");
1164         }
1165         gtk_label_set_text (GTK_LABEL(widget), text);
1166         g_free(text);
1167         widget = GHB_WIDGET (ud->builder, "source_dimensions");
1168         text = g_strdup_printf ("%d x %d", tinfo->width, tinfo->height);
1169         gtk_label_set_text (GTK_LABEL(widget), text);
1170         ghb_settings_set_int(ud->settings, "source_width", tinfo->width);
1171         ghb_settings_set_int(ud->settings, "source_height", tinfo->height);
1172         g_free(text);
1173         widget = GHB_WIDGET (ud->builder, "source_aspect");
1174         text = get_aspect_string(tinfo->aspect_n, tinfo->aspect_d);
1175         gtk_label_set_text (GTK_LABEL(widget), text);
1176         g_free(text);
1177
1178         widget = GHB_WIDGET (ud->builder, "source_frame_rate");
1179         text = (gchar*)get_rate_string(tinfo->rate_base, tinfo->rate);
1180         gtk_label_set_text (GTK_LABEL(widget), text);
1181         g_free(text);
1182
1183         ghb_ui_update(ud, "scale_width", 
1184                 ghb_int64_value(tinfo->width - tinfo->crop[2] - tinfo->crop[3]));
1185         // If anamorphic or keep_aspect, the hight will be automatically calculated
1186         gboolean keep_aspect;
1187         gint pic_par;
1188         keep_aspect = ghb_settings_get_boolean(ud->settings, "PictureKeepRatio");
1189         pic_par = ghb_settings_combo_int(ud->settings, "PicturePAR");
1190         if (!(keep_aspect || pic_par) || pic_par == 3)
1191         {
1192                 ghb_ui_update(ud, "scale_height", 
1193                         ghb_int64_value(tinfo->height - tinfo->crop[0] - tinfo->crop[1]));
1194         }
1195
1196         // Set the limits of cropping.  hb_set_anamorphic_size crashes if
1197         // you pass it a cropped width or height == 0.
1198         gint bound;
1199         bound = tinfo->height / 2 - 2;
1200         widget = GHB_WIDGET (ud->builder, "PictureTopCrop");
1201         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
1202         widget = GHB_WIDGET (ud->builder, "PictureBottomCrop");
1203         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
1204         bound = tinfo->width / 2 - 2;
1205         widget = GHB_WIDGET (ud->builder, "PictureLeftCrop");
1206         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
1207         widget = GHB_WIDGET (ud->builder, "PictureRightCrop");
1208         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
1209         if (ghb_settings_get_boolean(ud->settings, "PictureAutoCrop"))
1210         {
1211                 ghb_ui_update(ud, "PictureTopCrop", ghb_int64_value(tinfo->crop[0]));
1212                 ghb_ui_update(ud, "PictureBottomCrop", ghb_int64_value(tinfo->crop[1]));
1213                 ghb_ui_update(ud, "PictureLeftCrop", ghb_int64_value(tinfo->crop[2]));
1214                 ghb_ui_update(ud, "PictureRightCrop", ghb_int64_value(tinfo->crop[3]));
1215         }
1216         ghb_set_scale (ud, GHB_PIC_KEEP_PAR);
1217         gint width, height, crop[4];
1218         crop[0] = ghb_settings_get_int(ud->settings, "PictureTopCrop");
1219         crop[1] = ghb_settings_get_int(ud->settings, "PictureBottomCrop");
1220         crop[2] = ghb_settings_get_int(ud->settings, "PictureLeftCrop");
1221         crop[3] = ghb_settings_get_int(ud->settings, "PictureRightCrop");
1222         width = tinfo->width - crop[2] - crop[3];
1223         height = tinfo->height - crop[0] - crop[1];
1224         widget = GHB_WIDGET (ud->builder, "crop_dimensions");
1225         text = g_strdup_printf ("%d x %d", width, height);
1226         gtk_label_set_text (GTK_LABEL(widget), text);
1227         g_free(text);
1228
1229         g_debug("setting max end chapter %d", tinfo->num_chapters);
1230         widget = GHB_WIDGET (ud->builder, "end_chapter");
1231         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, tinfo->num_chapters);
1232         gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), tinfo->num_chapters);
1233         widget = GHB_WIDGET (ud->builder, "start_chapter");
1234         gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), 1);
1235         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, tinfo->num_chapters);
1236         ud->dont_clear_presets = FALSE;
1237 }
1238
1239 static gboolean update_preview = FALSE;
1240
1241 G_MODULE_EXPORT void
1242 title_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1243 {
1244         ghb_title_info_t tinfo;
1245         gint titleindex;
1246         
1247         g_debug("title_changed_cb ()");
1248         ghb_widget_to_setting(ud->settings, widget);
1249         ghb_check_dependency(ud, widget);
1250
1251         titleindex = ghb_settings_combo_int(ud->settings, "title");
1252         ghb_update_ui_combo_box (ud->builder, "AudioTrack", titleindex, FALSE);
1253         ghb_update_ui_combo_box (ud->builder, "Subtitles", titleindex, FALSE);
1254
1255         ghb_update_from_preset(ud, "Subtitles");
1256         if (ghb_get_title_info (&tinfo, titleindex))
1257         {
1258                 show_title_info(ud, &tinfo);
1259         }
1260         update_chapter_list (ud);
1261         ghb_adjust_audio_rate_combos(ud);
1262         ghb_set_pref_audio(titleindex, ud);
1263         if (ghb_settings_get_boolean(ud->settings, "vquality_type_target"))
1264         {
1265                 gint bitrate = ghb_calculate_target_bitrate (ud->settings, titleindex);
1266                 ghb_ui_update(ud, "VideoAvgBitrate", ghb_int64_value(bitrate));
1267         }
1268
1269         // Unfortunately, there is no way to query how many frames were
1270         // actually generated during the scan.
1271         // If I knew how many were generated, I would adjust the spin
1272         // control range here.
1273         // I do know how many were asked for.
1274         gint preview_count;
1275         preview_count = ghb_settings_get_int(ud->settings, "preview_count");
1276         widget = GHB_WIDGET(ud->builder, "preview_frame");
1277         gtk_range_set_range (GTK_RANGE(widget), 1, preview_count);
1278         ghb_ui_update(ud, "preview_frame", ghb_int64_value(2));
1279
1280         ghb_set_preview_image (ud);
1281         if (ghb_settings_get_boolean(ud->settings, "title_no_in_destination"))
1282         {
1283                 set_destination(ud);
1284         }
1285 }
1286
1287 G_MODULE_EXPORT void
1288 setting_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1289 {
1290         ghb_widget_to_setting(ud->settings, widget);
1291         ghb_check_dependency(ud, widget);
1292         ghb_clear_presets_selection(ud);
1293         ghb_live_reset(ud);
1294 }
1295
1296 G_MODULE_EXPORT void
1297 chapter_markers_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1298 {
1299         gboolean markers;
1300
1301         ghb_widget_to_setting(ud->settings, widget);
1302         ghb_check_dependency(ud, widget);
1303         ghb_clear_presets_selection(ud);
1304         ghb_live_reset(ud);
1305         markers = ghb_settings_get_boolean(ud->settings, "ChapterMarkers");
1306         if (markers)
1307         {
1308                 gchar *container;
1309
1310                 container = ghb_settings_get_string(ud->settings, "FileFormat");
1311                 if (strcmp(container, "mp4") == 0)
1312                 {
1313                         ghb_ui_update(ud, "FileFormat", ghb_string_value("m4v"));
1314                 }
1315                 g_free(container);
1316         }
1317 }
1318
1319 G_MODULE_EXPORT void
1320 vquality_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1321 {
1322         ghb_widget_to_setting(ud->settings, widget);
1323         ghb_check_dependency(ud, widget);
1324         ghb_clear_presets_selection(ud);
1325         ghb_live_reset(ud);
1326
1327         gint vcodec = ghb_settings_combo_int(ud->settings, "VideoEncoder");
1328         gdouble step;
1329         if (vcodec == HB_VCODEC_X264)
1330         {
1331                 step = ghb_settings_combo_double(ud->settings, 
1332                                                                                         "VideoQualityGranularity");
1333         }
1334         else
1335         {
1336                 step = 1;
1337         }
1338         gdouble val = gtk_range_get_value(GTK_RANGE(widget));
1339         val = ((int)((val + step / 2) / step)) * step;
1340         gtk_range_set_value(GTK_RANGE(widget), val);
1341 }
1342
1343 G_MODULE_EXPORT void
1344 http_opt_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1345 {
1346         ghb_widget_to_setting(ud->settings, widget);
1347         ghb_check_dependency(ud, widget);
1348         ghb_clear_presets_selection(ud);
1349         ghb_live_reset(ud);
1350         // AC3 is not allowed when Web optimized
1351         ghb_grey_combo_options (ud->builder);
1352 }
1353
1354 G_MODULE_EXPORT void
1355 vcodec_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1356 {
1357         gdouble vqmin, vqmax, step, page;
1358         gboolean inverted;
1359         gint digits;
1360
1361         ghb_widget_to_setting(ud->settings, widget);
1362         ghb_check_dependency(ud, widget);
1363         ghb_clear_presets_selection(ud);
1364         ghb_live_reset(ud);
1365         ghb_vquality_range(ud, &vqmin, &vqmax, &step, &page, &digits, &inverted);
1366         GtkWidget *qp = GHB_WIDGET(ud->builder, "VideoQualitySlider");
1367         gtk_range_set_range (GTK_RANGE(qp), vqmin, vqmax);
1368         gtk_range_set_increments (GTK_RANGE(qp), step, page);
1369         gtk_scale_set_digits(GTK_SCALE(qp), digits);
1370         gtk_range_set_inverted (GTK_RANGE(qp), inverted);
1371 }
1372
1373 G_MODULE_EXPORT void
1374 target_size_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1375 {
1376         const gchar *name = gtk_widget_get_name(widget);
1377         g_debug("target_size_changed_cb () %s", name);
1378         ghb_widget_to_setting(ud->settings, widget);
1379         ghb_check_dependency(ud, widget);
1380         ghb_clear_presets_selection(ud);
1381         ghb_live_reset(ud);
1382         if (ghb_settings_get_boolean(ud->settings, "vquality_type_target"))
1383         {
1384                 gint titleindex;
1385                 titleindex = ghb_settings_combo_int(ud->settings, "title");
1386                 gint bitrate = ghb_calculate_target_bitrate (ud->settings, titleindex);
1387                 ghb_ui_update(ud, "VideoAvgBitrate", ghb_int64_value(bitrate));
1388         }
1389 }
1390
1391 G_MODULE_EXPORT void
1392 start_chapter_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1393 {
1394         gint start, end;
1395         const gchar *name = gtk_widget_get_name(widget);
1396
1397         g_debug("start_chapter_changed_cb () %s", name);
1398         ghb_widget_to_setting(ud->settings, widget);
1399         start = ghb_settings_get_int(ud->settings, "start_chapter");
1400         end = ghb_settings_get_int(ud->settings, "end_chapter");
1401         if (start > end)
1402                 ghb_ui_update(ud, "end_chapter", ghb_int_value(start));
1403         ghb_check_dependency(ud, widget);
1404         if (ghb_settings_get_boolean(ud->settings, "chapters_in_destination"))
1405         {
1406                 set_destination(ud);
1407         }
1408 }
1409
1410 G_MODULE_EXPORT void
1411 end_chapter_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1412 {
1413         gint start, end;
1414         const gchar *name = gtk_widget_get_name(widget);
1415
1416         g_debug("end_chapter_changed_cb () %s", name);
1417         ghb_widget_to_setting(ud->settings, widget);
1418         start = ghb_settings_get_int(ud->settings, "start_chapter");
1419         end = ghb_settings_get_int(ud->settings, "end_chapter");
1420         if (start > end)
1421                 ghb_ui_update(ud, "start_chapter", ghb_int_value(end));
1422         ghb_check_dependency(ud, widget);
1423         if (ghb_settings_get_boolean(ud->settings, "chapters_in_destination"))
1424         {
1425                 set_destination(ud);
1426         }
1427 }
1428
1429 G_MODULE_EXPORT void
1430 scale_width_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1431 {
1432         g_debug("scale_width_changed_cb ()");
1433         ghb_widget_to_setting(ud->settings, widget);
1434         ghb_check_dependency(ud, widget);
1435         ghb_clear_presets_selection(ud);
1436         if (GTK_WIDGET_SENSITIVE(widget))
1437                 ghb_set_scale (ud, GHB_PIC_KEEP_WIDTH);
1438         update_preview = TRUE;
1439         gchar *text;
1440         gint width = ghb_settings_get_int(ud->settings, "scale_width");
1441         gint height = ghb_settings_get_int(ud->settings, "scale_height");
1442         widget = GHB_WIDGET (ud->builder, "scale_dimensions");
1443         text = g_strdup_printf ("%d x %d", width, height);
1444         gtk_label_set_text (GTK_LABEL(widget), text);
1445         g_free(text);
1446         ghb_live_reset(ud);
1447 }
1448
1449 G_MODULE_EXPORT void
1450 scale_height_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1451 {
1452         g_debug("scale_height_changed_cb ()");
1453         ghb_widget_to_setting(ud->settings, widget);
1454         ghb_check_dependency(ud, widget);
1455         ghb_clear_presets_selection(ud);
1456         if (GTK_WIDGET_SENSITIVE(widget))
1457                 ghb_set_scale (ud, GHB_PIC_KEEP_HEIGHT);
1458         update_preview = TRUE;
1459         gchar *text;
1460         gint width = ghb_settings_get_int(ud->settings, "scale_width");
1461         gint height = ghb_settings_get_int(ud->settings, "scale_height");
1462         widget = GHB_WIDGET (ud->builder, "scale_dimensions");
1463         text = g_strdup_printf ("%d x %d", width, height);
1464         gtk_label_set_text (GTK_LABEL(widget), text);
1465         g_free(text);
1466         ghb_live_reset(ud);
1467 }
1468
1469 G_MODULE_EXPORT void
1470 crop_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1471 {
1472         gint titleindex, crop[4];
1473         ghb_title_info_t tinfo;
1474         
1475         g_debug("crop_changed_cb ()");
1476         ghb_widget_to_setting(ud->settings, widget);
1477         ghb_check_dependency(ud, widget);
1478         ghb_clear_presets_selection(ud);
1479         if (GTK_WIDGET_SENSITIVE(widget))
1480                 ghb_set_scale (ud, 0);
1481
1482         crop[0] = ghb_settings_get_int(ud->settings, "PictureTopCrop");
1483         crop[1] = ghb_settings_get_int(ud->settings, "PictureBottomCrop");
1484         crop[2] = ghb_settings_get_int(ud->settings, "PictureLeftCrop");
1485         crop[3] = ghb_settings_get_int(ud->settings, "PictureRightCrop");
1486         titleindex = ghb_settings_combo_int(ud->settings, "title");
1487         if (ghb_get_title_info (&tinfo, titleindex))
1488         {
1489                 gint width, height;
1490                 gchar *text;
1491                 
1492                 width = tinfo.width - crop[2] - crop[3];
1493                 height = tinfo.height - crop[0] - crop[1];
1494                 widget = GHB_WIDGET (ud->builder, "crop_dimensions");
1495                 text = g_strdup_printf ("%d x %d", width, height);
1496                 gtk_label_set_text (GTK_LABEL(widget), text);
1497                 widget = GHB_WIDGET (ud->builder, "crop_dimensions2");
1498                 gtk_label_set_text (GTK_LABEL(widget), text);
1499                 g_free(text);
1500         }
1501         gchar *text;
1502         widget = GHB_WIDGET (ud->builder, "crop_values");
1503         text = g_strdup_printf ("%d:%d:%d:%d", crop[0], crop[1], crop[2], crop[3]);
1504         gtk_label_set_text (GTK_LABEL(widget), text);
1505         g_free(text);
1506         update_preview = TRUE;
1507         ghb_live_reset(ud);
1508 }
1509
1510 G_MODULE_EXPORT void
1511 display_width_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1512 {
1513         g_debug("scale_changed_cb ()");
1514         ghb_widget_to_setting(ud->settings, widget);
1515         ghb_check_dependency(ud, widget);
1516         ghb_clear_presets_selection(ud);
1517         ghb_live_reset(ud);
1518         if (GTK_WIDGET_SENSITIVE(widget))
1519                 ghb_set_scale (ud, GHB_PIC_KEEP_DISPLAY_WIDTH);
1520
1521         gint pic_par;
1522
1523         pic_par = ghb_settings_combo_int(ud->settings, "PicturePAR");
1524         if (pic_par == 3)
1525         {
1526                 gint par_width, par_height;
1527
1528                 par_width = ghb_settings_get_int(ud->settings, "par_width");
1529                 par_height = ghb_settings_get_int(ud->settings, "par_height");
1530                 ghb_settings_set_int(ud->settings, "PicturePARWidth", par_width);
1531                 ghb_settings_set_int(ud->settings, "PicturePARHeight", par_height);
1532         }
1533         update_preview = TRUE;
1534 }
1535
1536 void
1537 display_height_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1538 {
1539         g_debug("scale_changed_cb ()");
1540         ghb_widget_to_setting(ud->settings, widget);
1541         ghb_check_dependency(ud, widget);
1542         ghb_clear_presets_selection(ud);
1543         ghb_live_reset(ud);
1544         if (GTK_WIDGET_SENSITIVE(widget))
1545                 ghb_set_scale (ud, GHB_PIC_KEEP_DISPLAY_HEIGHT);
1546
1547         gint pic_par;
1548
1549         pic_par = ghb_settings_combo_int(ud->settings, "PicturePAR");
1550         if (pic_par == 3)
1551         {
1552                 gint par_width, par_height;
1553
1554                 par_width = ghb_settings_get_int(ud->settings, "par_width");
1555                 par_height = ghb_settings_get_int(ud->settings, "par_height");
1556                 ghb_settings_set_int(ud->settings, "PicturePARWidth", par_width);
1557                 ghb_settings_set_int(ud->settings, "PicturePARHeight", par_height);
1558         }
1559         update_preview = TRUE;
1560 }
1561
1562 void
1563 scale_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1564 {
1565         g_debug("scale_changed_cb ()");
1566         ghb_widget_to_setting(ud->settings, widget);
1567         ghb_check_dependency(ud, widget);
1568         ghb_clear_presets_selection(ud);
1569         ghb_live_reset(ud);
1570         if (GTK_WIDGET_SENSITIVE(widget))
1571                 ghb_set_scale (ud, 0);
1572         update_preview = TRUE;
1573         
1574         gchar *text;
1575         
1576         text = ghb_settings_get_boolean(ud->settings, "PictureAutoCrop") ? "On" : "Off";
1577         widget = GHB_WIDGET (ud->builder, "crop_auto");
1578         gtk_label_set_text (GTK_LABEL(widget), text);
1579         text = ghb_settings_get_boolean(ud->settings, "autoscale") ? "On" : "Off";
1580         widget = GHB_WIDGET (ud->builder, "scale_auto");
1581         gtk_label_set_text (GTK_LABEL(widget), text);
1582         switch (ghb_settings_combo_int(ud->settings, "PicturePAR"))
1583         {
1584                 case 0:
1585                         text = "Off";
1586                         break;
1587                 case 1:
1588                         text = "Strict";
1589                         break;
1590                 case 2:
1591                         text = "Loose";
1592                         break;
1593                 case 3:
1594                         text = "Custom";
1595                         break;
1596                 default:
1597                         text = "Unknown";
1598                         break;
1599         }
1600         widget = GHB_WIDGET (ud->builder, "scale_anamorphic");
1601         gtk_label_set_text (GTK_LABEL(widget), text);
1602 }
1603
1604 G_MODULE_EXPORT void
1605 generic_entry_changed_cb(GtkEntry *entry, signal_user_data_t *ud)
1606 {
1607         // Normally (due to user input) I only want to process the entry
1608         // when editing is done and the focus-out signal is sent.
1609         // But... there's always a but.
1610         // If the entry is changed by software, the focus-out signal is not sent.
1611         // The changed signal is sent ... so here we are.
1612         // I don't want to process upon every keystroke, so I prevent processing
1613         // while the widget has focus.
1614         g_debug("generic_entry_changed_cb ()");
1615         if (!GTK_WIDGET_HAS_FOCUS((GtkWidget*)entry))
1616         {
1617                 ghb_widget_to_setting(ud->settings, (GtkWidget*)entry);
1618         }
1619 }
1620
1621 G_MODULE_EXPORT void
1622 prefs_dialog_cb(GtkWidget *xwidget, signal_user_data_t *ud)
1623 {
1624         GtkWidget *dialog;
1625         GtkResponseType response;
1626
1627         g_debug("prefs_dialog_cb ()");
1628         dialog = GHB_WIDGET(ud->builder, "prefs_dialog");
1629         response = gtk_dialog_run(GTK_DIALOG(dialog));
1630         gtk_widget_hide(dialog);
1631 }
1632
1633 gboolean
1634 ghb_message_dialog(GtkMessageType type, const gchar *message, const gchar *no, const gchar *yes)
1635 {
1636         GtkWidget *dialog;
1637         GtkResponseType response;
1638                         
1639         // Toss up a warning dialog
1640         dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
1641                                                         type, GTK_BUTTONS_NONE,
1642                                                         "%s", message);
1643         gtk_dialog_add_buttons( GTK_DIALOG(dialog), 
1644                                                    no, GTK_RESPONSE_NO,
1645                                                    yes, GTK_RESPONSE_YES, NULL);
1646         response = gtk_dialog_run(GTK_DIALOG(dialog));
1647         gtk_widget_destroy (dialog);
1648         if (response == GTK_RESPONSE_NO)
1649         {
1650                 return FALSE;
1651         }
1652         return TRUE;
1653 }
1654
1655 gboolean
1656 ghb_cancel_encode(const gchar *extra_msg)
1657 {
1658         GtkWidget *dialog;
1659         GtkResponseType response;
1660         
1661         if (extra_msg == NULL) extra_msg = "";
1662         // Toss up a warning dialog
1663         dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
1664                                 GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE,
1665                                 "%sYour movie will be lost if you don't continue encoding.",
1666                                 extra_msg);
1667         gtk_dialog_add_buttons( GTK_DIALOG(dialog), 
1668                                                    "Continue Encoding", GTK_RESPONSE_NO,
1669                                                    "Stop Encoding", GTK_RESPONSE_YES, NULL);
1670         response = gtk_dialog_run(GTK_DIALOG(dialog));
1671         gtk_widget_destroy (dialog);
1672         if (response == GTK_RESPONSE_NO) return FALSE;
1673         ghb_stop_queue();
1674         return TRUE;
1675 }
1676
1677 static void
1678 submit_job(GValue *settings)
1679 {
1680         static gint unique_id = 1;
1681         gchar *type, *modified, *preset;
1682         const GValue *path;
1683         gboolean preset_modified;
1684
1685         g_debug("submit_job");
1686         if (settings == NULL) return;
1687         preset_modified = ghb_settings_get_boolean(settings, "preset_modified");
1688         path = ghb_settings_get_value(settings, "preset");
1689         preset = ghb_preset_path_string(path);
1690         type = ghb_preset_is_custom() ? "Custom " : "";
1691         modified = preset_modified ? "Modified " : "";
1692         ghb_log("%s%sPreset: %s", modified, type, preset);
1693         g_free(preset);
1694
1695         ghb_settings_set_int(settings, "job_unique_id", unique_id);
1696         ghb_settings_set_int(settings, "job_status", GHB_QUEUE_RUNNING);
1697         ghb_add_job (settings, unique_id);
1698         ghb_start_queue();
1699         unique_id++;
1700 }
1701
1702 static void
1703 prune_logs(signal_user_data_t *ud)
1704 {
1705         gchar *dest_dir;
1706
1707         // Only prune logs stored in the default config dir location
1708         dest_dir = ghb_get_user_config_dir("EncodeLogs");
1709         if (g_file_test(dest_dir, G_FILE_TEST_IS_DIR))
1710         {
1711                 const gchar *file;
1712                 int week = 7*24*60*60;
1713                 GDir *gdir = g_dir_open(dest_dir, 0, NULL);
1714                 time_t now;
1715
1716                 now = time(NULL);
1717                 file = g_dir_read_name(gdir);
1718                 while (file)
1719                 {
1720                         gchar *path;
1721                         struct stat stbuf;
1722
1723                         path = g_strdup_printf("%s/%s", dest_dir, file);
1724                         g_stat(path, &stbuf);
1725                         if (now - stbuf.st_mtime > week)
1726                         {
1727                                 g_unlink(path);
1728                         }
1729                         g_free(path);
1730                         file = g_dir_read_name(gdir);
1731                 }
1732                 g_dir_close(gdir);
1733         }
1734         g_free(dest_dir);
1735         ghb_preview_cleanup(ud);
1736 }
1737
1738 static void
1739 queue_scan(signal_user_data_t *ud, GValue *js)
1740 {
1741         gchar *path;
1742         gint titlenum;
1743         time_t  _now;
1744         struct tm *now;
1745         gchar *log_path, *pos, *destname, *basename, *dest_dir;
1746
1747         _now = time(NULL);
1748         now = localtime(&_now);
1749         destname = ghb_settings_get_string(js, "destination");
1750         basename = g_path_get_basename(destname);
1751         if (ghb_settings_get_boolean(ud->settings, "EncodeLogLocation"))
1752         {
1753                 dest_dir = g_path_get_dirname (destname);
1754         }
1755         else
1756         {
1757                 dest_dir = ghb_get_user_config_dir("EncodeLogs");
1758         }
1759         g_free(destname);
1760         pos = g_strrstr( basename, "." );
1761         if (pos != NULL)
1762         {
1763                 *pos = 0;
1764         }
1765         log_path = g_strdup_printf("%s/%d-%02d-%02d %02d-%02d-%02d %s.log",
1766                 dest_dir,
1767                 now->tm_year + 1900, now->tm_mon + 1, now->tm_mday,
1768                 now->tm_hour, now->tm_min, now->tm_sec, basename);
1769         g_free(basename);
1770         g_free(dest_dir);
1771         if (ud->job_activity_log)
1772                 g_io_channel_unref(ud->job_activity_log);
1773         ud->job_activity_log = g_io_channel_new_file (log_path, "w", NULL);
1774         if (ud->job_activity_log)
1775         {
1776                 gchar *ver_str;
1777
1778                 ver_str = g_strdup_printf("Handbrake Version: %s (%d)\n", 
1779                                                                         hb_get_version(NULL), hb_get_build(NULL));
1780                 g_io_channel_write_chars (ud->job_activity_log, ver_str, 
1781                                                                         -1, NULL, NULL);
1782                 g_free(ver_str);
1783         }
1784         g_free(log_path);
1785
1786         path = ghb_settings_get_string( js, "source");
1787         titlenum = ghb_settings_get_int(js, "titlenum");
1788         ghb_backend_queue_scan(path, titlenum);
1789         g_free(path);
1790 }
1791
1792 GValue* 
1793 ghb_start_next_job(signal_user_data_t *ud, gboolean find_first)
1794 {
1795         static gint current = 0;
1796         gint count, ii, jj;
1797         GValue *js;
1798         gint status;
1799
1800         g_debug("start_next_job");
1801         count = ghb_array_len(ud->queue);
1802         if (find_first)
1803         {       // Start the first pending item in the queue
1804                 current = 0;
1805                 for (ii = 0; ii < count; ii++)
1806                 {
1807
1808                         js = ghb_array_get_nth(ud->queue, ii);
1809                         status = ghb_settings_get_int(js, "job_status");
1810                         if (status == GHB_QUEUE_PENDING)
1811                         {
1812                                 current = ii;
1813                                 ghb_inhibit_gpm();
1814                                 queue_scan(ud, js);
1815                                 return js;
1816                         }
1817                 }
1818                 // Nothing pending
1819                 ghb_uninhibit_gpm();
1820                 ghb_notify_done(ud);
1821                 return NULL;
1822         }
1823         // Find the next pending item after the current running item
1824         for (ii = 0; ii < count-1; ii++)
1825         {
1826                 js = ghb_array_get_nth(ud->queue, ii);
1827                 status = ghb_settings_get_int(js, "job_status");
1828                 if (status == GHB_QUEUE_RUNNING)
1829                 {
1830                         for (jj = ii+1; jj < count; jj++)
1831                         {
1832                                 js = ghb_array_get_nth(ud->queue, jj);
1833                                 status = ghb_settings_get_int(js, "job_status");
1834                                 if (status == GHB_QUEUE_PENDING)
1835                                 {
1836                                         current = jj;
1837                                         ghb_inhibit_gpm();
1838                                         queue_scan(ud, js);
1839                                         return js;
1840                                 }
1841                         }
1842                 }
1843         }
1844         // No running item found? Maybe it was deleted
1845         // Look for a pending item starting from the last index we started
1846         for (ii = current; ii < count; ii++)
1847         {
1848                 js = ghb_array_get_nth(ud->queue, ii);
1849                 status = ghb_settings_get_int(js, "job_status");
1850                 if (status == GHB_QUEUE_PENDING)
1851                 {
1852                         current = ii;
1853                         ghb_inhibit_gpm();
1854                         queue_scan(ud, js);
1855                         return js;
1856                 }
1857         }
1858         // Nothing found
1859         ghb_uninhibit_gpm();
1860         ghb_notify_done(ud);
1861         return NULL;
1862 }
1863
1864 static gint
1865 find_queue_job(GValue *queue, gint unique_id, GValue **job)
1866 {
1867         GValue *js;
1868         gint ii, count;
1869         gint job_unique_id;
1870         
1871         *job = NULL;
1872         g_debug("find_queue_job");
1873         count = ghb_array_len(queue);
1874         for (ii = 0; ii < count; ii++)
1875         {
1876                 js = ghb_array_get_nth(queue, ii);
1877                 job_unique_id = ghb_settings_get_int(js, "job_unique_id");
1878                 if (job_unique_id == unique_id)
1879                 {
1880                         *job = js;
1881                         return ii;
1882                 }
1883         }
1884         return -1;
1885 }
1886
1887 gchar*
1888 working_status_string(signal_user_data_t *ud, ghb_instance_status_t *status)
1889 {
1890         gchar *task_str, *job_str, *status_str;
1891         gint qcount;
1892         gint index;
1893         GValue *js;
1894
1895         if (status->job_count > 1)
1896         {
1897                 task_str = g_strdup_printf("pass %d of %d, ", 
1898                         status->job_cur, status->job_count);
1899         }
1900         else
1901         {
1902                 task_str = g_strdup("");
1903         }
1904         qcount = ghb_array_len(ud->queue);
1905         if (qcount > 1)
1906         {
1907                 index = find_queue_job(ud->queue, status->unique_id, &js);
1908                 job_str = g_strdup_printf("job %d of %d, ", index+1, qcount);
1909         }
1910         else
1911         {
1912                 job_str = g_strdup("");
1913         }
1914         if(status->seconds > -1)
1915         {
1916                 status_str= g_strdup_printf(
1917                         "Encoding: %s%s%.2f %%"
1918                         " (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)",
1919                         job_str, task_str,
1920                         100.0 * status->progress,
1921                         status->rate_cur, status->rate_avg, status->hours, 
1922                         status->minutes, status->seconds );
1923         }
1924         else
1925         {
1926                 status_str= g_strdup_printf(
1927                         "Encoding: %s%s%.2f %%",
1928                         job_str, task_str,
1929                         100.0 * status->progress );
1930         }
1931         g_free(task_str);
1932         g_free(job_str);
1933         return status_str;
1934 }
1935
1936 static void
1937 ghb_backend_events(signal_user_data_t *ud)
1938 {
1939         ghb_status_t status;
1940         gchar *status_str;
1941         GtkProgressBar *progress;
1942         gint titleindex;
1943         GValue *js;
1944         gint index;
1945         GtkTreeView *treeview;
1946         GtkTreeStore *store;
1947         GtkTreeIter iter;
1948         static gint working = 0;
1949         static gboolean work_started = FALSE;
1950         
1951         ghb_track_status();
1952         ghb_get_status(&status);
1953         progress = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "progressbar"));
1954         // First handle the status of title scans
1955         // Then handle the status of the queue
1956         if (status.scan.state & GHB_STATE_SCANNING)
1957         {
1958                 if (status.scan.title_cur == 0)
1959                 {
1960                         status_str = g_strdup ("Scanning...");
1961                 }
1962                 else
1963                 {
1964                         status_str = g_strdup_printf ("Scanning title %d of %d...", 
1965                                                           status.scan.title_cur, status.scan.title_count );
1966                 }
1967                 gtk_progress_bar_set_text (progress, status_str);
1968                 g_free(status_str);
1969                 if (status.scan.title_count > 0)
1970                 {
1971                         gtk_progress_bar_set_fraction (progress, 
1972                                 (gdouble)status.scan.title_cur / status.scan.title_count);
1973                 }
1974         }
1975         else if (status.scan.state & GHB_STATE_SCANDONE)
1976         {
1977                 status_str = g_strdup_printf ("Scan done"); 
1978                 gtk_progress_bar_set_text (progress, status_str);
1979                 g_free(status_str);
1980                 gtk_progress_bar_set_fraction (progress, 1.0);
1981
1982                 ghb_title_info_t tinfo;
1983                         
1984                 ghb_update_ui_combo_box(ud->builder, "title", 0, FALSE);
1985                 titleindex = ghb_longest_title();
1986                 ghb_ui_update(ud, "title", ghb_int64_value(titleindex));
1987
1988                 // Are there really any titles.
1989                 if (!ghb_get_title_info(&tinfo, titleindex))
1990                 {
1991                         GtkProgressBar *progress;
1992                         progress = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "progressbar"));
1993                         gtk_progress_bar_set_fraction (progress, 0);
1994                         gtk_progress_bar_set_text (progress, "No Source");
1995                 }
1996                 ghb_clear_scan_state(GHB_STATE_SCANDONE);
1997                 ghb_queue_buttons_grey(ud, work_started);
1998                 if (ghb_queue_edit_settings)
1999                 {
2000                         gint jstatus;
2001
2002                         jstatus = ghb_settings_get_int(ghb_queue_edit_settings, "job_status");
2003                         ghb_settings_to_ui(ud, ghb_queue_edit_settings);
2004                         ghb_set_audio(ud, ghb_queue_edit_settings);
2005                         if (jstatus == GHB_QUEUE_PENDING)
2006                         {
2007                                 ghb_value_free(ghb_queue_edit_settings);
2008                         }
2009                         ghb_queue_edit_settings = NULL;
2010                 }
2011         }
2012         else if (status.queue.state & GHB_STATE_SCANNING)
2013         {
2014                 status_str = g_strdup_printf ("Scanning ...");
2015                 gtk_progress_bar_set_text (progress, status_str);
2016                 g_free(status_str);
2017                 gtk_progress_bar_set_fraction (progress, 0);
2018         }
2019         else if (status.queue.state & GHB_STATE_SCANDONE)
2020         {
2021                 ghb_clear_queue_state(GHB_STATE_SCANDONE);
2022                 usleep(2000000);
2023                 submit_job(ud->current_job);
2024         }
2025         else if (status.queue.state & GHB_STATE_PAUSED)
2026         {
2027                 status_str = g_strdup_printf ("Paused"); 
2028                 gtk_progress_bar_set_text (progress, status_str);
2029                 g_free(status_str);
2030         }
2031         else if (status.queue.state & GHB_STATE_WORKING)
2032         {
2033                 status_str = working_status_string(ud, &status.queue);
2034                 gtk_progress_bar_set_text (progress, status_str);
2035                 gtk_progress_bar_set_fraction (progress, status.queue.progress);
2036                 g_free(status_str);
2037         }
2038         else if (status.queue.state & GHB_STATE_WORKDONE)
2039         {
2040                 gint qstatus;
2041
2042                 work_started = FALSE;
2043                 ghb_queue_buttons_grey(ud, FALSE);
2044                 index = find_queue_job(ud->queue, status.queue.unique_id, &js);
2045                 treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
2046                 store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
2047                 if (ud->cancel_encode)
2048                         status.queue.error = GHB_ERROR_CANCELED;
2049                 switch( status.queue.error )
2050                 {
2051                         case GHB_ERROR_NONE:
2052                                 gtk_progress_bar_set_text( progress, "Rip done!" );
2053                                 qstatus = GHB_QUEUE_DONE;
2054                                 if (js != NULL)
2055                                 {
2056                                         gchar *path = g_strdup_printf ("%d", index);
2057                                         if (gtk_tree_model_get_iter_from_string(
2058                                                         GTK_TREE_MODEL(store), &iter, path))
2059                                         {
2060                                                 gtk_tree_store_set(store, &iter, 0, "hb-complete", -1);
2061                                         }
2062                                         g_free(path);
2063                                 }
2064                                 break;
2065                         case GHB_ERROR_CANCELED:
2066                                 gtk_progress_bar_set_text( progress, "Rip canceled." );
2067                                 qstatus = GHB_QUEUE_CANCELED;
2068                                 if (js != NULL)
2069                                 {
2070                                         gchar *path = g_strdup_printf ("%d", index);
2071                                         if (gtk_tree_model_get_iter_from_string(
2072                                                         GTK_TREE_MODEL(store), &iter, path))
2073                                         {
2074                                                 gtk_tree_store_set(store, &iter, 0, "hb-canceled", -1);
2075                                         }
2076                                         g_free(path);
2077                                 }
2078                                 break;
2079                         default:
2080                                 gtk_progress_bar_set_text( progress, "Rip failed.");
2081                                 qstatus = GHB_QUEUE_CANCELED;
2082                                 if (js != NULL)
2083                                 {
2084                                         gchar *path = g_strdup_printf ("%d", index);
2085                                         if (gtk_tree_model_get_iter_from_string(
2086                                                         GTK_TREE_MODEL(store), &iter, path))
2087                                         {
2088                                                 gtk_tree_store_set(store, &iter, 0, "hb-canceled", -1);
2089                                         }
2090                                         g_free(path);
2091                                 }
2092                 }
2093                 gtk_progress_bar_set_fraction (progress, 1.0);
2094                 ghb_clear_queue_state(GHB_STATE_WORKDONE);
2095                 if (ud->job_activity_log)
2096                         g_io_channel_unref(ud->job_activity_log);
2097                 ud->job_activity_log = NULL;
2098                 if (!ud->cancel_encode)
2099                 {
2100                         ud->current_job = ghb_start_next_job(ud, FALSE);
2101                 }
2102                 else
2103                 {
2104                         ghb_uninhibit_gpm();
2105                         ud->current_job = NULL;
2106                 }
2107                 if (js)
2108                         ghb_settings_set_int(js, "job_status", qstatus);
2109                 ghb_save_queue(ud->queue);
2110                 ud->cancel_encode = FALSE;
2111         }
2112         else if (status.queue.state & GHB_STATE_MUXING)
2113         {
2114                 gtk_progress_bar_set_text(progress, "Muxing: this may take awhile...");
2115         }
2116         if (status.queue.state & GHB_STATE_SCANNING)
2117         {
2118                 // This needs to be in scanning and working since scanning
2119                 // happens fast enough that it can be missed
2120                 if (!work_started)
2121                 {
2122                         work_started = TRUE;
2123                         ghb_queue_buttons_grey(ud, TRUE);
2124                 }
2125         }
2126         if (status.queue.state & GHB_STATE_WORKING)
2127         {
2128                 // This needs to be in scanning and working since scanning
2129                 // happens fast enough that it can be missed
2130                 if (!work_started)
2131                 {
2132                         work_started = TRUE;
2133                         ghb_queue_buttons_grey(ud, TRUE);
2134                 }
2135                 index = find_queue_job(ud->queue, status.queue.unique_id, &js);
2136                 if (status.queue.unique_id != 0 && index >= 0)
2137                 {
2138                         gchar working_icon[] = "hb-working0";
2139                         working_icon[10] = '0' + working;
2140                         working = (working+1) % 6;
2141                         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
2142                         store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
2143                         gchar *path = g_strdup_printf ("%d", index);
2144                         if (gtk_tree_model_get_iter_from_string(
2145                                         GTK_TREE_MODEL(store), &iter, path))
2146                         {
2147                                 gtk_tree_store_set(store, &iter, 0, working_icon, -1);
2148                         }
2149                         g_free(path);
2150                 }
2151                 GtkLabel *label;
2152                 gchar *status_str;
2153
2154                 status_str = working_status_string(ud, &status.queue);
2155                 label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_status"));
2156                 gtk_label_set_text (label, status_str);
2157                 g_free(status_str);
2158         }
2159         if (status.scan.state & GHB_STATE_WORKING)
2160         {
2161                 GtkProgressBar *live_progress;
2162                 live_progress = GTK_PROGRESS_BAR(
2163                         GHB_WIDGET(ud->builder, "live_encode_progress"));
2164                 status_str = working_status_string(ud, &status.scan);
2165                 gtk_progress_bar_set_text (live_progress, status_str);
2166                 gtk_progress_bar_set_fraction (live_progress, status.scan.progress);
2167                 g_free(status_str);
2168         }
2169         if (status.scan.state & GHB_STATE_WORKDONE)
2170         {
2171                 switch( status.scan.error )
2172                 {
2173                         case GHB_ERROR_NONE:
2174                         {
2175                                 ghb_live_encode_done(ud, TRUE);
2176                         } break;
2177                         default:
2178                         {
2179                                 ghb_live_encode_done(ud, FALSE);
2180                         } break;
2181                 }
2182                 ghb_clear_scan_state(GHB_STATE_WORKDONE);
2183         }
2184 }
2185
2186 G_MODULE_EXPORT gboolean
2187 ghb_timer_cb(gpointer data)
2188 {
2189         signal_user_data_t *ud = (signal_user_data_t*)data;
2190
2191         ghb_live_preview_progress(ud);
2192         ghb_backend_events(ud);
2193         if (update_default_destination)
2194         {
2195                 gchar *dest, *dest_dir, *def_dest;
2196                 dest = ghb_settings_get_string(ud->settings, "destination");
2197                 dest_dir = g_path_get_dirname (dest);
2198                 def_dest = ghb_settings_get_string(ud->settings, "destination_dir");
2199                 if (strcmp(dest_dir, def_dest) != 0)
2200                 {
2201                         ghb_settings_set_string (ud->settings, "destination_dir", dest_dir);
2202                         ghb_pref_save (ud->settings, "destination_dir");
2203                 }
2204                 g_free(dest);
2205                 g_free(dest_dir);
2206                 g_free(def_dest);
2207                 update_default_destination = FALSE;
2208         }
2209         if (update_preview)
2210         {
2211                 ghb_set_preview_image (ud);
2212                 update_preview = FALSE;
2213         }
2214         return TRUE;
2215 }
2216
2217 G_MODULE_EXPORT gboolean
2218 ghb_log_cb(GIOChannel *source, GIOCondition cond, gpointer data)
2219 {
2220         gchar *text = NULL;
2221         gsize length;
2222         GtkTextView *textview;
2223         GtkTextBuffer *buffer;
2224         GtkTextIter iter;
2225         GtkTextMark *mark;
2226         GError *gerror = NULL;
2227         GIOStatus status;
2228         
2229         signal_user_data_t *ud = (signal_user_data_t*)data;
2230
2231         status = g_io_channel_read_line (source, &text, &length, NULL, &gerror);
2232         if (text != NULL)
2233         {
2234                 GdkWindow *window;
2235                 gint width, height;
2236                 gint x, y;
2237                 gboolean bottom = FALSE;
2238
2239                 textview = GTK_TEXT_VIEW(GHB_WIDGET (ud->builder, "activity_view"));
2240                 buffer = gtk_text_view_get_buffer (textview);
2241                 // I would like to auto-scroll the window when the scrollbar
2242                 // is at the bottom, 
2243                 // must determine whether the insert point is at
2244                 // the bottom of the window 
2245                 window = gtk_text_view_get_window(textview, GTK_TEXT_WINDOW_TEXT);
2246                 if (window != NULL)
2247                 {
2248                         gdk_drawable_get_size(GDK_DRAWABLE(window), &width, &height);
2249                         gtk_text_view_window_to_buffer_coords(textview, 
2250                                 GTK_TEXT_WINDOW_TEXT, width, height, &x, &y);
2251                         gtk_text_view_get_iter_at_location(textview, &iter, x, y);
2252                         if (gtk_text_iter_is_end(&iter))
2253                         {
2254                                 bottom = TRUE;
2255                         }
2256                 }
2257                 else
2258                 {
2259                         // If the window isn't available, assume bottom
2260                         bottom = TRUE;
2261                 }
2262                 gtk_text_buffer_get_end_iter(buffer, &iter);
2263                 gtk_text_buffer_insert(buffer, &iter, text, -1);
2264                 if (bottom)
2265                 {
2266                         gtk_text_buffer_get_end_iter(buffer, &iter);
2267                         mark = gtk_text_buffer_create_mark(buffer, NULL, &iter, FALSE);
2268                         gtk_text_view_scroll_mark_onscreen(textview, mark);
2269                         gtk_text_buffer_delete_mark(buffer, mark);
2270                 }
2271 #if defined(_WIN32)
2272                 gsize one = 1;
2273                 text[length-1] = '\r';
2274 #endif
2275                 g_io_channel_write_chars (ud->activity_log, text, 
2276                                                                 length, &length, NULL);
2277 #if defined(_WIN32)
2278                 g_io_channel_write_chars (ud->activity_log, "\n", 
2279                                                                 one, &one, NULL);
2280 #endif
2281                 g_io_channel_flush(ud->activity_log, NULL);
2282                 if (ud->job_activity_log)
2283                 {
2284                         g_io_channel_write_chars (ud->job_activity_log, text, 
2285                                                                         length, &length, NULL);
2286 #if defined(_WIN32)
2287                         g_io_channel_write_chars (ud->activity_log, "\n", 
2288                                                                         one, &one, NULL);
2289 #endif
2290                         g_io_channel_flush(ud->job_activity_log, NULL);
2291                 }
2292                 g_free(text);
2293         }
2294         if (status != G_IO_STATUS_NORMAL)
2295         {
2296                 // This should never happen, but if it does I would get into an
2297                 // infinite loop.  Returning false removes this callback.
2298                 g_warning("Error while reading activity from pipe");
2299                 if (gerror != NULL)
2300                 {
2301                         g_warning("%s", gerror->message);
2302                         g_error_free (gerror);
2303                 }
2304                 return FALSE;
2305         }
2306         if (gerror != NULL)
2307                 g_error_free (gerror);
2308         return TRUE;
2309 }
2310
2311 static void
2312 set_visible(GtkWidget *widget, gboolean visible)
2313 {
2314         if (visible)
2315         {
2316                 gtk_widget_show_now(widget);
2317         }
2318         else
2319         {
2320                 gtk_widget_hide(widget);
2321         }
2322 }
2323
2324 G_MODULE_EXPORT void
2325 show_activity_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
2326 {
2327         GtkWidget *widget = GHB_WIDGET (ud->builder, "activity_window");
2328         set_visible(widget, gtk_toggle_tool_button_get_active(
2329                                                 GTK_TOGGLE_TOOL_BUTTON(xwidget)));
2330 }
2331
2332 G_MODULE_EXPORT void
2333 show_activity_menu_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
2334 {
2335         GtkWidget *widget = GHB_WIDGET (ud->builder, "activity_window");
2336         set_visible(widget, TRUE);
2337         widget = GHB_WIDGET (ud->builder, "show_activity");
2338         gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), TRUE);
2339 }
2340
2341 G_MODULE_EXPORT gboolean
2342 activity_window_delete_cb(GtkWidget *xwidget, GdkEvent *event, signal_user_data_t *ud)
2343 {
2344         set_visible(xwidget, FALSE);
2345         GtkWidget *widget = GHB_WIDGET (ud->builder, "show_activity");
2346         gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), FALSE);
2347         return TRUE;
2348 }
2349
2350 void
2351 ghb_log(gchar *log, ...)
2352 {
2353         va_list args;
2354         time_t _now;
2355     struct tm *now;
2356         gchar fmt[362];
2357
2358         _now = time(NULL);
2359         now = localtime( &_now );
2360         snprintf(fmt, 362, "[%02d:%02d:%02d] lingui: %s\n", 
2361                         now->tm_hour, now->tm_min, now->tm_sec, log);
2362         va_start(args, log);
2363         vfprintf(stderr, fmt, args);
2364         va_end(args);
2365 }
2366
2367 static void
2368 browse_url(const gchar *url)
2369 {
2370         gboolean result;
2371         char *argv[] = 
2372                 {"xdg-open",NULL,NULL,NULL};
2373         argv[1] = (gchar*)url;
2374         result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
2375                                 NULL, NULL, NULL);
2376         if (result) return;
2377
2378         argv[0] = "gnome-open";
2379         result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
2380                                 NULL, NULL, NULL);
2381         if (result) return;
2382
2383         argv[0] = "kfmclient";
2384         argv[1] = "exec";
2385         argv[2] = "http://trac.handbrake.fr/wiki/HandBrakeGuide";
2386         result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
2387                                 NULL, NULL, NULL);
2388         if (result) return;
2389
2390         argv[0] = "firefox";
2391         argv[1] = "http://trac.handbrake.fr/wiki/HandBrakeGuide";
2392         argv[2] = NULL;
2393         result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
2394                                 NULL, NULL, NULL);
2395 }
2396
2397 void
2398 about_web_hook(GtkAboutDialog *about, const gchar *link, gpointer data)
2399 {
2400         browse_url(link);
2401 }
2402
2403 G_MODULE_EXPORT void
2404 about_activate_cb(GtkWidget *xwidget, signal_user_data_t *ud)
2405 {
2406         GtkWidget *widget = GHB_WIDGET (ud->builder, "hb_about");
2407         gchar *ver;
2408
2409         ver = g_strdup_printf("%s (%s)", HB_PROJECT_VERSION, HB_PROJECT_BUILD_ARCH);
2410         gtk_about_dialog_set_url_hook(about_web_hook, NULL, NULL);
2411         gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(widget), ver);
2412         g_free(ver);
2413         gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(widget), 
2414                                                                 HB_PROJECT_URL_WEBSITE);
2415         gtk_about_dialog_set_website_label(GTK_ABOUT_DIALOG(widget), 
2416                                                                                 HB_PROJECT_URL_WEBSITE);
2417         gtk_widget_show (widget);
2418 }
2419
2420 G_MODULE_EXPORT void
2421 guide_activate_cb(GtkWidget *xwidget, signal_user_data_t *ud)
2422 {
2423         browse_url("http://trac.handbrake.fr/wiki/HandBrakeGuide");
2424 }
2425
2426 G_MODULE_EXPORT void
2427 hb_about_response_cb(GtkWidget *widget, gint response, signal_user_data_t *ud)
2428 {
2429         gtk_widget_hide (widget);
2430 }
2431
2432 G_MODULE_EXPORT void
2433 show_queue_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
2434 {
2435         GtkWidget *widget = GHB_WIDGET (ud->builder, "queue_window");
2436         set_visible(widget, gtk_toggle_tool_button_get_active(
2437                                                 GTK_TOGGLE_TOOL_BUTTON(xwidget)));
2438 }
2439
2440 G_MODULE_EXPORT void
2441 show_queue_menu_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
2442 {
2443         GtkWidget *widget = GHB_WIDGET (ud->builder, "queue_window");
2444         set_visible(widget, TRUE);
2445         widget = GHB_WIDGET (ud->builder, "show_queue");
2446         gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), TRUE);
2447 }
2448
2449 G_MODULE_EXPORT gboolean
2450 queue_window_delete_cb(GtkWidget *xwidget, GdkEvent *event, signal_user_data_t *ud)
2451 {
2452         set_visible(xwidget, FALSE);
2453         GtkWidget *widget = GHB_WIDGET (ud->builder, "show_queue");
2454         gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), FALSE);
2455         return TRUE;
2456 }
2457
2458 G_MODULE_EXPORT void
2459 show_presets_toggled_cb(GtkWidget *action, signal_user_data_t *ud)
2460 {
2461         GtkWidget *widget;
2462         GtkWindow *hb_window;
2463         
2464         g_debug("show_presets_clicked_cb ()");
2465         widget = GHB_WIDGET (ud->builder, "presets_frame");
2466         ghb_widget_to_setting(ud->settings, action);
2467         if (ghb_settings_get_boolean(ud->settings, "show_presets"))
2468         {
2469                 gtk_widget_show_now(widget);
2470         }
2471         else
2472         {
2473                 gtk_widget_hide(widget);
2474                 hb_window = GTK_WINDOW(GHB_WIDGET (ud->builder, "hb_window"));
2475                 gtk_window_resize(hb_window, 16, 16);
2476         }
2477         ghb_pref_save(ud->settings, "show_presets");
2478 }
2479
2480 static void
2481 update_chapter_list(signal_user_data_t *ud)
2482 {
2483         GtkTreeView *treeview;
2484         GtkTreeIter iter;
2485         GtkListStore *store;
2486         gboolean done;
2487         GValue *chapters;
2488         gint titleindex, ii;
2489         gint count;
2490         
2491         g_debug("update_chapter_list ()");
2492         titleindex = ghb_settings_combo_int(ud->settings, "title");
2493         chapters = ghb_get_chapters(titleindex);
2494         count = ghb_array_len(chapters);
2495         if (chapters)
2496                 ghb_settings_set_value(ud->settings, "chapter_list", chapters);
2497         
2498         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "chapters_list"));
2499         store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview));
2500         ii = 0;
2501         if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter))
2502         {
2503                 do
2504                 {
2505
2506                         if (ii < count)
2507                         {
2508                                 gchar *chapter, *duration;
2509                                 gint hh, mm, ss;
2510
2511                                 // Update row with settings data
2512                                 g_debug("Updating row");
2513                                 chapter = ghb_value_string(ghb_array_get_nth(chapters, ii));
2514                                 ghb_get_chapter_duration(titleindex, ii, &hh, &mm, &ss);
2515                                 duration = g_strdup_printf("%02d:%02d:%02d", hh, mm, ss);
2516                                 gtk_list_store_set(store, &iter, 
2517                                         0, ii+1,
2518                                         1, duration,
2519                                         2, chapter,
2520                                         3, TRUE,
2521                                         -1);
2522                                 g_free(chapter);
2523                                 g_free(duration);
2524                                 ii++;
2525                                 done = !gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
2526                         }
2527                         else
2528                         {
2529                                 // No more settings data, remove row
2530                                 g_debug("Removing row");
2531                                 done = !gtk_list_store_remove(store, &iter);
2532                         }
2533                 } while (!done);
2534         }
2535         while (ii < count)
2536         {
2537                 gchar *chapter, *duration;
2538                 gint hh, mm, ss;
2539
2540                 // Additional settings, add row
2541                 g_debug("Adding row");
2542                 chapter = ghb_value_string(ghb_array_get_nth(chapters, ii));
2543                 ghb_get_chapter_duration(titleindex, ii, &hh, &mm, &ss);
2544                 duration = g_strdup_printf("%02d:%02d:%02d", hh, mm, ss);
2545                 gtk_list_store_append(store, &iter);
2546                 gtk_list_store_set(store, &iter, 
2547                         0, ii+1,
2548                         1, duration,
2549                         2, chapter,
2550                         3, TRUE,
2551                         -1);
2552                 g_free(chapter);
2553                 g_free(duration);
2554                 ii++;
2555         }
2556 }
2557
2558 static gint chapter_edit_key = 0;
2559
2560 G_MODULE_EXPORT gboolean
2561 chapter_keypress_cb(
2562         GhbCellRendererText *cell,
2563         GdkEventKey *event,
2564         signal_user_data_t *ud)
2565 {
2566         chapter_edit_key = event->keyval;
2567         return FALSE;
2568 }
2569
2570 G_MODULE_EXPORT void
2571 chapter_edited_cb(
2572         GhbCellRendererText *cell, 
2573         gchar *path, 
2574         gchar *text, 
2575         signal_user_data_t *ud)
2576 {
2577         GtkTreePath *treepath;
2578         GtkListStore *store;
2579         GtkTreeView *treeview;
2580         GtkTreeIter iter;
2581         gint index;
2582         gint *pi;
2583         gint row;
2584         
2585         g_debug("chapter_edited_cb ()");
2586         g_debug("path (%s)", path);
2587         g_debug("text (%s)", text);
2588         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "chapters_list"));
2589         store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview));
2590         treepath = gtk_tree_path_new_from_string (path);
2591         pi = gtk_tree_path_get_indices(treepath);
2592         row = pi[0];
2593         gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, treepath);
2594         gtk_list_store_set(store, &iter, 
2595                 2, text,
2596                 3, TRUE,
2597                 -1);
2598         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 0, &index, -1);
2599
2600         const GValue *chapters;
2601         GValue *chapter;
2602
2603         chapters = ghb_settings_get_value(ud->settings, "chapter_list");
2604         chapter = ghb_array_get_nth(chapters, index-1);
2605         g_value_set_string(chapter, text);
2606         if ((chapter_edit_key == GDK_Return || chapter_edit_key == GDK_Down) &&
2607                 gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter))
2608         {
2609                 GtkTreeViewColumn *column;
2610
2611                 gtk_tree_path_next(treepath);
2612                 // When a cell has been edited, I want to advance to the
2613                 // next cell and start editing it automaitcally.
2614                 // Unfortunately, we may not be in a state here where
2615                 // editing is allowed.  This happens when the user selects
2616                 // a new cell with the mouse instead of just hitting enter.
2617                 // Some kind of Gtk quirk.  widget_editable==NULL assertion.
2618                 // Editing is enabled again once the selection event has been
2619                 // processed.  So I'm queueing up a callback to be called
2620                 // when things go idle.  There, I will advance to the next
2621                 // cell and initiate editing.
2622                 //
2623                 // Now, you might be asking why I don't catch the keypress
2624                 // event and determine what action to take based on that.
2625                 // The Gtk developers in their infinite wisdom have made the 
2626                 // actual GtkEdit widget being used a private member of
2627                 // GtkCellRendererText, so it can not be accessed to hang a
2628                 // signal handler off of.  And they also do not propagate the
2629                 // keypress signals in any other way.  So that information is lost.
2630                 //g_idle_add((GSourceFunc)next_cell, ud);
2631                 //
2632                 // Keeping the above comment for posterity.
2633                 // I got industrious and made my own CellTextRendererText that
2634                 // passes on the key-press-event. So now I have much better
2635                 // control of this.
2636                 column = gtk_tree_view_get_column(treeview, 2);
2637                 gtk_tree_view_set_cursor(treeview, treepath, column, TRUE);
2638         }
2639         else if (chapter_edit_key == GDK_Up && row > 0)
2640         {
2641                 GtkTreeViewColumn *column;
2642                 gtk_tree_path_prev(treepath);
2643                 column = gtk_tree_view_get_column(treeview, 2);
2644                 gtk_tree_view_set_cursor(treeview, treepath, column, TRUE);
2645         }
2646         gtk_tree_path_free (treepath);
2647 }
2648
2649 void
2650 debug_log_handler(const gchar *domain, GLogLevelFlags flags, const gchar *msg, gpointer data)
2651 {
2652         signal_user_data_t *ud = (signal_user_data_t*)data;
2653         
2654         if (ud->debug)
2655         {
2656                 printf("%s: %s\n", domain, msg);
2657         }
2658 }
2659
2660 void
2661 warn_log_handler(const gchar *domain, GLogLevelFlags flags, const gchar *msg, gpointer data)
2662 {
2663         printf("%s: %s\n", domain, msg);
2664 }
2665
2666 void
2667 ghb_hbfd(signal_user_data_t *ud, gboolean hbfd)
2668 {
2669         GtkWidget *widget;
2670         g_debug("ghb_hbfd");
2671         widget = GHB_WIDGET(ud->builder, "queue_pause1");
2672         set_visible(widget, !hbfd);
2673         widget = GHB_WIDGET(ud->builder, "queue_add");
2674         set_visible(widget, !hbfd);
2675         widget = GHB_WIDGET(ud->builder, "show_queue");
2676         set_visible(widget, !hbfd);
2677         widget = GHB_WIDGET(ud->builder, "show_activity");
2678         set_visible(widget, !hbfd);
2679
2680         widget = GHB_WIDGET(ud->builder, "chapter_box");
2681         set_visible(widget, !hbfd);
2682         widget = GHB_WIDGET(ud->builder, "container_box");
2683         set_visible(widget, !hbfd);
2684         widget = GHB_WIDGET(ud->builder, "settings_box");
2685         set_visible(widget, !hbfd);
2686         widget = GHB_WIDGET(ud->builder, "presets_save");
2687         set_visible(widget, !hbfd);
2688         widget = GHB_WIDGET(ud->builder, "presets_remove");
2689         set_visible(widget, !hbfd);
2690         widget = GHB_WIDGET (ud->builder, "hb_window");
2691         gtk_window_resize(GTK_WINDOW(widget), 16, 16);
2692
2693 }
2694
2695 G_MODULE_EXPORT void
2696 hbfd_toggled_cb(GtkWidget *widget, signal_user_data_t *ud)
2697 {
2698         g_debug("hbfd_toggled_cb");
2699         ghb_widget_to_setting (ud->settings, widget);
2700         gboolean hbfd = ghb_settings_get_boolean(ud->settings, "hbfd");
2701         ghb_hbfd(ud, hbfd);
2702         ghb_pref_save(ud->settings, "hbfd");
2703 }
2704
2705 G_MODULE_EXPORT void
2706 pref_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
2707 {
2708         g_debug("pref_changed_cb");
2709         ghb_widget_to_setting (ud->settings, widget);
2710         ghb_check_dependency(ud, widget);
2711         const gchar *name = gtk_widget_get_name(widget);
2712         ghb_pref_save(ud->settings, name);
2713 }
2714
2715 G_MODULE_EXPORT void
2716 vqual_granularity_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
2717 {
2718         g_debug("vqual_granularity_changed_cb");
2719         ghb_widget_to_setting (ud->settings, widget);
2720         ghb_check_dependency(ud, widget);
2721
2722         const gchar *name = gtk_widget_get_name(widget);
2723         ghb_pref_save(ud->settings, name);
2724
2725         gdouble vqmin, vqmax, step, page;
2726         gboolean inverted;
2727         gint digits;
2728
2729         ghb_vquality_range(ud, &vqmin, &vqmax, &step, &page, &digits, &inverted);
2730         GtkWidget *qp = GHB_WIDGET(ud->builder, "VideoQualitySlider");
2731         gtk_range_set_increments (GTK_RANGE(qp), step, page);
2732 }
2733
2734 G_MODULE_EXPORT void
2735 tweaks_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
2736 {
2737         g_debug("tweaks_changed_cb");
2738         ghb_widget_to_setting (ud->settings, widget);
2739         const gchar *name = gtk_widget_get_name(widget);
2740         ghb_pref_save(ud->settings, name);
2741 }
2742
2743 G_MODULE_EXPORT void
2744 hbfd_feature_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
2745 {
2746         g_debug("hbfd_feature_changed_cb");
2747         ghb_widget_to_setting (ud->settings, widget);
2748         const gchar *name = gtk_widget_get_name(widget);
2749         ghb_pref_save(ud->settings, name);
2750
2751         gboolean hbfd = ghb_settings_get_boolean(ud->settings, "hbfd_feature");
2752         GtkAction *action;
2753         if (hbfd)
2754         {
2755                 const GValue *val;
2756                 val = ghb_settings_get_value(ud->settings, "hbfd");
2757                 ghb_ui_update(ud, "hbfd", val);
2758         }
2759         action = GHB_ACTION (ud->builder, "hbfd");
2760         gtk_action_set_visible(action, hbfd);
2761 }
2762
2763 void
2764 ghb_file_menu_add_dvd(signal_user_data_t *ud)
2765 {
2766         GList *link, *drives;
2767         static GtkActionGroup *agroup = NULL;
2768         static gint merge_id;
2769
2770         link = drives = dvd_device_list();
2771         if (drives != NULL)
2772         {
2773                 GtkUIManager *ui = GTK_UI_MANAGER(
2774                         gtk_builder_get_object(ud->builder, "uimanager1"));
2775
2776                 if (agroup == NULL)
2777                 {
2778                         agroup = gtk_action_group_new("dvdgroup");
2779                         gtk_ui_manager_insert_action_group(ui, agroup, 0);
2780                 }
2781                 else
2782                         gtk_ui_manager_remove_ui(ui, merge_id);
2783
2784                 merge_id = gtk_ui_manager_new_merge_id(ui);
2785                 // Add separator
2786                 gtk_ui_manager_add_ui(ui, merge_id, 
2787                         "ui/menubar1/menuitem1/quit1", "dvdsep", NULL,
2788                         GTK_UI_MANAGER_SEPARATOR, TRUE);
2789
2790                 while (link != NULL)
2791                 {
2792                         GtkAction *action;
2793                         gchar *drive = get_dvd_device_name(link->data);
2794                         gchar *name = get_dvd_volume_name(link->data);
2795                 
2796                         action = gtk_action_group_get_action(agroup, drive);
2797                         if (action != NULL)
2798                         {
2799                                 gtk_action_group_remove_action(agroup, action);
2800                                 g_object_unref(G_OBJECT(action));
2801                         }
2802                         // Create action for this drive
2803                         action = gtk_action_new(drive, name,
2804                                 "Scan this DVD source", "gtk-cdrom");
2805                         // Add action to action group
2806                         gtk_action_group_add_action_with_accel(agroup, action, NULL);
2807                         // Add to ui manager
2808                         gtk_ui_manager_add_ui(ui, merge_id, 
2809                                 "ui/menubar1/menuitem1/dvdsep", drive, drive,
2810                                 GTK_UI_MANAGER_AUTO, TRUE);
2811                         // Connect signal to action (menu item)
2812                         g_signal_connect(action, "activate", 
2813                                 (GCallback)dvd_source_activate_cb, ud);
2814                         g_free(name);
2815                         g_free(drive);
2816                         g_object_unref(link->data);
2817                         link = link->next;
2818                 }
2819                 g_list_free(drives);
2820         }
2821 }
2822
2823 gboolean ghb_is_cd(GDrive *gd);
2824
2825 static GList*
2826 dvd_device_list()
2827 {
2828         GList *dvd_devices = NULL;
2829
2830 #if defined(_WIN32)
2831         gint ii, drives;
2832         gchar drive[5];
2833
2834         strcpy(drive, "A:" G_DIR_SEPARATOR_S);
2835         drives = GetLogicalDrives();
2836         for (ii = 0; ii < 26; ii++)
2837         {
2838                 if (drives & 0x01)
2839                 {
2840                         guint dtype;
2841
2842                         drive[0] = 'A' + ii;
2843                         dtype = GetDriveType(drive);
2844                         if (dtype == DRIVE_CDROM)
2845                         {
2846                                 dvd_devices = g_list_append(dvd_devices, 
2847                                                 (gpointer)g_strdup(drive));
2848                         }
2849                 }
2850                 drives >>= 1;
2851         }
2852 #else
2853         GVolumeMonitor *gvm;
2854         GList *drives, *link;
2855         
2856         gvm = g_volume_monitor_get ();
2857         drives = g_volume_monitor_get_connected_drives (gvm);
2858         link = drives;
2859         while (link != NULL)
2860         {
2861                 GDrive *gd;
2862                 
2863                 gd = (GDrive*)link->data;
2864                 if (ghb_is_cd(gd))
2865                 {
2866                         dvd_devices = g_list_append(dvd_devices, gd);
2867                 }
2868                 else
2869                         g_object_unref (gd);
2870                 link = link->next;
2871         }
2872         g_list_free(drives);
2873 #endif
2874
2875         return dvd_devices;
2876 }
2877
2878 #if !defined(_WIN32)
2879 static LibHalContext *hal_ctx;
2880 #endif
2881
2882 gboolean
2883 ghb_is_cd(GDrive *gd)
2884 {
2885 #if !defined(_WIN32)
2886         gchar *device;
2887         LibHalDrive *halDrive;
2888         LibHalDriveType dtype;
2889
2890         device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
2891         halDrive = libhal_drive_from_device_file (hal_ctx, device);
2892         dtype = libhal_drive_get_type(halDrive);
2893         libhal_drive_free(halDrive);
2894         g_free(device);
2895         return (dtype == LIBHAL_DRIVE_TYPE_CDROM);
2896 #else
2897         return FALSE;
2898 #endif
2899 }
2900
2901 #if defined(_WIN32)
2902 static void
2903 handle_media_change(const gchar *device, gboolean insert, signal_user_data_t *ud)
2904 {
2905         guint dtype;
2906         static gint ins_count = 0;
2907         static gint rem_count = 0;
2908
2909         // The media change event in windows bounces around a bit
2910         // so I debounce it here
2911         // DVD insertion detected.  Scan it.
2912         dtype = GetDriveType(device);
2913         if (dtype != DRIVE_CDROM)
2914                 return;
2915         if (insert)
2916         {
2917                 rem_count = 0;
2918                 ins_count++;
2919                 if (ins_count == 2)
2920                 {
2921                         ghb_file_menu_add_dvd(ud);
2922                         if (ud->current_dvd_device != NULL &&
2923                                 strcmp(device, ud->current_dvd_device) == 0)
2924                         {
2925                                 GtkProgressBar *progress;
2926                                 progress = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "progressbar"));
2927                                 gtk_progress_bar_set_text (progress, "Scanning ...");
2928                                 gtk_progress_bar_set_fraction (progress, 0);
2929                                 update_source_label(ud, device);
2930                                 gint preview_count;
2931                                 preview_count = ghb_settings_get_int(ud->settings, "preview_count");
2932                                 ghb_backend_scan(device, 0, preview_count);
2933                         }
2934                 }
2935         }
2936         else
2937         {
2938                 ins_count = 0;
2939                 rem_count++;
2940                 if (rem_count == 2)
2941                 {
2942                         ghb_file_menu_add_dvd(ud);
2943                         if (ud->current_dvd_device != NULL &&
2944                                 strcmp(device, ud->current_dvd_device) == 0)
2945                         {
2946                                 ghb_hb_cleanup(TRUE);
2947                                 prune_logs(ud);
2948                                 ghb_backend_scan("/dev/null", 0, 1);
2949                         }
2950                 }
2951         }
2952 }
2953
2954 static gchar
2955 FindDriveFromMask(ULONG unitmask)
2956 {
2957         gchar cc;
2958         for (cc = 0; cc < 26; cc++)
2959         {
2960                 if (unitmask & 0x01)
2961                         return 'A' + cc;
2962                 unitmask >>= 1;
2963         }
2964         return 0;
2965 }
2966
2967 void
2968 wm_drive_changed(MSG *msg, signal_user_data_t *ud)
2969 {
2970         PDEV_BROADCAST_HDR bch = (PDEV_BROADCAST_HDR)msg->lParam;
2971         gchar drive[4];
2972
2973         g_strlcpy(drive, "A:" G_DIR_SEPARATOR_S, 4);
2974         switch (msg->wParam)
2975         {
2976                 case DBT_DEVICEARRIVAL:
2977                 {
2978                         if (bch->dbch_devicetype == DBT_DEVTYP_VOLUME)
2979                         {
2980                                 PDEV_BROADCAST_VOLUME bcv = (PDEV_BROADCAST_VOLUME)bch;
2981
2982                                 if (bcv->dbcv_flags & DBTF_MEDIA)
2983                                 {
2984                                         drive[0] = FindDriveFromMask(bcv->dbcv_unitmask);
2985                                         handle_media_change(drive, TRUE, ud);
2986                                 }
2987                         }
2988                 } break;
2989
2990                 case DBT_DEVICEREMOVECOMPLETE:
2991                 {
2992                         if (bch->dbch_devicetype == DBT_DEVTYP_VOLUME)
2993                         {
2994                                 PDEV_BROADCAST_VOLUME bcv = (PDEV_BROADCAST_VOLUME)bch;
2995
2996                                 if (bcv->dbcv_flags & DBTF_MEDIA)
2997                                 {
2998                                         drive[0] = FindDriveFromMask(bcv->dbcv_unitmask);
2999                                         handle_media_change(drive, FALSE, ud);
3000                                 }
3001                         }
3002                 } break;
3003                 default: ;
3004         }
3005 }
3006
3007 #else
3008
3009 G_MODULE_EXPORT void
3010 drive_changed_cb(GVolumeMonitor *gvm, GDrive *gd, signal_user_data_t *ud)
3011 {
3012         gchar *device;
3013         gint state;
3014
3015         g_debug("drive_changed_cb()");
3016         ghb_file_menu_add_dvd(ud);
3017
3018         state = ghb_get_scan_state();
3019         device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
3020         if (ud->current_dvd_device == NULL ||
3021                 strcmp(device, ud->current_dvd_device) != 0 ||
3022                 state != GHB_STATE_IDLE )
3023         {
3024                 return;
3025         }
3026         if (g_drive_has_media(gd))
3027         {
3028                 GtkProgressBar *progress;
3029                 progress = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "progressbar"));
3030                 gtk_progress_bar_set_text (progress, "Scanning ...");
3031                 gtk_progress_bar_set_fraction (progress, 0);
3032                 update_source_label(ud, device);
3033                 gint preview_count;
3034                 preview_count = ghb_settings_get_int(ud->settings, "preview_count");
3035                 ghb_backend_scan(device, 0, preview_count);
3036         }
3037         else
3038         {
3039                 ghb_hb_cleanup(TRUE);
3040                 prune_logs(ud);
3041                 ghb_backend_scan("/dev/null", 0, 1);
3042         }
3043 }
3044 #endif
3045
3046 #if !defined(_WIN32)
3047 static void
3048 dbus_init (void)
3049 {
3050         dbus_g_thread_init();
3051 }
3052
3053 #define GPM_DBUS_SERVICE                        "org.freedesktop.PowerManagement"
3054 #define GPM_DBUS_INHIBIT_PATH           "/org/freedesktop/PowerManagement/Inhibit"
3055 #define GPM_DBUS_INHIBIT_INTERFACE      "org.freedesktop.PowerManagement.Inhibit" 
3056 static gboolean gpm_inhibited = FALSE;
3057 static guint gpm_cookie = -1;
3058 #endif
3059
3060 void
3061 ghb_inhibit_gpm()
3062 {
3063 #if !defined(_WIN32)
3064         DBusGConnection *conn;
3065         DBusGProxy      *proxy;
3066         GError *error = NULL;
3067         gboolean res;
3068         
3069
3070         if (gpm_inhibited)
3071         {
3072                 // Already inhibited
3073                 return;
3074         }
3075         g_debug("ghb_inhibit_gpm()");
3076         conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
3077         if (error != NULL)
3078         {
3079                 g_debug("DBUS cannot connect: %s", error->message);
3080                 g_error_free(error);
3081                 return;
3082         }
3083         proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SERVICE,
3084                                                         GPM_DBUS_INHIBIT_PATH, GPM_DBUS_INHIBIT_INTERFACE);
3085         if (proxy == NULL)
3086         {
3087                 g_debug("Could not get DBUS proxy: %s", GPM_DBUS_SERVICE);
3088                 dbus_g_connection_unref(conn);
3089                 return;
3090         }
3091         res = dbus_g_proxy_call(proxy, "Inhibit", &error,
3092                                                         G_TYPE_STRING, "ghb",
3093                                                         G_TYPE_STRING, "Encoding",
3094                                                         G_TYPE_INVALID,
3095                                                         G_TYPE_UINT, &gpm_cookie,
3096                                                         G_TYPE_INVALID);
3097         if (!res)
3098         {
3099                 g_warning("Inhibit method failed");
3100                 gpm_cookie = -1;
3101         }
3102         if (error != NULL)
3103         {
3104                 g_warning("Inhibit problem: %s", error->message);
3105                 g_error_free(error);
3106                 gpm_cookie = -1;
3107         }
3108         gpm_inhibited = TRUE;
3109         g_object_unref(G_OBJECT(proxy));
3110         dbus_g_connection_unref(conn);
3111 #endif
3112 }
3113
3114 void
3115 ghb_uninhibit_gpm()
3116 {
3117 #if !defined(_WIN32)
3118         DBusGConnection *conn;
3119         DBusGProxy      *proxy;
3120         GError *error = NULL;
3121         gboolean res;
3122         
3123         g_debug("ghb_uninhibit_gpm() gpm_cookie %u", gpm_cookie);
3124
3125         if (!gpm_inhibited)
3126         {
3127                 // Not inhibited
3128                 return;
3129         }
3130         conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
3131         if (error != NULL)
3132         {
3133                 g_debug("DBUS cannot connect: %s", error->message);
3134                 g_error_free(error);
3135                 return;
3136         }
3137         proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SERVICE,
3138                                                         GPM_DBUS_INHIBIT_PATH, GPM_DBUS_INHIBIT_INTERFACE);
3139         if (proxy == NULL)
3140         {
3141                 g_debug("Could not get DBUS proxy: %s", GPM_DBUS_SERVICE);
3142                 dbus_g_connection_unref(conn);
3143                 return;
3144         }
3145         res = dbus_g_proxy_call(proxy, "UnInhibit", &error,
3146                                                         G_TYPE_UINT, gpm_cookie,
3147                                                         G_TYPE_INVALID,
3148                                                         G_TYPE_INVALID);
3149         if (!res)
3150         {
3151                 g_warning("UnInhibit method failed");
3152         }
3153         if (error != NULL)
3154         {
3155                 g_warning("UnInhibit problem: %s", error->message);
3156                 g_error_free(error);
3157         }
3158         gpm_inhibited = FALSE;
3159         dbus_g_connection_unref(conn);
3160         g_object_unref(G_OBJECT(proxy));
3161 #endif
3162 }
3163
3164 void
3165 ghb_hal_init()
3166 {
3167 #if !defined(_WIN32)
3168         DBusGConnection *gconn;
3169         DBusConnection *conn;
3170         GError *gerror = NULL;
3171         DBusError error;
3172         char **devices;
3173         int nr;
3174
3175         dbus_init ();
3176
3177         if (!(hal_ctx = libhal_ctx_new ())) {
3178                 g_warning ("failed to create a HAL context!");
3179                 return;
3180         }
3181
3182         gconn = dbus_g_bus_get(DBUS_BUS_SYSTEM, &gerror);
3183         if (gerror != NULL)
3184         {
3185                 g_warning("DBUS cannot connect: %s", gerror->message);
3186                 g_error_free(gerror);
3187                 return;
3188         }
3189         conn = dbus_g_connection_get_connection(gconn);
3190         libhal_ctx_set_dbus_connection (hal_ctx, conn);
3191         dbus_error_init (&error);
3192         if (!libhal_ctx_init (hal_ctx, &error)) {
3193                 g_warning ("libhal_ctx_init failed: %s", error.message ? error.message : "unknown");
3194                 dbus_error_free (&error);
3195                 libhal_ctx_free (hal_ctx);
3196                 dbus_g_connection_unref(gconn);
3197                 return;
3198         }
3199
3200         /*
3201          * Do something to ping the HAL daemon - the above functions will
3202          * succeed even if hald is not running, so long as DBUS is.  But we
3203          * want to exit silently if hald is not running, to behave on
3204          * pre-2.6 systems.
3205          */
3206         if (!(devices = libhal_get_all_devices (hal_ctx, &nr, &error))) {
3207                 g_warning ("seems that HAL is not running: %s", error.message ? error.message : "unknown");
3208                 dbus_error_free (&error);
3209
3210                 libhal_ctx_shutdown (hal_ctx, NULL);
3211                 libhal_ctx_free (hal_ctx);
3212                 dbus_g_connection_unref(gconn);
3213                 return;
3214         }
3215
3216         libhal_free_string_array (devices);
3217         dbus_g_connection_unref(gconn);
3218 #endif
3219 }
3220
3221 G_MODULE_EXPORT gboolean 
3222 tweak_setting_cb(
3223         GtkWidget *widget, 
3224         GdkEventButton *event, 
3225         signal_user_data_t *ud)
3226 {
3227         const gchar *name;
3228         gchar *tweak_name;
3229         gboolean ret = FALSE;
3230         gboolean allow_tweaks;
3231
3232         g_debug("press %d %d", event->type, event->button);
3233         allow_tweaks = ghb_settings_get_boolean(ud->settings, "allow_tweaks");
3234         if (allow_tweaks && event->type == GDK_BUTTON_PRESS && event->button == 3)
3235         { // Its a right mouse click
3236                 GtkWidget *dialog;
3237                 GtkEntry *entry;
3238                 GtkResponseType response;
3239                 gchar *tweak = NULL;
3240
3241                 name = gtk_widget_get_name(widget);
3242                 if (g_str_has_prefix(name, "tweak_"))
3243                 {
3244                         tweak_name = g_strdup(name);
3245                 }
3246                 else
3247                 {
3248                         tweak_name = g_strdup_printf("tweak_%s", name);
3249                 }
3250
3251                 tweak = ghb_settings_get_string (ud->settings, tweak_name);
3252                 dialog = GHB_WIDGET(ud->builder, "tweak_dialog");
3253                 gtk_window_set_title(GTK_WINDOW(dialog), tweak_name);
3254                 entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "tweak_setting"));
3255                 if (tweak)
3256                 {
3257                         gtk_entry_set_text(entry, tweak);
3258                         g_free(tweak);
3259                 }
3260                 response = gtk_dialog_run(GTK_DIALOG(dialog));
3261                 gtk_widget_hide(dialog);
3262                 if (response == GTK_RESPONSE_OK)
3263                 {
3264                         tweak = (gchar*)gtk_entry_get_text(entry);
3265                         if (ghb_validate_filter_string(tweak, -1))
3266                                 ghb_settings_set_string(ud->settings, tweak_name, tweak);
3267                         else
3268                         {
3269                                 gchar *message;
3270                                 message = g_strdup_printf(
3271                                                         "Invalid Settings:\n%s",
3272                                                         tweak);
3273                                 ghb_message_dialog(GTK_MESSAGE_ERROR, message, "Cancel", NULL);
3274                                 g_free(message);
3275                         }
3276                 }
3277                 g_free(tweak_name);
3278                 ret = TRUE;
3279         }
3280         return ret;
3281 }
3282
3283 G_MODULE_EXPORT gboolean 
3284 easter_egg_cb(
3285         GtkWidget *widget, 
3286         GdkEventButton *event, 
3287         signal_user_data_t *ud)
3288 {
3289         g_debug("press %d %d", event->type, event->button);
3290         if (event->type == GDK_3BUTTON_PRESS && event->button == 1)
3291         { // Its a tripple left mouse button click
3292                 GtkWidget *widget;
3293                 widget = GHB_WIDGET(ud->builder, "allow_tweaks");
3294                 gtk_widget_show(widget);
3295                 widget = GHB_WIDGET(ud->builder, "hbfd_feature");
3296                 gtk_widget_show(widget);
3297         }
3298         else if (event->type == GDK_BUTTON_PRESS && event->button == 1)
3299         {
3300                 GtkWidget *widget;
3301                 widget = GHB_WIDGET(ud->builder, "allow_tweaks");
3302                 gtk_widget_hide(widget);
3303                 widget = GHB_WIDGET(ud->builder, "hbfd_feature");
3304                 gtk_widget_hide(widget);
3305         }
3306         return FALSE;
3307 }
3308
3309 G_MODULE_EXPORT gchar*
3310 format_deblock_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud)
3311 {
3312         if (val < 5.0)
3313         {
3314                 return g_strdup_printf("Off");
3315         }
3316         else
3317         {
3318                 return g_strdup_printf("%d", (gint)val);
3319         }
3320 }
3321
3322 G_MODULE_EXPORT gchar*
3323 format_drc_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud)
3324 {
3325         if (val <= 0.0)
3326         {
3327                 return g_strdup_printf("Off");
3328         }
3329         else
3330         {
3331                 return g_strdup_printf("%.1f", val);
3332         }
3333 }
3334
3335 G_MODULE_EXPORT gchar*
3336 format_vquality_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud)
3337 {
3338         gdouble percent;
3339
3340         gint vcodec = ghb_settings_combo_int(ud->settings, "VideoEncoder");
3341         switch (vcodec)
3342         {
3343                 case HB_VCODEC_X264:
3344                 {
3345                         gboolean crf;
3346                         crf = ghb_settings_get_boolean(ud->settings, "constant_rate_factor");
3347                         percent = 100. * (51 - val) / 51.;
3348                         if (crf)
3349                                 return g_strdup_printf("RF: %.4g (%.0f%%)", val, percent);
3350                         else
3351                                 return g_strdup_printf("QP: %.4g (%.0f%%)", val, percent);
3352                 } break;
3353
3354                 case HB_VCODEC_XVID:
3355                 case HB_VCODEC_FFMPEG:
3356                 {
3357                         percent = 100. * (30 - (val - 1)) / 30.;
3358                         return g_strdup_printf("QP: %d (%.0f%%)", (int)val, percent);
3359                 } break;
3360
3361                 case HB_VCODEC_THEORA:
3362                 {
3363                         percent = 100. * val / 63.;
3364                         return g_strdup_printf("QP: %d (%.0f%%)", (int)val, percent);
3365                 } break;
3366
3367                 default:
3368                 {
3369                         percent = 0;
3370                 } break;
3371         }
3372         return g_strdup_printf("QP: %.1f / %.1f%%", val, percent);
3373 }
3374
3375 #if !defined(_WIN32)
3376 G_MODULE_EXPORT void
3377 html_link_cb(GtkHTML *html, const gchar *url, signal_user_data_t *ud)
3378 {
3379         browse_url(url);
3380 }
3381 #endif
3382
3383 static gpointer check_stable_update(signal_user_data_t *ud);
3384 static gboolean stable_update_lock = FALSE;
3385
3386 static void
3387 process_appcast(signal_user_data_t *ud)
3388 {
3389         gchar *description = NULL, *build = NULL, *version = NULL, *msg;
3390 #if !defined(_WIN32)
3391         GtkWidget *html, *window;
3392 #endif
3393         GtkWidget *dialog, *label;
3394         gint    response, ibuild = 0, skip;
3395
3396         if (ud->appcast == NULL || ud->appcast_len < 15 || 
3397                 strncmp(&(ud->appcast[9]), "200 OK", 6))
3398         {
3399                 if (!stable_update_lock && hb_get_build(NULL) % 100)
3400                         g_idle_add((GSourceFunc)check_stable_update, ud);
3401                 goto done;
3402         }
3403         ghb_appcast_parse(ud->appcast, &description, &build, &version);
3404         if (build)
3405                 ibuild = g_strtod(build, NULL);
3406         skip = ghb_settings_get_int(ud->settings, "update_skip_version");
3407         if (description == NULL || build == NULL || version == NULL 
3408                 || ibuild <= hb_get_build(NULL) || skip == ibuild)
3409         {
3410                 if (!stable_update_lock && hb_get_build(NULL) % 100)
3411                         g_thread_create((GThreadFunc)check_stable_update, ud, FALSE, NULL);
3412                 goto done;
3413         }
3414         msg = g_strdup_printf("HandBrake %s/%s is now available (you have %s/%d).",
3415                         version, build, hb_get_version(NULL), hb_get_build(NULL));
3416         label = GHB_WIDGET(ud->builder, "update_message");
3417         gtk_label_set_text(GTK_LABEL(label), msg);
3418 #if !defined(_WIN32)
3419         html = gtk_html_new_from_string(description, -1);
3420         g_signal_connect(html, "link_clicked", G_CALLBACK(html_link_cb), ud);
3421         window = GHB_WIDGET(ud->builder, "update_scroll");
3422         gtk_container_add(GTK_CONTAINER(window), html);
3423         // Show it
3424         gtk_widget_set_size_request(html, 420, 240);
3425         gtk_widget_show(html);
3426 #endif
3427         dialog = GHB_WIDGET(ud->builder, "update_dialog");
3428         response = gtk_dialog_run(GTK_DIALOG(dialog));
3429         gtk_widget_hide(dialog);
3430 #if !defined(_WIN32)
3431         gtk_widget_destroy(html);
3432 #endif
3433         if (response == GTK_RESPONSE_OK)
3434         {
3435                 // Skip
3436                 ghb_settings_set_int(ud->settings, "update_skip_version", ibuild);
3437                 ghb_pref_save(ud->settings, "update_skip_version");
3438         }
3439         g_free(msg);
3440
3441 done:
3442         if (description) g_free(description);
3443         if (build) g_free(build);
3444         if (version) g_free(version);
3445         g_free(ud->appcast);
3446         ud->appcast_len = 0;
3447         ud->appcast = NULL;
3448 }
3449
3450 void
3451 ghb_net_close(GIOChannel *ioc)
3452 {
3453         gint fd;
3454
3455         g_debug("ghb_net_close");
3456         if (ioc == NULL) return;
3457         fd = g_io_channel_unix_get_fd(ioc);
3458         close(fd);
3459         g_io_channel_unref(ioc);
3460 }
3461
3462 G_MODULE_EXPORT gboolean
3463 ghb_net_recv_cb(GIOChannel *ioc, GIOCondition cond, gpointer data)
3464 {
3465         gchar buf[2048];
3466         gsize len;
3467         GError *gerror = NULL;
3468         GIOStatus status;
3469         
3470         g_debug("ghb_net_recv_cb");
3471         signal_user_data_t *ud = (signal_user_data_t*)data;
3472
3473         status = g_io_channel_read_chars (ioc, buf, 2048, &len, &gerror);
3474         if ((status == G_IO_STATUS_NORMAL || status == G_IO_STATUS_EOF) &&
3475                 len > 0)
3476         {
3477                 gint new_len = ud->appcast_len + len;
3478                 ud->appcast = g_realloc(ud->appcast, new_len + 1);
3479                 memcpy(&(ud->appcast[ud->appcast_len]), buf, len);
3480                 ud->appcast_len = new_len;
3481         }
3482         if (status == G_IO_STATUS_EOF)
3483         {
3484                 ud->appcast[ud->appcast_len] = 0;
3485                 ghb_net_close(ioc);
3486                 process_appcast(ud);
3487                 return FALSE;
3488         }
3489         return TRUE;
3490 }
3491
3492 GIOChannel*
3493 ghb_net_open(signal_user_data_t *ud, gchar *address, gint port)
3494 {
3495         GIOChannel *ioc;
3496         gint fd;
3497
3498         struct sockaddr_in   sock;
3499         struct hostent     * host;
3500
3501         g_debug("ghb_net_open");
3502         if( !( host = gethostbyname( address ) ) )
3503         {
3504                 g_warning( "gethostbyname failed (%s)", address );
3505                 return NULL;
3506         }
3507
3508         memset( &sock, 0, sizeof( struct sockaddr_in ) );
3509         sock.sin_family = host->h_addrtype;
3510         sock.sin_port   = htons( port );
3511         memcpy( &sock.sin_addr, host->h_addr, host->h_length );
3512
3513         fd = socket(host->h_addrtype, SOCK_STREAM, 0);
3514         if( fd < 0 )
3515         {
3516                 g_debug( "socket failed" );
3517                 return NULL;
3518         }
3519
3520         if(connect(fd, (struct sockaddr*)&sock, sizeof(struct sockaddr_in )) < 0 )
3521         {
3522                 g_debug( "connect failed" );
3523                 return NULL;
3524         }
3525         ioc = g_io_channel_unix_new(fd);
3526         g_io_channel_set_encoding (ioc, NULL, NULL);
3527         g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
3528         g_io_add_watch (ioc, G_IO_IN, ghb_net_recv_cb, (gpointer)ud );
3529
3530         return ioc;
3531 }
3532
3533 gpointer
3534 ghb_check_update(signal_user_data_t *ud)
3535 {
3536         gchar *query;
3537         gsize len;
3538         GIOChannel *ioc;
3539         GError *gerror = NULL;
3540
3541         g_debug("ghb_check_update");
3542         if (hb_get_build(NULL) % 100)
3543         {
3544                 query = 
3545                 "GET /appcast_unstable.xml HTTP/1.0\r\nHost: handbrake.fr\r\n\r\n";
3546         }
3547         else
3548         {
3549                 stable_update_lock = TRUE;
3550                 query = "GET /appcast.xml HTTP/1.0\r\nHost: handbrake.fr\r\n\r\n";
3551         }
3552         ioc = ghb_net_open(ud, "handbrake.fr", 80);
3553         if (ioc == NULL)
3554                 return NULL;
3555
3556         g_io_channel_write_chars(ioc, query, strlen(query), &len, &gerror);
3557         g_io_channel_flush(ioc, &gerror);
3558         // This function is initiated by g_idle_add.  Must return false
3559         // so that it is not called again
3560         return NULL;
3561 }
3562
3563 static gpointer
3564 check_stable_update(signal_user_data_t *ud)
3565 {
3566         gchar *query;
3567         gsize len;
3568         GIOChannel *ioc;
3569         GError *gerror = NULL;
3570
3571         g_debug("check_stable_update");
3572         stable_update_lock = TRUE;
3573         query = "GET /appcast.xml HTTP/1.0\r\nHost: handbrake.fr\r\n\r\n";
3574         ioc = ghb_net_open(ud, "handbrake.fr", 80);
3575         if (ioc == NULL)
3576                 return NULL;
3577
3578         g_io_channel_write_chars(ioc, query, strlen(query), &len, &gerror);
3579         g_io_channel_flush(ioc, &gerror);
3580         // This function is initiated by g_idle_add.  Must return false
3581         // so that it is not called again
3582         return NULL;
3583 }
3584
3585 G_MODULE_EXPORT void
3586 status_activate_cb(GtkStatusIcon *si, signal_user_data_t *ud)
3587 {
3588         GtkWindow *window;
3589
3590         window = GTK_WINDOW(GHB_WIDGET(ud->builder, "hb_window"));
3591         gtk_window_present(window);
3592 }
3593
3594 #if !defined(_WIN32)
3595 G_MODULE_EXPORT void
3596 notify_closed_cb(NotifyNotification *notification, signal_user_data_t *ud)
3597 {
3598         g_object_unref(G_OBJECT(notification));
3599 }
3600 #endif
3601
3602 void
3603 ghb_notify_done(signal_user_data_t *ud)
3604 {
3605         GtkStatusIcon *si;
3606
3607         si = GTK_STATUS_ICON(GHB_OBJECT(ud->builder, "hb_status"));
3608         gtk_status_icon_set_from_icon_name(si, "hb-status-empty");
3609
3610 #if !defined(_WIN32)
3611         NotifyNotification *notification;
3612         notification = notify_notification_new(
3613                 "Encode Complete",
3614                 "Put down that cocktail, Your HandBrake queue is done!",
3615                 "hb-icon",
3616                 NULL);
3617         notify_notification_attach_to_status_icon(notification, si);
3618         g_signal_connect(notification, "closed", (GCallback)notify_closed_cb, ud);
3619         notify_notification_show(notification, NULL);
3620 #endif
3621 }