OSDN Git Service

LinGui: fix some text alignment issues on the picture summary tab
[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 #include <time.h>
22
23 #if !defined(_WIN32)
24 #include <poll.h>
25 #define G_UDEV_API_IS_SUBJECT_TO_CHANGE 1
26 #include <gudev/gudev.h>
27 #include <dbus/dbus-glib.h>
28 #include <dbus/dbus-glib-lowlevel.h>
29
30 #include <netinet/in.h>
31 #include <netdb.h>
32
33 #if !defined(_NO_UPDATE_CHECK)
34 #if defined(_OLD_WEBKIT)
35 #include <webkit.h>
36 #else
37 #include <webkit/webkit.h>
38 #endif
39 #endif
40
41 #include <libnotify/notify.h>
42 #include <gdk/gdkx.h>
43 #else
44 #define WINVER 0x0500
45 #include <winsock2.h>
46 #include <dbt.h>
47 #endif
48
49 #include <gtk/gtk.h>
50 #include <gdk/gdkkeysyms.h>
51 #include <glib/gstdio.h>
52 #include <gio/gio.h>
53
54 #include "hb.h"
55 #include "callbacks.h"
56 #include "queuehandler.h"
57 #include "audiohandler.h"
58 #include "subtitlehandler.h"
59 #include "resources.h"
60 #include "settings.h"
61 #include "presets.h"
62 #include "preview.h"
63 #include "values.h"
64 #include "plist.h"
65 #include "appcast.h"
66 #include "hb-backend.h"
67 #include "ghb-dvd.h"
68 #include "ghbcellrenderertext.h"
69
70 static void reset_chapter_list(signal_user_data_t *ud, GValue *settings);
71 static void update_chapter_list(signal_user_data_t *ud);
72 static GList* dvd_device_list();
73 static void prune_logs(signal_user_data_t *ud);
74 void ghb_notify_done(signal_user_data_t *ud);
75 gpointer ghb_check_update(signal_user_data_t *ud);
76 static gboolean ghb_can_shutdown_gsm();
77 static void ghb_shutdown_gsm();
78 static gboolean ghb_can_suspend_gpm();
79 static void ghb_suspend_gpm();
80 static gboolean appcast_busy = FALSE;
81
82 // This is a dependency map used for greying widgets
83 // that are dependent on the state of another widget.
84 // The enable_value comes from the values that are
85 // obtained from ghb_widget_value().  For combo boxes
86 // you will have to look further to combo box options
87 // maps in hb-backend.c
88
89 GValue *dep_map;
90 GValue *rev_map;
91
92 void
93 ghb_init_dep_map()
94 {
95         dep_map = ghb_resource_get("widget-deps");
96         rev_map = ghb_resource_get("widget-reverse-deps");
97 }
98
99 static gboolean
100 dep_check(signal_user_data_t *ud, const gchar *name, gboolean *out_hide)
101 {
102         GtkWidget *widget;
103         GObject *dep_object;
104         gint ii;
105         gint count;
106         gboolean result = TRUE;
107         GValue *array, *data;
108         gchar *widget_name;
109         
110         g_debug("dep_check () %s", name);
111
112         if (rev_map == NULL) return TRUE;
113         array = ghb_dict_lookup(rev_map, name);
114         count = ghb_array_len(array);
115         *out_hide = FALSE;
116         for (ii = 0; ii < count; ii++)
117         {
118                 data = ghb_array_get_nth(array, ii);
119                 widget_name = ghb_value_string(ghb_array_get_nth(data, 0));
120                 widget = GHB_WIDGET(ud->builder, widget_name);
121                 dep_object = gtk_builder_get_object(ud->builder, name);
122                 if (widget != NULL && !GTK_WIDGET_SENSITIVE(widget))
123                         continue;
124                 if (dep_object == NULL)
125                 {
126                         g_message("Failed to find widget");
127                 }
128                 else
129                 {
130                         gchar *value;
131                         gint jj = 0;
132                         gchar **values;
133                         gboolean sensitive = FALSE;
134                         gboolean die, hide;
135
136                         die = ghb_value_boolean(ghb_array_get_nth(data, 2));
137                         hide = ghb_value_boolean(ghb_array_get_nth(data, 3));
138                         value = ghb_value_string(ghb_array_get_nth(data, 1));
139                         values = g_strsplit(value, "|", 10);
140                         g_free(value);
141
142                         if (widget)
143                                 value = ghb_widget_string(widget);
144                         else
145                                 value = ghb_settings_get_string(ud->settings, widget_name);
146                         while (values && values[jj])
147                         {
148                                 if (values[jj][0] == '>')
149                                 {
150                                         gdouble dbl = g_strtod (&values[jj][1], NULL);
151                                         gdouble dvalue = ghb_widget_double(widget);
152                                         if (dvalue > dbl)
153                                         {
154                                                 sensitive = TRUE;
155                                                 break;
156                                         }
157                                 }
158                                 else if (values[jj][0] == '<')
159                                 {
160                                         gdouble dbl = g_strtod (&values[jj][1], NULL);
161                                         gdouble dvalue = ghb_widget_double(widget);
162                                         if (dvalue < dbl)
163                                         {
164                                                 sensitive = TRUE;
165                                                 break;
166                                         }
167                                 }
168                                 if (strcmp(values[jj], value) == 0)
169                                 {
170                                         sensitive = TRUE;
171                                         break;
172                                 }
173                                 jj++;
174                         }
175                         sensitive = die ^ sensitive;
176                         if (!sensitive)
177                         {
178                                 result = FALSE;
179                                 *out_hide |= hide;
180                         }
181                         g_strfreev (values);
182                         g_free(value);
183                 }
184                 g_free(widget_name);
185         }
186         return result;
187 }
188
189 void
190 ghb_check_dependency(
191         signal_user_data_t *ud, 
192         GtkWidget *widget, 
193         const char *alt_name)
194 {
195         GObject *dep_object;
196         const gchar *name;
197         GValue *array, *data;
198         gint count, ii;
199         gchar *dep_name;
200         GType type;
201
202         if (widget != NULL)
203         {
204                 type = GTK_WIDGET_TYPE(widget);
205                 if (type == GTK_TYPE_COMBO_BOX || type == GTK_TYPE_COMBO_BOX_ENTRY)
206                         if (gtk_combo_box_get_active(GTK_COMBO_BOX(widget)) < 0) return;
207                 name = ghb_get_setting_key(widget);
208         }
209         else
210                 name = alt_name;
211
212         g_debug("ghb_check_dependency () %s", name);
213
214         if (dep_map == NULL) return;
215         array = ghb_dict_lookup(dep_map, name);
216         count = ghb_array_len(array);
217         for (ii = 0; ii < count; ii++)
218         {
219                 gboolean sensitive;
220                 gboolean hide;
221
222                 data = ghb_array_get_nth(array, ii);
223                 dep_name = ghb_value_string(data);
224                 dep_object = gtk_builder_get_object(ud->builder, dep_name);
225                 if (dep_object == NULL)
226                 {
227                         g_message("Failed to find dependent widget %s", dep_name);
228                         g_free(dep_name);
229                         continue;
230                 }
231                 sensitive = dep_check(ud, dep_name, &hide);
232                 g_free(dep_name);
233                 if (GTK_IS_ACTION(dep_object))
234                 {
235                         gtk_action_set_sensitive(GTK_ACTION(dep_object), sensitive);
236                         gtk_action_set_visible(GTK_ACTION(dep_object), sensitive || !hide);
237                 }
238                 else
239                 {
240                         gtk_widget_set_sensitive(GTK_WIDGET(dep_object), sensitive);
241                         if (!sensitive && hide)
242                         {
243                                 gtk_widget_hide(GTK_WIDGET(dep_object));
244                         }
245                         else
246                         {
247                                 gtk_widget_show_now(GTK_WIDGET(dep_object));
248                         }
249                 }
250         }
251 }
252
253 void
254 ghb_check_all_depencencies(signal_user_data_t *ud)
255 {
256         GHashTableIter iter;
257         gchar *dep_name;
258         GValue *value;
259         GObject *dep_object;
260
261         g_debug("ghb_check_all_depencencies ()");
262         if (rev_map == NULL) return;
263         ghb_dict_iter_init(&iter, rev_map);
264         // middle (void*) cast prevents gcc warning "defreferencing type-punned
265         // pointer will break strict-aliasing rules"
266         while (g_hash_table_iter_next(
267                         &iter, (gpointer*)(void*)&dep_name, (gpointer*)(void*)&value))
268         {
269                 gboolean sensitive;
270                 gboolean hide;
271
272                 dep_object = gtk_builder_get_object (ud->builder, dep_name);
273                 if (dep_object == NULL)
274                 {
275                         g_message("Failed to find dependent widget %s", dep_name);
276                         continue;
277                 }
278                 sensitive = dep_check(ud, dep_name, &hide);
279                 if (GTK_IS_ACTION(dep_object))
280                 {
281                         gtk_action_set_sensitive(GTK_ACTION(dep_object), sensitive);
282                         gtk_action_set_visible(GTK_ACTION(dep_object), sensitive || !hide);
283                 }
284                 else
285                 {
286                         gtk_widget_set_sensitive(GTK_WIDGET(dep_object), sensitive);
287                         if (!sensitive && hide)
288                         {
289                                 gtk_widget_hide(GTK_WIDGET(dep_object));
290                         }
291                         else
292                         {
293                                 gtk_widget_show_now(GTK_WIDGET(dep_object));
294                         }
295                 }
296         }
297 }
298
299 G_MODULE_EXPORT void
300 on_quit1_activate(GtkMenuItem *quit, signal_user_data_t *ud)
301 {
302         gint state = ghb_get_queue_state();
303         g_debug("on_quit1_activate ()");
304         if (state & (GHB_STATE_WORKING|GHB_STATE_SEARCHING))
305         {
306                 if (ghb_cancel_encode2(ud, "Closing HandBrake will terminate encoding.\n"))
307                 {
308                         ghb_hb_cleanup(FALSE);
309                         prune_logs(ud);
310                         gtk_main_quit();
311                         return;
312                 }
313                 return;
314         }
315         ghb_hb_cleanup(FALSE);
316         prune_logs(ud);
317         gtk_main_quit();
318 }
319
320 gboolean
321 uppers_and_unders(gchar *str)
322 {
323         if (str == NULL) return FALSE;
324         str = g_strchomp(g_strchug(str));
325         while (*str)
326         {
327                 if (*str == ' ')
328                 {
329                         return FALSE;
330                 }
331                 if (*str >= 'a' && *str <= 'z')
332                 {
333                         return FALSE;
334                 }
335                 str++;
336         }
337         return TRUE;
338 }
339
340 enum
341 {
342         CAMEL_FIRST_UPPER,
343         CAMEL_OTHER
344 };
345
346 void
347 camel_convert(gchar *str)
348 {
349         gint state = CAMEL_OTHER;
350         
351         if (str == NULL) return;
352         while (*str)
353         {
354                 if (*str == '_') *str = ' ';
355                 switch (state)
356                 {
357                         case CAMEL_OTHER:
358                         {
359                                 if (*str >= 'A' && *str <= 'Z')
360                                         state = CAMEL_FIRST_UPPER;
361                                 else
362                                         state = CAMEL_OTHER;
363                                 
364                         } break;
365                         case CAMEL_FIRST_UPPER:
366                         {
367                                 if (*str >= 'A' && *str <= 'Z')
368                                         *str = *str - 'A' + 'a';
369                                 else
370                                         state = CAMEL_OTHER;
371                         } break;
372                 }
373                 str++;
374         }
375 }
376
377 #if defined(_WIN32)
378 static gchar*
379 get_dvd_device_name(gchar *device)
380 {
381         return g_strdup(device);
382 }
383 #else
384 static gchar*
385 get_dvd_device_name(GDrive *gd)
386 {
387         return g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
388 }
389 #endif
390
391 static GHashTable *volname_hash = NULL;
392 static GMutex     *volname_mutex = NULL;
393
394 static void
395 free_volname_key(gpointer data)
396 {
397         if (data != NULL)
398                 g_free(data);
399 }
400
401 static void
402 free_volname_value(gpointer data)
403 {
404         if (data != NULL)
405                 g_free(data);
406 }
407
408 #if defined(_WIN32)
409 static gchar*
410 get_direct_dvd_volume_name(const gchar *drive)
411 {
412         gchar *result = NULL;
413         gchar vname[51], fsname[51];
414
415         if (GetVolumeInformation(drive, vname, 50, NULL, NULL, NULL, fsname, 50))
416         {
417                 result = g_strdup_printf("%s", vname);
418         }
419         return result;
420 }
421 #else
422 static gchar*
423 get_direct_dvd_volume_name(const gchar *drive)
424 {
425         gchar *result;
426
427         result = ghb_dvd_volname (drive);
428         return result;
429 }
430 #endif
431
432 static gchar*
433 get_dvd_volume_name(gpointer gd)
434 {
435         gchar *label = NULL;
436         gchar *result;
437         gchar *drive;
438
439         drive = get_dvd_device_name(gd);
440         g_mutex_lock(volname_mutex);
441         label = g_strdup(g_hash_table_lookup(volname_hash, drive));
442         g_mutex_unlock(volname_mutex);
443         if (label != NULL)
444         {
445                 if (uppers_and_unders(label))
446                 {
447                         camel_convert(label);
448                 }
449 #if defined(_WIN32)
450                 result = g_strdup_printf("%s (%s)", label, drive);
451 #else
452                 result = g_strdup_printf("%s - %s", drive, label);
453 #endif
454                 g_free(label);
455         }
456         else
457         {
458                 result = g_strdup_printf("%s", drive);
459         }
460         g_free(drive);
461         return result;
462 }
463
464 void
465 ghb_volname_cache_init(void)
466 {
467         volname_mutex = g_mutex_new();
468         volname_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
469                                                                                 free_volname_key, free_volname_value);
470 }
471
472 static void
473 free_drive(gpointer drive)
474 {
475 #if defined(_WIN32)
476                 g_free(drive);
477 #else
478                 g_object_unref(drive);
479 #endif
480 }
481
482 gpointer
483 ghb_cache_volnames(signal_user_data_t *ud)
484 {
485         GList *link, *drives;
486
487         g_debug("ghb_cache_volnames()");
488         link = drives = dvd_device_list();
489         if (drives == NULL)
490                 return NULL;
491
492         g_mutex_lock(volname_mutex);
493         g_hash_table_remove_all(volname_hash);
494         while (link != NULL)
495         {
496                 gchar *name, *drive;
497
498 #if !defined(_WIN32)
499                 if (!g_drive_has_media (link->data))
500                 {
501                         g_object_unref(link->data);
502                         link = link->next;
503                         continue;
504                 }
505 #endif
506                 drive = get_dvd_device_name(link->data);
507                 name = get_direct_dvd_volume_name(drive);
508
509                 if (drive != NULL && name != NULL)
510                 {
511                         g_hash_table_insert(volname_hash, drive, name);
512                 }
513                 else
514                 {
515                         if (drive != NULL)
516                                 g_free(drive);
517                         if (name != NULL)
518                                 g_free(name);
519                 }
520         
521                 free_drive(link->data);
522                 link = link->next;
523         }
524         g_mutex_unlock(volname_mutex);
525
526         g_list_free(drives);
527
528         g_idle_add((GSourceFunc)ghb_file_menu_add_dvd, ud);
529
530         return NULL;
531 }
532
533 static const gchar*
534 get_extension(signal_user_data_t *ud)
535 {
536         int container;
537         const gchar *extension = "error";
538         GValue *audio_list;
539         GValue *subtitle_list;
540
541         container = ghb_settings_combo_int(ud->settings, "FileFormat");
542         if (container == HB_MUX_MP4)
543         {
544                 extension = "mp4";
545                 audio_list = ghb_settings_get_value(ud->settings, "audio_list");
546                 subtitle_list = ghb_settings_get_value(ud->settings, "subtitle_list");
547                 if (ghb_ac3_in_audio_list(audio_list) ||
548                         ghb_soft_in_subtitle_list(subtitle_list) ||
549                         ghb_settings_get_boolean(ud->settings, "ChapterMarkers") ||
550                         ghb_settings_get_boolean(ud->settings, "UseM4v"))
551                 {
552                         extension = "m4v";
553                 }
554         }
555         else if (container == HB_MUX_MKV)
556         {
557                 extension = "mkv";
558         }
559         return extension;
560 }
561
562 static void
563 set_destination(signal_user_data_t *ud)
564 {
565         g_debug("set_destination");
566         if (ghb_settings_get_boolean(ud->settings, "use_source_name"))
567         {
568                 GString *str = g_string_new("");
569                 gchar *vol_name, *filename;
570                 const gchar *extension;
571                 gchar *new_name;
572                 gint title;
573                 
574                 filename = ghb_settings_get_string(ud->settings, "dest_file");
575                 extension = get_extension(ud);
576                 vol_name = ghb_settings_get_string(ud->settings, "volume_label");
577                 g_string_append_printf(str, "%s", vol_name);
578                 title = ghb_settings_combo_int(ud->settings, "title");
579                 if (title >= 0)
580                 {
581                         if (ghb_settings_get_boolean(
582                                         ud->settings, "title_no_in_destination"))
583                         {
584
585                                 title = ghb_settings_combo_int(ud->settings, "title");
586                                 g_string_append_printf(str, " - %d", title+1);
587                         }
588                         if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0 && 
589                                 ghb_settings_get_boolean(
590                                         ud->settings, "chapters_in_destination"))
591                         {
592                                 gint start, end;
593
594                                 if (!ghb_settings_get_boolean(
595                                                 ud->settings, "title_no_in_destination"))
596                                 {
597                                         g_string_append_printf(str, " -");
598                                 }
599                                 start = ghb_settings_get_int(ud->settings, "start_point");
600                                 end = ghb_settings_get_int(ud->settings, "end_point");
601                                 if (start == end)
602                                         g_string_append_printf(str, " Ch %d", start);
603                                 else
604                                         g_string_append_printf(str, " Ch %d-%d", start, end);
605                         }
606                 }
607                 g_string_append_printf(str, ".%s", extension);
608                 new_name = g_string_free(str, FALSE);
609                 ghb_ui_update(ud, "dest_file", ghb_string_value(new_name));
610                 g_free(filename);
611                 g_free(vol_name);
612                 g_free(new_name);
613         }
614 }
615
616 static gchar*
617 get_file_label(const gchar *filename)
618 {
619         gchar *base, *pos, *end;
620
621         base = g_path_get_basename(filename);
622         pos = strrchr(base, '.');
623         if (pos != NULL)
624         {
625                 // If the last '.' is within 4 chars of end of name, assume
626                 // there is an extension we want to strip.
627                 end = &base[strlen(base) - 1];
628                 if (end - pos <= 4)
629                         *pos = 0;
630         }
631         return base;
632 }
633
634 static gchar*
635 resolve_drive_name(gchar *filename)
636 {
637 #if defined(_WIN32)
638         if (filename[1] == ':')
639         {
640                 gchar drive[4];
641                 gchar *name;
642                 gint dtype;
643
644                 g_strlcpy(drive, filename, 4);
645                 dtype = GetDriveType(drive);
646                 if (dtype == DRIVE_CDROM)
647                 {
648                         gchar vname[51], fsname[51];
649                         GetVolumeInformation(drive, vname, 50, NULL, 
650                                                                 NULL, NULL, fsname, 50);
651                         name = g_strdup(vname);
652                         return name;
653                 }
654         }
655         return NULL;
656 #else
657         return NULL;
658 #endif
659 }
660
661 static gboolean
662 update_source_label(signal_user_data_t *ud, const gchar *source, gboolean update_dest)
663 {
664         gchar *label = NULL;
665         gint len;
666         gchar **path;
667         gchar *start;
668         gchar *filename = g_strdup(source);
669         
670         g_debug("update_source_label()");
671         len = strlen(filename);
672         if (g_file_test(filename, G_FILE_TEST_IS_DIR))
673         {
674                 // Skip dos drive letters
675 #if defined(_WIN32)
676                 start = strchr(filename, ':');
677 #else
678                 start = filename;
679 #endif
680                 label = resolve_drive_name(filename);
681                 if (label != NULL)
682                 {
683                         if (uppers_and_unders(label))
684                         {
685                                 camel_convert(label);
686                         }
687                 }
688                 else
689                 {
690                         if (filename[len-1] == G_DIR_SEPARATOR) filename[len-1] = 0;
691                         if (start != NULL)
692                                 start++;
693                         else
694                                 start = filename;
695                         
696                         path = g_strsplit(start, G_DIR_SEPARATOR_S, -1);
697                         len = g_strv_length (path);
698                         if ((len > 1) && (strcmp("VIDEO_TS", path[len-1]) == 0))
699                         {
700                                 label = g_strdup(path[len-2]);
701                                 if (uppers_and_unders(label))
702                                 {
703                                         camel_convert(label);
704                                 }
705                         }
706                         else if (len > 0)
707                         {
708                                 if (path[len-1][0] != 0)
709                                 {
710                                         label = g_strdup(path[len-1]);
711                                         if (uppers_and_unders(label))
712                                         {
713                                                 camel_convert(label);
714                                         }
715                                 }
716                                 else
717                                         label = g_strdup("new_video");
718                         }
719                         else
720                                 label = g_strdup("new_video");
721                         g_strfreev (path);
722                 }
723         }
724         else
725         {
726                 // Is regular file or block dev.
727                 // Check to see if it is a dvd image
728                 label = ghb_dvd_volname (filename);
729                 if (label == NULL)
730                 {
731                         label = get_file_label(filename);
732                 }
733                 else
734                 {
735                         if (uppers_and_unders(label))
736                         {
737                                 camel_convert(label);
738                         }
739                 }
740         }
741         g_free(filename);
742         GtkWidget *widget = GHB_WIDGET (ud->builder, "source_title");
743         if (label != NULL)
744         {
745                 gtk_label_set_text (GTK_LABEL(widget), label);
746                 ghb_settings_set_string(ud->settings, "volume_label", label);
747                 g_free(label);
748                 if (update_dest)
749                         set_destination(ud);
750         }
751         else
752         {
753                 label = "No Title Found";
754                 gtk_label_set_text (GTK_LABEL(widget), label);
755                 ghb_settings_set_string(ud->settings, "volume_label", label);
756                 return FALSE;
757         }
758         return TRUE;
759 }
760
761 G_MODULE_EXPORT void
762 chooser_file_selected_cb(GtkFileChooser *dialog, signal_user_data_t *ud)
763 {
764         gchar *name = gtk_file_chooser_get_filename (dialog);
765         GtkTreeModel *store;
766         GtkTreeIter iter;
767         const gchar *device;
768         gboolean foundit = FALSE;
769         GtkComboBox *combo;
770         
771         g_debug("chooser_file_selected_cb ()");
772
773         if (name == NULL) return;
774         combo = GTK_COMBO_BOX(GHB_WIDGET(ud->builder, "source_device"));
775         store = gtk_combo_box_get_model(combo);
776         if (gtk_tree_model_get_iter_first(store, &iter))
777         {
778                 do
779                 {
780                         gtk_tree_model_get(store, &iter, 0, &device, -1);
781                         if (strcmp(name, device) == 0)
782                         {
783                                 foundit = TRUE;
784                                 break;
785                         }
786                 } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter));
787         }
788         if (foundit)
789                 gtk_combo_box_set_active_iter (combo, &iter);
790         else
791                 gtk_combo_box_set_active (combo, 0);
792
793         g_free(name);
794 }
795
796 G_MODULE_EXPORT void
797 dvd_device_changed_cb(GtkComboBox *combo, signal_user_data_t *ud)
798 {
799         GtkWidget *dialog;
800         gint ii;
801
802         g_debug("dvd_device_changed_cb ()");
803         ii = gtk_combo_box_get_active (combo);
804         if (ii > 0)
805         {
806                 const gchar *device;
807                 gchar *name;
808
809                 dialog = GHB_WIDGET(ud->builder, "source_dialog");
810                 device = gtk_combo_box_get_active_text (combo);
811                 name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog));
812                 if (name == NULL || strcmp(name, device) != 0)
813                         gtk_file_chooser_select_filename (GTK_FILE_CHOOSER(dialog), device);
814                 if (name != NULL)
815                         g_free(name);
816         }
817 }
818
819 static void
820 source_dialog_extra_widgets(
821         signal_user_data_t *ud,
822         GtkWidget *dialog)
823 {
824         GtkComboBox *combo;
825         GList *drives, *link;
826         
827         g_debug("source_dialog_extra_widgets ()");
828         combo = GTK_COMBO_BOX(GHB_WIDGET(ud->builder, "source_device"));
829         gtk_list_store_clear(GTK_LIST_STORE(gtk_combo_box_get_model(combo)));
830
831         link = drives = dvd_device_list();
832         gtk_combo_box_append_text (combo, "Not Selected");
833         while (link != NULL)
834         {
835                 gchar *name = get_dvd_device_name(link->data);
836                 gtk_combo_box_append_text(combo, name);
837                 g_free(name);
838                 free_drive(link->data);
839                 link = link->next;
840         }
841         g_list_free(drives);
842         gtk_combo_box_set_active (combo, 0);
843 }
844
845 extern GValue *ghb_queue_edit_settings;
846 static gchar *last_scan_file = NULL;
847
848 static void 
849 show_scan_progress(signal_user_data_t *ud)
850 {
851         GtkProgressBar *progress;
852         GtkLabel *label;
853
854         progress = GTK_PROGRESS_BAR(GHB_WIDGET(ud->builder, "scan_prog"));
855         gtk_progress_bar_set_fraction (progress, 0);
856         gtk_widget_show(GTK_WIDGET(progress));
857
858         label = GTK_LABEL(GHB_WIDGET(ud->builder, "source_title"));
859         gtk_label_set_text( label, "Scanning ..." );
860 }
861
862 static void
863 start_scan(
864         signal_user_data_t *ud, 
865         const gchar *path, 
866         gint titlenum, 
867         gint preview_count)
868 {
869         GtkWidget *widget;
870         GtkAction *action;
871         ghb_status_t status;
872
873         ghb_get_status(&status);
874         if (status.scan.state != GHB_STATE_IDLE)
875                 return;
876
877         widget = GHB_WIDGET(ud->builder, "sourcetoolbutton");
878         gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(widget), "hb-stop");
879         gtk_tool_button_set_label(GTK_TOOL_BUTTON(widget), "Stop Scan");
880         gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(widget), "Stop Scan");
881         //gtk_widget_set_sensitive(widget, FALSE);
882
883         action = GHB_ACTION(ud->builder, "source_action");
884         gtk_action_set_sensitive(action, FALSE);
885         action = GHB_ACTION(ud->builder, "source_single_action");
886         gtk_action_set_sensitive(action, FALSE);
887         ghb_backend_scan(path, titlenum, preview_count, 
888                         90000L * ghb_settings_get_int64(ud->settings, "MinTitleDuration"));
889 }
890
891 void
892 ghb_do_scan(
893         signal_user_data_t *ud, 
894         const gchar *filename, 
895         gint titlenum, 
896         gboolean force)
897 {
898         g_debug("ghb_do_scan()");
899         if (!force && last_scan_file != NULL &&
900                 strcmp(last_scan_file, filename) == 0)
901         {
902                 if (ghb_queue_edit_settings)
903                 {
904                         ghb_settings_to_ui(ud, ghb_queue_edit_settings);
905                         ghb_set_audio(ud, ghb_queue_edit_settings);
906                         ghb_reset_subtitles(ud, ghb_queue_edit_settings);
907                         reset_chapter_list(ud, ghb_queue_edit_settings);
908                         ghb_value_free(ghb_queue_edit_settings);
909                         ghb_queue_edit_settings = NULL;
910                 }
911                 return;
912         }
913         if (last_scan_file != NULL)
914                 g_free(last_scan_file);
915         last_scan_file = NULL;
916         if (filename != NULL)
917         {
918                 last_scan_file = g_strdup(filename);
919                 ghb_settings_set_string(ud->settings, "scan_source", filename);
920                 if (update_source_label(ud, filename, TRUE))
921                 {
922                         gchar *path;
923                         gint preview_count;
924
925                         show_scan_progress(ud);
926                         path = ghb_settings_get_string( ud->settings, "scan_source");
927                         prune_logs(ud);
928
929                         preview_count = ghb_settings_get_int(ud->settings, "preview_count");
930                         start_scan(ud, path, titlenum, preview_count);
931                         g_free(path);
932                 }
933                 else
934                 {
935                         // TODO: error dialog
936                 }
937         }
938 }
939
940 static void
941 do_source_dialog(GtkButton *button, gboolean single, signal_user_data_t *ud)
942 {
943         GtkWidget *dialog;
944         gchar *sourcename;
945         gint    response;
946
947         g_debug("source_browse_clicked_cb ()");
948         sourcename = ghb_settings_get_string(ud->settings, "scan_source");
949         GtkWidget *widget;
950         widget = GHB_WIDGET(ud->builder, "single_title_box");
951         if (single)
952                 gtk_widget_show(widget);
953         else
954                 gtk_widget_hide(widget);
955         dialog = GHB_WIDGET(ud->builder, "source_dialog");
956         source_dialog_extra_widgets(ud, dialog);
957         gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), sourcename);
958         response = gtk_dialog_run(GTK_DIALOG (dialog));
959         gtk_widget_hide(dialog);
960         if (response == GTK_RESPONSE_NO)
961         {
962                 gchar *filename;
963
964                 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
965                 if (filename != NULL)
966                 {
967                         gint titlenum;
968
969                         if (single)
970                                 titlenum = ghb_settings_get_int(ud->settings, "single_title");
971                         else
972                                 titlenum = 0;
973                         ghb_do_scan(ud, filename, titlenum, TRUE);
974                         if (strcmp(sourcename, filename) != 0)
975                         {
976                                 ghb_settings_set_string (ud->settings, 
977                                                                                 "default_source", filename);
978                                 ghb_pref_save (ud->settings, "default_source");
979                                 ghb_dvd_set_current (filename, ud);
980                         }
981                         g_free(filename);
982                 }
983         }
984         g_free(sourcename);
985 }
986
987 G_MODULE_EXPORT void
988 source_button_clicked_cb(GtkButton *button, signal_user_data_t *ud)
989 {
990         ghb_status_t status;
991         ghb_get_status(&status);
992         if (status.scan.state & GHB_STATE_SCANNING)
993         {
994                 ghb_backend_scan_stop();
995         }
996         else
997         {
998                 do_source_dialog(button, FALSE, ud);
999         }
1000 }
1001
1002 G_MODULE_EXPORT void
1003 single_title_source_cb(GtkButton *button, signal_user_data_t *ud)
1004 {
1005         do_source_dialog(button, TRUE, ud);
1006 }
1007
1008 G_MODULE_EXPORT void
1009 dvd_source_activate_cb(GtkAction *action, signal_user_data_t *ud)
1010 {
1011         const gchar *filename;
1012         gchar *sourcename;
1013
1014         sourcename = ghb_settings_get_string(ud->settings, "scan_source");
1015         filename = gtk_buildable_get_name(GTK_BUILDABLE(action));
1016         ghb_do_scan(ud, filename, 0, TRUE);
1017         if (strcmp(sourcename, filename) != 0)
1018         {
1019                 ghb_settings_set_string (ud->settings, "default_source", filename);
1020                 ghb_pref_save (ud->settings, "default_source");
1021                 ghb_dvd_set_current (filename, ud);
1022         }
1023         g_free(sourcename);
1024 }
1025
1026 void
1027 ghb_update_destination_extension(signal_user_data_t *ud)
1028 {
1029         static gchar *containers[] = {".mkv", ".mp4", ".m4v", NULL};
1030         gchar *filename;
1031         const gchar *extension;
1032         gint ii;
1033         GtkEntry *entry;
1034         static gboolean busy = FALSE;
1035
1036         g_debug("ghb_update_destination_extension ()");
1037         // Since this function modifies the thing that triggers it's
1038         // invocation, check to see if busy to prevent accidental infinite
1039         // recursion.
1040         if (busy)
1041                 return;
1042         busy = TRUE;
1043         extension = get_extension(ud);
1044         entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "dest_file"));
1045         filename = g_strdup(gtk_entry_get_text(entry));
1046         for (ii = 0; containers[ii] != NULL; ii++)
1047         {
1048                 if (g_str_has_suffix(filename, containers[ii]))
1049                 {
1050                         gchar *pos;
1051                         gchar *new_name;
1052                         
1053                         pos = g_strrstr( filename, "." );
1054                         if (pos == NULL)
1055                         {
1056                                 // No period? shouldn't happen
1057                                 break;
1058                         }
1059                         *pos = 0;
1060                         if (strcmp(extension, &pos[1]) == 0)
1061                         {
1062                                 // Extension is already correct
1063                                 break;
1064                         }
1065                         new_name = g_strjoin(".", filename, extension, NULL); 
1066                         ghb_ui_update(ud, "dest_file", ghb_string_value(new_name));
1067                         g_free(new_name);
1068                         break;
1069                 }
1070         }
1071         g_free(filename);
1072         busy = FALSE;
1073 }
1074
1075 static void
1076 destination_select_title(GtkEntry *entry)
1077 {
1078         const gchar *dest;
1079         gint start, end;
1080
1081         dest = gtk_entry_get_text(entry);
1082         for (end = strlen(dest)-1; end > 0; end--)
1083         {
1084                 if (dest[end] == '.')
1085                 {
1086                         break;
1087                 }
1088         }
1089         for (start = end; start >= 0; start--)
1090         {
1091                 if (dest[start] == G_DIR_SEPARATOR)
1092                 {
1093                         start++;
1094                         break;
1095                 }
1096         }
1097         if (start < 0) start = 0;
1098         if (start < end)
1099         {
1100                 gtk_editable_select_region(GTK_EDITABLE(entry), start, end);
1101         }
1102 }
1103
1104 G_MODULE_EXPORT gboolean
1105 destination_grab_cb(
1106         GtkEntry *entry, 
1107         signal_user_data_t *ud)
1108 {
1109         destination_select_title(entry);
1110         return FALSE;
1111 }
1112
1113 static gboolean update_default_destination = FALSE;
1114
1115 G_MODULE_EXPORT void
1116 dest_dir_set_cb(GtkFileChooserButton *dest_chooser, signal_user_data_t *ud)
1117 {
1118         gchar *dest_file, *dest_dir, *dest;
1119         
1120         g_debug("dest_dir_set_cb ()");
1121         ghb_widget_to_setting(ud->settings, (GtkWidget*)dest_chooser);
1122         dest_file = ghb_settings_get_string(ud->settings, "dest_file");
1123         dest_dir = ghb_settings_get_string(ud->settings, "dest_dir");
1124         dest = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dest_dir, dest_file);
1125         ghb_settings_set_string(ud->settings, "destination", dest);
1126         g_free(dest_file);
1127         g_free(dest_dir);
1128         g_free(dest);
1129         update_default_destination = TRUE;
1130 }
1131
1132 G_MODULE_EXPORT void
1133 dest_file_changed_cb(GtkEntry *entry, signal_user_data_t *ud)
1134 {
1135         gchar *dest_file, *dest_dir, *dest;
1136         
1137         g_debug("dest_file_changed_cb ()");
1138         ghb_update_destination_extension(ud);
1139         ghb_widget_to_setting(ud->settings, (GtkWidget*)entry);
1140         // This signal goes off with ever keystroke, so I'm putting this
1141         // update on the timer.
1142         dest_file = ghb_settings_get_string(ud->settings, "dest_file");
1143         dest_dir = ghb_settings_get_string(ud->settings, "dest_dir");
1144         dest = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dest_dir, dest_file);
1145         ghb_settings_set_string(ud->settings, "destination", dest);
1146         g_free(dest_file);
1147         g_free(dest_dir);
1148         g_free(dest);
1149         update_default_destination = TRUE;
1150 }
1151
1152 G_MODULE_EXPORT void
1153 destination_browse_clicked_cb(GtkButton *button, signal_user_data_t *ud)
1154 {
1155         GtkWidget *dialog;
1156         GtkEntry *entry;
1157         gchar *destname;
1158         gchar *basename;
1159
1160         g_debug("destination_browse_clicked_cb ()");
1161         destname = ghb_settings_get_string(ud->settings, "destination");
1162         dialog = gtk_file_chooser_dialog_new ("Choose Destination",
1163                                           NULL,
1164                                           GTK_FILE_CHOOSER_ACTION_SAVE,
1165                                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1166                                           GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
1167                                           NULL);
1168         gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), destname);
1169         basename = g_path_get_basename(destname);
1170         g_free(destname);
1171         gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), basename);
1172         g_free(basename);
1173         if (gtk_dialog_run(GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
1174         {
1175                 char *filename, *dirname;
1176                 GtkFileChooser *dest_chooser;
1177                 
1178                 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
1179                 basename = g_path_get_basename(filename);
1180                 dirname = g_path_get_dirname(filename);
1181                 entry = (GtkEntry*)GHB_WIDGET(ud->builder, "dest_file");
1182                 gtk_entry_set_text(entry, basename);
1183                 dest_chooser = GTK_FILE_CHOOSER(GHB_WIDGET(ud->builder, "dest_dir"));
1184                 gtk_file_chooser_set_filename(dest_chooser, dirname);
1185                 g_free (dirname);
1186                 g_free (basename);
1187                 g_free (filename);
1188         }
1189         gtk_widget_destroy(dialog);
1190 }
1191
1192 G_MODULE_EXPORT gboolean
1193 window_destroy_event_cb(GtkWidget *widget, GdkEvent *event, signal_user_data_t *ud)
1194 {
1195         g_debug("window_destroy_event_cb ()");
1196         ghb_hb_cleanup(FALSE);
1197         prune_logs(ud);
1198         gtk_main_quit();
1199         return FALSE;
1200 }
1201
1202 G_MODULE_EXPORT gboolean
1203 window_delete_event_cb(GtkWidget *widget, GdkEvent *event, signal_user_data_t *ud)
1204 {
1205         gint state = ghb_get_queue_state();
1206         g_debug("window_delete_event_cb ()");
1207         if (state & (GHB_STATE_WORKING|GHB_STATE_SEARCHING))
1208         {
1209                 if (ghb_cancel_encode2(ud, "Closing HandBrake will terminate encoding.\n"))
1210                 {
1211                         ghb_hb_cleanup(FALSE);
1212                         prune_logs(ud);
1213                         gtk_main_quit();
1214                         return FALSE;
1215                 }
1216                 return TRUE;
1217         }
1218         ghb_hb_cleanup(FALSE);
1219         prune_logs(ud);
1220         gtk_main_quit();
1221         return FALSE;
1222 }
1223
1224 static void
1225 update_acodec_combo(signal_user_data_t *ud)
1226 {
1227         ghb_grey_combo_options (ud->builder);
1228 }
1229
1230 G_MODULE_EXPORT void
1231 container_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1232 {
1233         g_debug("container_changed_cb ()");
1234         ghb_widget_to_setting(ud->settings, widget);
1235         ghb_check_dependency(ud, widget, NULL);
1236         update_acodec_combo(ud);
1237         ghb_update_destination_extension(ud);
1238         ghb_clear_presets_selection(ud);
1239         ghb_live_reset(ud);
1240         ghb_subtitle_prune(ud);
1241         ghb_audio_list_refresh_selected(ud);
1242 }
1243
1244 static gchar*
1245 get_aspect_string(gint aspect_n, gint aspect_d)
1246 {
1247         gchar *aspect;
1248
1249         if (aspect_d < 10)
1250         {
1251                 aspect = g_strdup_printf("%d:%d", aspect_n, aspect_d);
1252         }
1253         else
1254         {
1255                 gdouble aspect_nf = (gdouble)aspect_n / aspect_d;
1256                 aspect = g_strdup_printf("%.2f:1", aspect_nf);
1257         }
1258         return aspect;
1259 }
1260
1261 static gchar*
1262 get_rate_string(gint rate_base, gint rate)
1263 {
1264         gdouble rate_f = (gdouble)rate / rate_base;
1265         gchar *rate_s;
1266
1267         rate_s = g_strdup_printf("%.6g", rate_f);
1268         return rate_s;
1269 }
1270
1271 static void
1272 update_title_duration(signal_user_data_t *ud)
1273 {
1274         gint ti;
1275         gint hh, mm, ss, start, end;
1276         gchar *text;
1277         GtkWidget *widget;
1278
1279         ti = ghb_settings_combo_int(ud->settings, "title");
1280         widget = GHB_WIDGET (ud->builder, "title_duration");
1281
1282         if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0)
1283         {
1284                 start = ghb_settings_get_int(ud->settings, "start_point");
1285                 end = ghb_settings_get_int(ud->settings, "end_point");
1286                 ghb_part_duration(ti, start, end, &hh, &mm, &ss);
1287         }
1288         else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1)
1289         {
1290                 gint duration;
1291
1292                 start = ghb_settings_get_int(ud->settings, "start_point");
1293                 end = ghb_settings_get_int(ud->settings, "end_point");
1294                 duration = end - start;
1295                 hh = duration / (60*60);
1296                 mm = (duration / 60) % 60;
1297                 ss = duration % 60;
1298         }
1299         else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 2)
1300         {
1301                 ghb_title_info_t tinfo;
1302
1303                 if (ghb_get_title_info (&tinfo, ti))
1304                 {
1305                         gint64 frames;
1306                         gint duration;
1307
1308                         start = ghb_settings_get_int(ud->settings, "start_point");
1309                         end = ghb_settings_get_int(ud->settings, "end_point");
1310                         frames = end - start + 1;
1311                         duration = frames * tinfo.rate_base / tinfo.rate;
1312                         hh = duration / (60*60);
1313                         mm = (duration / 60) % 60;
1314                         ss = duration % 60;
1315                 }
1316                 else
1317                 {
1318                         hh = mm = ss = 0;
1319                 }
1320         }
1321         text = g_strdup_printf("%02d:%02d:%02d", hh, mm, ss);
1322         gtk_label_set_text (GTK_LABEL(widget), text);
1323         g_free(text);
1324 }
1325
1326 static void
1327 show_title_info(signal_user_data_t *ud, ghb_title_info_t *tinfo)
1328 {
1329         GtkWidget *widget;
1330         gchar *text;
1331
1332         ghb_settings_set_string(ud->settings, "source", tinfo->path);
1333         if (tinfo->type == HB_STREAM_TYPE)
1334         {
1335                 GtkWidget *widget = GHB_WIDGET (ud->builder, "source_title");
1336                 if (tinfo->name != NULL && tinfo->name[0] != 0)
1337                 {
1338                         gtk_label_set_text (GTK_LABEL(widget), tinfo->name);
1339                         ghb_settings_set_string(ud->settings, "volume_label", tinfo->name);
1340                         set_destination(ud);
1341                 }
1342                 else
1343                 {
1344                         gchar *label = "No Title Found";
1345                         gtk_label_set_text (GTK_LABEL(widget), label);
1346                         ghb_settings_set_string(ud->settings, "volume_label", label);
1347                 }
1348         }
1349         ud->dont_clear_presets = TRUE;
1350         ud->scale_busy = TRUE;
1351         update_title_duration(ud);
1352         widget = GHB_WIDGET (ud->builder, "source_dimensions");
1353         text = g_strdup_printf ("%d x %d", tinfo->width, tinfo->height);
1354         gtk_label_set_text (GTK_LABEL(widget), text);
1355         ghb_settings_set_int(ud->settings, "source_width", tinfo->width);
1356         ghb_settings_set_int(ud->settings, "source_height", tinfo->height);
1357         g_free(text);
1358         widget = GHB_WIDGET (ud->builder, "source_aspect");
1359         text = get_aspect_string(tinfo->aspect_n, tinfo->aspect_d);
1360         gtk_label_set_text (GTK_LABEL(widget), text);
1361         g_free(text);
1362
1363         widget = GHB_WIDGET (ud->builder, "source_frame_rate");
1364         text = (gchar*)get_rate_string(tinfo->rate_base, tinfo->rate);
1365         gtk_label_set_text (GTK_LABEL(widget), text);
1366         g_free(text);
1367
1368         //widget = GHB_WIDGET (ud->builder, "source_interlaced");
1369         //gtk_label_set_text (GTK_LABEL(widget), tinfo->interlaced ? "Yes" : "No");
1370
1371         ghb_ui_update(ud, "scale_width", 
1372                 ghb_int64_value(tinfo->width - tinfo->crop[2] - tinfo->crop[3]));
1373         // If anamorphic or keep_aspect, the hight will be automatically calculated
1374         gboolean keep_aspect;
1375         gint pic_par;
1376         keep_aspect = ghb_settings_get_boolean(ud->settings, "PictureKeepRatio");
1377         pic_par = ghb_settings_combo_int(ud->settings, "PicturePAR");
1378         if (!(keep_aspect || pic_par) || pic_par == 3)
1379         {
1380                 ghb_ui_update(ud, "scale_height", 
1381                         ghb_int64_value(tinfo->height - tinfo->crop[0] - tinfo->crop[1]));
1382         }
1383
1384         // Set the limits of cropping.  hb_set_anamorphic_size crashes if
1385         // you pass it a cropped width or height == 0.
1386         gint bound;
1387         bound = tinfo->height / 2 - 8;
1388         widget = GHB_WIDGET (ud->builder, "PictureTopCrop");
1389         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
1390         widget = GHB_WIDGET (ud->builder, "PictureBottomCrop");
1391         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
1392         bound = tinfo->width / 2 - 8;
1393         widget = GHB_WIDGET (ud->builder, "PictureLeftCrop");
1394         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
1395         widget = GHB_WIDGET (ud->builder, "PictureRightCrop");
1396         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, bound);
1397         if (ghb_settings_get_boolean(ud->settings, "PictureAutoCrop"))
1398         {
1399                 ghb_ui_update(ud, "PictureTopCrop", ghb_int64_value(tinfo->crop[0]));
1400                 ghb_ui_update(ud, "PictureBottomCrop", ghb_int64_value(tinfo->crop[1]));
1401                 ghb_ui_update(ud, "PictureLeftCrop", ghb_int64_value(tinfo->crop[2]));
1402                 ghb_ui_update(ud, "PictureRightCrop", ghb_int64_value(tinfo->crop[3]));
1403         }
1404         ud->scale_busy = FALSE;
1405         ghb_set_scale (ud, GHB_PIC_KEEP_PAR|GHB_PIC_USE_MAX);
1406         gint width, height, crop[4];
1407         crop[0] = ghb_settings_get_int(ud->settings, "PictureTopCrop");
1408         crop[1] = ghb_settings_get_int(ud->settings, "PictureBottomCrop");
1409         crop[2] = ghb_settings_get_int(ud->settings, "PictureLeftCrop");
1410         crop[3] = ghb_settings_get_int(ud->settings, "PictureRightCrop");
1411         width = tinfo->width - crop[2] - crop[3];
1412         height = tinfo->height - crop[0] - crop[1];
1413         widget = GHB_WIDGET (ud->builder, "crop_dimensions");
1414         text = g_strdup_printf ("%d x %d", width, height);
1415         gtk_label_set_text (GTK_LABEL(widget), text);
1416         g_free(text);
1417
1418
1419         gint duration = tinfo->duration / 90000;
1420
1421         if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0)
1422         {
1423                 widget = GHB_WIDGET (ud->builder, "start_point");
1424                 gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, tinfo->num_chapters);
1425                 gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), 1);
1426
1427                 widget = GHB_WIDGET (ud->builder, "end_point");
1428                 gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, tinfo->num_chapters);
1429                 gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), tinfo->num_chapters);
1430         }
1431         else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1)
1432         {
1433
1434                 widget = GHB_WIDGET (ud->builder, "start_point");
1435                 gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, duration-1);
1436                 gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), 0);
1437
1438                 widget = GHB_WIDGET (ud->builder, "end_point");
1439                 gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, duration);
1440                 gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), duration);
1441         }
1442         else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 2)
1443         {
1444                 gdouble max_frames = (gdouble)duration * tinfo->rate / tinfo->rate_base;
1445                 widget = GHB_WIDGET (ud->builder, "start_point");
1446                 gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, max_frames);
1447                 gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), 1);
1448
1449                 widget = GHB_WIDGET (ud->builder, "end_point");
1450                 gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, max_frames);
1451                 gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), max_frames);
1452         }
1453
1454         widget = GHB_WIDGET (ud->builder, "angle");
1455         gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), 1);
1456         gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, tinfo->angle_count);
1457         ghb_settings_set_int(ud->settings, "angle_count", tinfo->angle_count);
1458         ud->dont_clear_presets = FALSE;
1459 }
1460
1461 static gboolean update_preview = FALSE;
1462
1463 G_MODULE_EXPORT void
1464 title_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1465 {
1466         ghb_title_info_t tinfo;
1467         gint titleindex;
1468         
1469         g_debug("title_changed_cb ()");
1470         ghb_widget_to_setting(ud->settings, widget);
1471
1472         titleindex = ghb_settings_combo_int(ud->settings, "title");
1473         ghb_update_ui_combo_box (ud, "AudioTrack", titleindex, FALSE);
1474         ghb_update_ui_combo_box (ud, "SubtitleTrack", titleindex, FALSE);
1475
1476         if (ghb_get_title_info (&tinfo, titleindex))
1477         {
1478                 show_title_info(ud, &tinfo);
1479         }
1480         ghb_check_dependency(ud, widget, NULL);
1481         update_chapter_list (ud);
1482         ghb_adjust_audio_rate_combos(ud);
1483         ghb_set_pref_audio(titleindex, ud);
1484         ghb_set_pref_subtitle(titleindex, ud);
1485         if (ghb_settings_get_boolean(ud->settings, "vquality_type_target"))
1486         {
1487                 gint bitrate = ghb_calculate_target_bitrate (ud->settings, titleindex);
1488                 ghb_ui_update(ud, "VideoAvgBitrate", ghb_int64_value(bitrate));
1489         }
1490
1491         // Unfortunately, there is no way to query how many frames were
1492         // actually generated during the scan.
1493         // If I knew how many were generated, I would adjust the spin
1494         // control range here.
1495         // I do know how many were asked for.
1496         gint preview_count;
1497         preview_count = ghb_settings_get_int(ud->settings, "preview_count");
1498         widget = GHB_WIDGET(ud->builder, "preview_frame");
1499         gtk_range_set_range (GTK_RANGE(widget), 1, preview_count);
1500         ghb_ui_update(ud, "preview_frame", ghb_int64_value(2));
1501
1502         ghb_set_preview_image (ud);
1503         if (ghb_settings_get_boolean(ud->settings, "title_no_in_destination"))
1504         {
1505                 set_destination(ud);
1506         }
1507         ghb_preview_set_visible(ud);
1508
1509         gint end;
1510         widget = GHB_WIDGET (ud->builder, "ChapterMarkers");
1511         gtk_widget_set_sensitive(widget, TRUE);
1512         end = ghb_settings_get_int(ud->settings, "end_point");
1513         if (1 == end)
1514         {
1515                 ud->dont_clear_presets = TRUE;
1516                 ghb_ui_update(ud, "ChapterMarkers", ghb_boolean_value(FALSE));
1517                 ud->dont_clear_presets = FALSE;
1518                 gtk_widget_set_sensitive(widget, FALSE);
1519         }
1520 }
1521
1522 G_MODULE_EXPORT void
1523 ptop_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1524 {
1525         gint ti;
1526         ghb_title_info_t tinfo;
1527
1528         ghb_widget_to_setting(ud->settings, widget);
1529         ghb_check_dependency(ud, widget, NULL);
1530         ghb_live_reset(ud);
1531
1532         ti = ghb_settings_combo_int(ud->settings, "title");
1533         if (!ghb_get_title_info (&tinfo, ti))
1534                 return;
1535
1536         gint duration = tinfo.duration / 90000;
1537         if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0)
1538         {
1539                 widget = GHB_WIDGET (ud->builder, "start_point");
1540                 gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, tinfo.num_chapters);
1541                 gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), 1);
1542
1543                 widget = GHB_WIDGET (ud->builder, "end_point");
1544                 gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, tinfo.num_chapters);
1545                 gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), tinfo.num_chapters);
1546         }
1547         else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1)
1548         {
1549                 widget = GHB_WIDGET (ud->builder, "start_point");
1550                 gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 0, duration-1);
1551                 gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), 0);
1552
1553                 widget = GHB_WIDGET (ud->builder, "end_point");
1554                 gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, duration);
1555                 gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), duration);
1556         }
1557         else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 2)
1558         {
1559                 gdouble max_frames = (gdouble)duration * tinfo.rate / tinfo.rate_base;
1560                 widget = GHB_WIDGET (ud->builder, "start_point");
1561                 gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, max_frames);
1562                 gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), 1);
1563
1564                 widget = GHB_WIDGET (ud->builder, "end_point");
1565                 gtk_spin_button_set_range (GTK_SPIN_BUTTON(widget), 1, max_frames);
1566                 gtk_spin_button_set_value (GTK_SPIN_BUTTON(widget), max_frames);
1567         }
1568 }
1569
1570 G_MODULE_EXPORT void
1571 setting_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1572 {
1573         ghb_widget_to_setting(ud->settings, widget);
1574         ghb_check_dependency(ud, widget, NULL);
1575         ghb_clear_presets_selection(ud);
1576         ghb_live_reset(ud);
1577 }
1578
1579 G_MODULE_EXPORT void
1580 chapter_markers_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1581 {
1582         ghb_widget_to_setting(ud->settings, widget);
1583         ghb_check_dependency(ud, widget, NULL);
1584         ghb_clear_presets_selection(ud);
1585         ghb_live_reset(ud);
1586         ghb_update_destination_extension(ud);
1587 }
1588
1589 G_MODULE_EXPORT void
1590 vquality_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1591 {
1592         ghb_widget_to_setting(ud->settings, widget);
1593         ghb_check_dependency(ud, widget, NULL);
1594         ghb_clear_presets_selection(ud);
1595         ghb_live_reset(ud);
1596
1597         gint vcodec = ghb_settings_combo_int(ud->settings, "VideoEncoder");
1598         gdouble step;
1599         if (vcodec == HB_VCODEC_X264)
1600         {
1601                 step = ghb_settings_combo_double(ud->settings, 
1602                                                                                         "VideoQualityGranularity");
1603         }
1604         else
1605         {
1606                 step = 1;
1607         }
1608         gdouble val = gtk_range_get_value(GTK_RANGE(widget));
1609         val = ((int)((val + step / 2) / step)) * step;
1610         gtk_range_set_value(GTK_RANGE(widget), val);
1611 }
1612
1613 G_MODULE_EXPORT void
1614 http_opt_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1615 {
1616         ghb_widget_to_setting(ud->settings, widget);
1617         ghb_check_dependency(ud, widget, NULL);
1618         ghb_clear_presets_selection(ud);
1619         ghb_live_reset(ud);
1620         // AC3 is not allowed when Web optimized
1621         ghb_grey_combo_options (ud->builder);
1622 }
1623
1624 G_MODULE_EXPORT void
1625 vcodec_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1626 {
1627         gdouble vqmin, vqmax, step, page;
1628         gboolean inverted;
1629         gint digits;
1630
1631         ghb_widget_to_setting(ud->settings, widget);
1632         ghb_check_dependency(ud, widget, NULL);
1633         ghb_clear_presets_selection(ud);
1634         ghb_live_reset(ud);
1635         ghb_vquality_range(ud, &vqmin, &vqmax, &step, &page, &digits, &inverted);
1636         GtkWidget *qp = GHB_WIDGET(ud->builder, "VideoQualitySlider");
1637         gtk_range_set_range (GTK_RANGE(qp), vqmin, vqmax);
1638         gtk_range_set_increments (GTK_RANGE(qp), step, page);
1639         gtk_scale_set_digits(GTK_SCALE(qp), digits);
1640         gtk_range_set_inverted (GTK_RANGE(qp), inverted);
1641 }
1642
1643 G_MODULE_EXPORT void
1644 target_size_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1645 {
1646         const gchar *name = ghb_get_setting_key(widget);
1647         g_debug("target_size_changed_cb () %s", name);
1648         ghb_widget_to_setting(ud->settings, widget);
1649         ghb_check_dependency(ud, widget, NULL);
1650         ghb_clear_presets_selection(ud);
1651         ghb_live_reset(ud);
1652         if (ghb_settings_get_boolean(ud->settings, "vquality_type_target"))
1653         {
1654                 gint titleindex;
1655                 titleindex = ghb_settings_combo_int(ud->settings, "title");
1656                 gint bitrate = ghb_calculate_target_bitrate (ud->settings, titleindex);
1657                 ghb_ui_update(ud, "VideoAvgBitrate", ghb_int64_value(bitrate));
1658         }
1659 }
1660
1661 G_MODULE_EXPORT void
1662 start_point_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1663 {
1664         gint start, end;
1665         const gchar *name = ghb_get_setting_key(widget);
1666
1667         g_debug("start_point_changed_cb () %s", name);
1668         ghb_widget_to_setting(ud->settings, widget);
1669         if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0)
1670         {
1671                 start = ghb_settings_get_int(ud->settings, "start_point");
1672                 end = ghb_settings_get_int(ud->settings, "end_point");
1673                 if (start > end)
1674                         ghb_ui_update(ud, "end_point", ghb_int_value(start));
1675                 ghb_check_dependency(ud, widget, NULL);
1676                 if (ghb_settings_get_boolean(ud->settings, "chapters_in_destination"))
1677                 {
1678                         set_destination(ud);
1679                 }
1680                 widget = GHB_WIDGET (ud->builder, "ChapterMarkers");
1681                 gtk_widget_set_sensitive(widget, TRUE);
1682                 // End may have been changed above, get it again
1683                 end = ghb_settings_get_int(ud->settings, "end_point");
1684                 if (start == end)
1685                 {
1686                         ud->dont_clear_presets = TRUE;
1687                         ghb_ui_update(ud, "ChapterMarkers", ghb_boolean_value(FALSE));
1688                         ud->dont_clear_presets = FALSE;
1689                         gtk_widget_set_sensitive(widget, FALSE);
1690                 }
1691                 update_title_duration(ud);
1692         }
1693         else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1)
1694         {
1695                 start = ghb_settings_get_int(ud->settings, "start_point");
1696                 end = ghb_settings_get_int(ud->settings, "end_point");
1697                 if (start >= end)
1698                         ghb_ui_update(ud, "end_point", ghb_int_value(start+1));
1699                 ghb_check_dependency(ud, widget, NULL);
1700                 update_title_duration(ud);
1701         }
1702         else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 2)
1703         {
1704                 start = ghb_settings_get_int(ud->settings, "start_point");
1705                 end = ghb_settings_get_int(ud->settings, "end_point");
1706                 if (start > end)
1707                         ghb_ui_update(ud, "end_point", ghb_int_value(start));
1708                 ghb_check_dependency(ud, widget, NULL);
1709                 update_title_duration(ud);
1710         }
1711 }
1712
1713 G_MODULE_EXPORT void
1714 end_point_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1715 {
1716         gint start, end;
1717         const gchar *name = ghb_get_setting_key(widget);
1718
1719         g_debug("end_point_changed_cb () %s", name);
1720         ghb_widget_to_setting(ud->settings, widget);
1721         if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0)
1722         {
1723                 start = ghb_settings_get_int(ud->settings, "start_point");
1724                 end = ghb_settings_get_int(ud->settings, "end_point");
1725                 if (start > end)
1726                         ghb_ui_update(ud, "start_point", ghb_int_value(end));
1727                 ghb_check_dependency(ud, widget, NULL);
1728                 if (ghb_settings_get_boolean(ud->settings, "chapters_in_destination"))
1729                 {
1730                         set_destination(ud);
1731                 }
1732                 widget = GHB_WIDGET (ud->builder, "ChapterMarkers");
1733                 gtk_widget_set_sensitive(widget, TRUE);
1734                 // Start may have been changed above, get it again
1735                 start = ghb_settings_get_int(ud->settings, "start_point");
1736                 if (start == end)
1737                 {
1738                         ud->dont_clear_presets = TRUE;
1739                         ghb_ui_update(ud, "ChapterMarkers", ghb_boolean_value(FALSE));
1740                         ud->dont_clear_presets = FALSE;
1741                         gtk_widget_set_sensitive(widget, FALSE);
1742                 }
1743                 update_title_duration(ud);
1744         }
1745         else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1)
1746         {
1747                 start = ghb_settings_get_int(ud->settings, "start_point");
1748                 end = ghb_settings_get_int(ud->settings, "end_point");
1749                 if (start >= end)
1750                         ghb_ui_update(ud, "start_point", ghb_int_value(end-1));
1751                 ghb_check_dependency(ud, widget, NULL);
1752                 update_title_duration(ud);
1753         }
1754         else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 2)
1755         {
1756                 start = ghb_settings_get_int(ud->settings, "start_point");
1757                 end = ghb_settings_get_int(ud->settings, "end_point");
1758                 if (start > end)
1759                         ghb_ui_update(ud, "start_point", ghb_int_value(end));
1760                 ghb_check_dependency(ud, widget, NULL);
1761                 update_title_duration(ud);
1762         }
1763 }
1764
1765 G_MODULE_EXPORT void
1766 scale_width_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1767 {
1768         g_debug("scale_width_changed_cb ()");
1769         ghb_widget_to_setting(ud->settings, widget);
1770         ghb_check_dependency(ud, widget, NULL);
1771         ghb_clear_presets_selection(ud);
1772         if (GTK_WIDGET_SENSITIVE(widget))
1773                 ghb_set_scale (ud, GHB_PIC_KEEP_WIDTH);
1774         update_preview = TRUE;
1775         gchar *text;
1776         gint width = ghb_settings_get_int(ud->settings, "scale_width");
1777         gint height = ghb_settings_get_int(ud->settings, "scale_height");
1778         widget = GHB_WIDGET (ud->builder, "scale_dimensions");
1779         text = g_strdup_printf ("%d x %d", width, height);
1780         gtk_label_set_text (GTK_LABEL(widget), text);
1781         g_free(text);
1782         ghb_live_reset(ud);
1783 }
1784
1785 G_MODULE_EXPORT void
1786 scale_height_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1787 {
1788         g_debug("scale_height_changed_cb ()");
1789         ghb_widget_to_setting(ud->settings, widget);
1790         ghb_check_dependency(ud, widget, NULL);
1791         ghb_clear_presets_selection(ud);
1792         if (GTK_WIDGET_SENSITIVE(widget))
1793                 ghb_set_scale (ud, GHB_PIC_KEEP_HEIGHT);
1794         update_preview = TRUE;
1795         gchar *text;
1796         gint width = ghb_settings_get_int(ud->settings, "scale_width");
1797         gint height = ghb_settings_get_int(ud->settings, "scale_height");
1798         widget = GHB_WIDGET (ud->builder, "scale_dimensions");
1799         text = g_strdup_printf ("%d x %d", width, height);
1800         gtk_label_set_text (GTK_LABEL(widget), text);
1801         g_free(text);
1802         ghb_live_reset(ud);
1803 }
1804
1805 G_MODULE_EXPORT void
1806 crop_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1807 {
1808         gint titleindex, crop[4];
1809         ghb_title_info_t tinfo;
1810         
1811         g_debug("crop_changed_cb ()");
1812         ghb_widget_to_setting(ud->settings, widget);
1813         ghb_check_dependency(ud, widget, NULL);
1814         ghb_clear_presets_selection(ud);
1815         if (GTK_WIDGET_SENSITIVE(widget))
1816                 ghb_set_scale (ud, 0);
1817
1818         crop[0] = ghb_settings_get_int(ud->settings, "PictureTopCrop");
1819         crop[1] = ghb_settings_get_int(ud->settings, "PictureBottomCrop");
1820         crop[2] = ghb_settings_get_int(ud->settings, "PictureLeftCrop");
1821         crop[3] = ghb_settings_get_int(ud->settings, "PictureRightCrop");
1822         titleindex = ghb_settings_combo_int(ud->settings, "title");
1823         if (ghb_get_title_info (&tinfo, titleindex))
1824         {
1825                 gint width, height;
1826                 gchar *text;
1827                 
1828                 width = tinfo.width - crop[2] - crop[3];
1829                 height = tinfo.height - crop[0] - crop[1];
1830                 widget = GHB_WIDGET (ud->builder, "crop_dimensions");
1831                 text = g_strdup_printf ("%d x %d", width, height);
1832                 gtk_label_set_text (GTK_LABEL(widget), text);
1833                 widget = GHB_WIDGET (ud->builder, "crop_dimensions2");
1834                 gtk_label_set_text (GTK_LABEL(widget), text);
1835                 g_free(text);
1836         }
1837         gchar *text;
1838         widget = GHB_WIDGET (ud->builder, "crop_values");
1839         text = g_strdup_printf ("%d:%d:%d:%d", crop[0], crop[1], crop[2], crop[3]);
1840         gtk_label_set_text (GTK_LABEL(widget), text);
1841         g_free(text);
1842         update_preview = TRUE;
1843         ghb_live_reset(ud);
1844 }
1845
1846 G_MODULE_EXPORT void
1847 display_width_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1848 {
1849         g_debug("display_width_changed_cb ()");
1850         ghb_widget_to_setting(ud->settings, widget);
1851         ghb_check_dependency(ud, widget, NULL);
1852         ghb_clear_presets_selection(ud);
1853         ghb_live_reset(ud);
1854         if (GTK_WIDGET_SENSITIVE(widget))
1855                 ghb_set_scale (ud, GHB_PIC_KEEP_DISPLAY_WIDTH);
1856
1857         update_preview = TRUE;
1858 }
1859
1860 G_MODULE_EXPORT void
1861 display_height_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1862 {
1863         g_debug("display_height_changed_cb ()");
1864         ghb_widget_to_setting(ud->settings, widget);
1865         ghb_check_dependency(ud, widget, NULL);
1866         ghb_clear_presets_selection(ud);
1867         ghb_live_reset(ud);
1868         if (GTK_WIDGET_SENSITIVE(widget))
1869                 ghb_set_scale (ud, GHB_PIC_KEEP_DISPLAY_HEIGHT);
1870
1871         update_preview = TRUE;
1872 }
1873
1874 G_MODULE_EXPORT void
1875 par_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1876 {
1877         g_debug("par_changed_cb ()");
1878         ghb_widget_to_setting(ud->settings, widget);
1879         ghb_check_dependency(ud, widget, NULL);
1880         ghb_clear_presets_selection(ud);
1881         ghb_live_reset(ud);
1882         if (GTK_WIDGET_SENSITIVE(widget))
1883                 ghb_set_scale (ud, GHB_PIC_KEEP_PAR);
1884
1885         update_preview = TRUE;
1886 }
1887
1888 G_MODULE_EXPORT void
1889 scale_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1890 {
1891         g_debug("scale_changed_cb ()");
1892         ghb_widget_to_setting(ud->settings, widget);
1893         ghb_check_dependency(ud, widget, NULL);
1894         ghb_clear_presets_selection(ud);
1895         ghb_live_reset(ud);
1896         if (GTK_WIDGET_SENSITIVE(widget))
1897                 ghb_set_scale (ud, 0);
1898         update_preview = TRUE;
1899         
1900         gchar *text;
1901         
1902         text = ghb_settings_get_boolean(ud->settings, "PictureAutoCrop") ? "On" : "Off";
1903         widget = GHB_WIDGET (ud->builder, "crop_auto");
1904         gtk_label_set_text (GTK_LABEL(widget), text);
1905         text = ghb_settings_get_boolean(ud->settings, "autoscale") ? "On" : "Off";
1906         widget = GHB_WIDGET (ud->builder, "scale_auto");
1907         gtk_label_set_text (GTK_LABEL(widget), text);
1908         switch (ghb_settings_combo_int(ud->settings, "PicturePAR"))
1909         {
1910                 case 0:
1911                         text = "Off";
1912                         break;
1913                 case 1:
1914                         text = "Strict";
1915                         break;
1916                 case 2:
1917                         text = "Loose";
1918                         break;
1919                 case 3:
1920                         text = "Custom";
1921                         break;
1922                 default:
1923                         text = "Unknown";
1924                         break;
1925         }
1926         widget = GHB_WIDGET (ud->builder, "scale_anamorphic");
1927         gtk_label_set_text (GTK_LABEL(widget), text);
1928 }
1929
1930 G_MODULE_EXPORT void
1931 show_crop_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
1932 {
1933         g_debug("show_crop_changed_cb ()");
1934         ghb_widget_to_setting(ud->settings, widget);
1935         ghb_check_dependency(ud, widget, NULL);
1936         ghb_live_reset(ud);
1937         if (GTK_WIDGET_SENSITIVE(widget))
1938                 ghb_set_scale (ud, 0);
1939         update_preview = TRUE;
1940 }
1941
1942 G_MODULE_EXPORT void
1943 generic_entry_changed_cb(GtkEntry *entry, signal_user_data_t *ud)
1944 {
1945         // Normally (due to user input) I only want to process the entry
1946         // when editing is done and the focus-out signal is sent.
1947         // But... there's always a but.
1948         // If the entry is changed by software, the focus-out signal is not sent.
1949         // The changed signal is sent ... so here we are.
1950         // I don't want to process upon every keystroke, so I prevent processing
1951         // while the widget has focus.
1952         g_debug("generic_entry_changed_cb ()");
1953         if (!GTK_WIDGET_HAS_FOCUS((GtkWidget*)entry))
1954         {
1955                 ghb_widget_to_setting(ud->settings, (GtkWidget*)entry);
1956         }
1957 }
1958
1959 G_MODULE_EXPORT void
1960 prefs_dialog_cb(GtkWidget *xwidget, signal_user_data_t *ud)
1961 {
1962         GtkWidget *dialog;
1963         GtkResponseType response;
1964
1965         g_debug("prefs_dialog_cb ()");
1966         dialog = GHB_WIDGET(ud->builder, "prefs_dialog");
1967         response = gtk_dialog_run(GTK_DIALOG(dialog));
1968         gtk_widget_hide(dialog);
1969 }
1970
1971 typedef struct
1972 {
1973         GtkMessageDialog *dlg;
1974         const gchar *msg;
1975         const gchar *action;
1976         gint timeout;
1977 } countdown_t;
1978
1979 static gboolean
1980 shutdown_cb(countdown_t *cd)
1981 {
1982         gchar *str;
1983
1984         cd->timeout--;
1985         if (cd->timeout == 0)
1986         {
1987                 ghb_shutdown_gsm();
1988                 gtk_main_quit();
1989                 return FALSE;
1990         }
1991         str = g_strdup_printf("%s\n\n%s in %d seconds ...", 
1992                                                         cd->msg, cd->action, cd->timeout);
1993         gtk_message_dialog_set_markup(cd->dlg, str);
1994         g_free(str);
1995         return TRUE;
1996 }
1997
1998 static gboolean
1999 suspend_cb(countdown_t *cd)
2000 {
2001         gchar *str;
2002
2003         cd->timeout--;
2004         if (cd->timeout == 0)
2005         {
2006                 gtk_widget_destroy (GTK_WIDGET(cd->dlg));
2007                 ghb_suspend_gpm();
2008                 return FALSE;
2009         }
2010         str = g_strdup_printf("%s\n\n%s in %d seconds ...", 
2011                                                         cd->msg, cd->action, cd->timeout);
2012         gtk_message_dialog_set_markup(cd->dlg, str);
2013         g_free(str);
2014         return TRUE;
2015 }
2016
2017 void
2018 ghb_countdown_dialog(
2019         GtkMessageType type, 
2020         const gchar *message, 
2021         const gchar *action, 
2022         const gchar *cancel, 
2023         GSourceFunc action_func,
2024         gint timeout)
2025 {
2026         GtkWidget *dialog;
2027         GtkResponseType response;
2028         guint timeout_id;
2029         countdown_t cd;
2030                         
2031         cd.msg = message;
2032         cd.action = action;
2033         cd.timeout = timeout;
2034
2035         // Toss up a warning dialog
2036         dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
2037                                                         type, GTK_BUTTONS_NONE,
2038                                                         "%s\n\n%s in %d seconds ...", 
2039                                                         message, action, timeout);
2040         gtk_dialog_add_buttons( GTK_DIALOG(dialog), 
2041                                                    cancel, GTK_RESPONSE_CANCEL,
2042                                                    NULL);
2043
2044         cd.dlg = GTK_MESSAGE_DIALOG(dialog);
2045         timeout_id = g_timeout_add(1000, action_func, &cd);
2046         response = gtk_dialog_run(GTK_DIALOG(dialog));
2047         gtk_widget_destroy (dialog);
2048         if (response == GTK_RESPONSE_CANCEL)
2049         {
2050                 GMainContext *mc;
2051                 GSource *source;
2052
2053                 mc = g_main_context_default();
2054                 source = g_main_context_find_source_by_id(mc, timeout_id);
2055                 if (source != NULL)
2056                         g_source_destroy(source);
2057         }
2058 }
2059
2060 gboolean
2061 ghb_message_dialog(GtkMessageType type, const gchar *message, const gchar *no, const gchar *yes)
2062 {
2063         GtkWidget *dialog;
2064         GtkResponseType response;
2065                         
2066         // Toss up a warning dialog
2067         dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
2068                                                         type, GTK_BUTTONS_NONE,
2069                                                         "%s", message);
2070         gtk_dialog_add_buttons( GTK_DIALOG(dialog), 
2071                                                    no, GTK_RESPONSE_NO,
2072                                                    yes, GTK_RESPONSE_YES, NULL);
2073         response = gtk_dialog_run(GTK_DIALOG(dialog));
2074         gtk_widget_destroy (dialog);
2075         if (response == GTK_RESPONSE_NO)
2076         {
2077                 return FALSE;
2078         }
2079         return TRUE;
2080 }
2081
2082 void
2083 ghb_error_dialog(GtkMessageType type, const gchar *message, const gchar *cancel)
2084 {
2085         GtkWidget *dialog;
2086         GtkResponseType response;
2087                         
2088         // Toss up a warning dialog
2089         dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
2090                                                         type, GTK_BUTTONS_NONE,
2091                                                         "%s", message);
2092         gtk_dialog_add_buttons( GTK_DIALOG(dialog), 
2093                                                    cancel, GTK_RESPONSE_CANCEL, NULL);
2094         response = gtk_dialog_run(GTK_DIALOG(dialog));
2095         gtk_widget_destroy (dialog);
2096 }
2097
2098 void
2099 ghb_cancel_encode(signal_user_data_t *ud, const gchar *extra_msg)
2100 {
2101         GtkWidget *dialog;
2102         GtkResponseType response;
2103         
2104         if (extra_msg == NULL) extra_msg = "";
2105         // Toss up a warning dialog
2106         dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
2107                                 GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE,
2108                                 "%sYour movie will be lost if you don't continue encoding.",
2109                                 extra_msg);
2110         gtk_dialog_add_buttons( GTK_DIALOG(dialog), 
2111                                                    "Cancel Current and Stop", 1,
2112                                                    "Cancel Current, Start Next", 2,
2113                                                    "Finish Current, then Stop", 3,
2114                                                    "Continue Encoding", 4,
2115                                                    NULL);
2116         response = gtk_dialog_run(GTK_DIALOG(dialog));
2117         gtk_widget_destroy (dialog);
2118         switch (response)
2119         {
2120                 case 1:
2121                         ghb_stop_queue();
2122                         ud->cancel_encode = GHB_CANCEL_ALL;
2123                         break;
2124                 case 2:
2125                         ghb_stop_queue();
2126                         ud->cancel_encode = GHB_CANCEL_CURRENT;
2127                         break;
2128                 case 3:
2129                         ud->cancel_encode = GHB_CANCEL_FINISH;
2130                         break;
2131                 case 4:
2132                 default:
2133                         ud->cancel_encode = GHB_CANCEL_NONE;
2134                         break;
2135         }
2136 }
2137
2138 gboolean
2139 ghb_cancel_encode2(signal_user_data_t *ud, const gchar *extra_msg)
2140 {
2141         GtkWidget *dialog;
2142         GtkResponseType response;
2143         
2144         if (extra_msg == NULL) extra_msg = "";
2145         // Toss up a warning dialog
2146         dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL,
2147                                 GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE,
2148                                 "%sYour movie will be lost if you don't continue encoding.",
2149                                 extra_msg);
2150         gtk_dialog_add_buttons( GTK_DIALOG(dialog), 
2151                                                    "Cancel Current and Stop", 1,
2152                                                    "Continue Encoding", 4,
2153                                                    NULL);
2154         response = gtk_dialog_run(GTK_DIALOG(dialog));
2155         gtk_widget_destroy (dialog);
2156         switch (response)
2157         {
2158                 case 1:
2159                         ghb_stop_queue();
2160                         ud->cancel_encode = GHB_CANCEL_ALL;
2161                         return TRUE;
2162                 case 4:
2163                 default:
2164                         break;
2165         }
2166         return FALSE;
2167 }
2168
2169 static void
2170 submit_job(GValue *settings)
2171 {
2172         static gint unique_id = 1;
2173         gchar *type, *modified, *preset;
2174         const GValue *path;
2175         gboolean preset_modified;
2176
2177         g_debug("submit_job");
2178         if (settings == NULL) return;
2179         preset_modified = ghb_settings_get_boolean(settings, "preset_modified");
2180         path = ghb_settings_get_value(settings, "preset");
2181         preset = ghb_preset_path_string(path);
2182         type = ghb_preset_is_custom() ? "Custom " : "";
2183         modified = preset_modified ? "Modified " : "";
2184         ghb_log("%s%sPreset: %s", modified, type, preset);
2185         g_free(preset);
2186
2187         ghb_settings_set_int(settings, "job_unique_id", unique_id);
2188         ghb_settings_set_int(settings, "job_status", GHB_QUEUE_RUNNING);
2189         ghb_add_job (settings, unique_id);
2190         ghb_start_queue();
2191         unique_id++;
2192 }
2193
2194 static void
2195 prune_logs(signal_user_data_t *ud)
2196 {
2197         gchar *dest_dir;
2198         gint days;
2199
2200         // Only prune logs stored in the default config dir location
2201         days = ghb_settings_combo_int(ud->settings, "LogLongevity");
2202         if (days > 365)
2203                 return;
2204
2205         dest_dir = ghb_get_user_config_dir("EncodeLogs");
2206         if (g_file_test(dest_dir, G_FILE_TEST_IS_DIR))
2207         {
2208                 const gchar *file;
2209                 gint duration = days * 24 * 60 * 60;
2210                 
2211                 GDir *gdir = g_dir_open(dest_dir, 0, NULL);
2212                 time_t now;
2213
2214                 now = time(NULL);
2215                 file = g_dir_read_name(gdir);
2216                 while (file)
2217                 {
2218                         gchar *path;
2219                         struct stat stbuf;
2220
2221                         path = g_strdup_printf("%s/%s", dest_dir, file);
2222                         g_stat(path, &stbuf);
2223                         if (now - stbuf.st_mtime > duration)
2224                         {
2225                                 g_unlink(path);
2226                         }
2227                         g_free(path);
2228                         file = g_dir_read_name(gdir);
2229                 }
2230                 g_dir_close(gdir);
2231         }
2232         g_free(dest_dir);
2233         ghb_preview_cleanup(ud);
2234 }
2235
2236 static void
2237 queue_scan(signal_user_data_t *ud, GValue *js)
2238 {
2239         gchar *path;
2240         gint titlenum;
2241         time_t  _now;
2242         struct tm *now;
2243         gchar *log_path, *pos, *destname, *basename, *dest_dir;
2244
2245         _now = time(NULL);
2246         now = localtime(&_now);
2247         destname = ghb_settings_get_string(js, "destination");
2248         basename = g_path_get_basename(destname);
2249         if (ghb_settings_get_boolean(ud->settings, "EncodeLogLocation"))
2250         {
2251                 dest_dir = g_path_get_dirname (destname);
2252         }
2253         else
2254         {
2255                 dest_dir = ghb_get_user_config_dir("EncodeLogs");
2256         }
2257         g_free(destname);
2258         pos = g_strrstr( basename, "." );
2259         if (pos != NULL)
2260         {
2261                 *pos = 0;
2262         }
2263         log_path = g_strdup_printf("%s/%s %d-%02d-%02d %02d-%02d-%02d.log",
2264                 dest_dir,
2265                 basename,
2266                 now->tm_year + 1900, now->tm_mon + 1, now->tm_mday,
2267                 now->tm_hour, now->tm_min, now->tm_sec);
2268         g_free(basename);
2269         g_free(dest_dir);
2270         if (ud->job_activity_log)
2271                 g_io_channel_unref(ud->job_activity_log);
2272         ud->job_activity_log = g_io_channel_new_file (log_path, "w", NULL);
2273         if (ud->job_activity_log)
2274         {
2275                 gchar *ver_str;
2276
2277                 ver_str = g_strdup_printf("Handbrake Version: %s (%d)\n", 
2278                                                                         hb_get_version(NULL), hb_get_build(NULL));
2279                 g_io_channel_write_chars (ud->job_activity_log, ver_str, 
2280                                                                         -1, NULL, NULL);
2281                 g_free(ver_str);
2282         }
2283         g_free(log_path);
2284
2285         path = ghb_settings_get_string( js, "source");
2286         titlenum = ghb_settings_get_int(js, "titlenum");
2287         ghb_backend_queue_scan(path, titlenum);
2288         g_free(path);
2289 }
2290
2291 static gint
2292 queue_pending_count(GValue *queue)
2293 {
2294         gint nn, ii, count;
2295         GValue *js;
2296         gint status;
2297
2298         nn = 0;
2299         count = ghb_array_len(queue);
2300         for (ii = 0; ii < count; ii++)
2301         {
2302
2303                 js = ghb_array_get_nth(queue, ii);
2304                 status = ghb_settings_get_int(js, "job_status");
2305                 if (status == GHB_QUEUE_PENDING)
2306                 {
2307                         nn++;
2308                 }
2309         }
2310         return nn;
2311 }
2312
2313 void
2314 ghb_update_pending(signal_user_data_t *ud)
2315 {
2316         GtkLabel *label;
2317         gint pending;
2318         gchar *str;
2319
2320         label = GTK_LABEL(GHB_WIDGET(ud->builder, "pending_status"));
2321         pending = queue_pending_count(ud->queue);
2322         str = g_strdup_printf("%d encode(s) pending", pending);
2323         gtk_label_set_text(label, str);
2324         g_free(str);
2325 }
2326
2327 GValue* 
2328 ghb_start_next_job(signal_user_data_t *ud, gboolean find_first)
2329 {
2330         static gint current = 0;
2331         gint count, ii, jj;
2332         GValue *js;
2333         gint status;
2334         GtkWidget *prog;
2335
2336         g_debug("start_next_job");
2337         prog = GHB_WIDGET(ud->builder, "progressbar");
2338         gtk_widget_show(prog);
2339
2340         count = ghb_array_len(ud->queue);
2341         if (find_first)
2342         {       // Start the first pending item in the queue
2343                 current = 0;
2344                 for (ii = 0; ii < count; ii++)
2345                 {
2346
2347                         js = ghb_array_get_nth(ud->queue, ii);
2348                         status = ghb_settings_get_int(js, "job_status");
2349                         if (status == GHB_QUEUE_PENDING)
2350                         {
2351                                 current = ii;
2352                                 ghb_inhibit_gsm(ud);
2353                                 queue_scan(ud, js);
2354                                 ghb_update_pending(ud);
2355                                 return js;
2356                         }
2357                 }
2358                 // Nothing pending
2359                 ghb_uninhibit_gsm();
2360                 ghb_notify_done(ud);
2361                 return NULL;
2362         }
2363         // Find the next pending item after the current running item
2364         for (ii = 0; ii < count-1; ii++)
2365         {
2366                 js = ghb_array_get_nth(ud->queue, ii);
2367                 status = ghb_settings_get_int(js, "job_status");
2368                 if (status == GHB_QUEUE_RUNNING)
2369                 {
2370                         for (jj = ii+1; jj < count; jj++)
2371                         {
2372                                 js = ghb_array_get_nth(ud->queue, jj);
2373                                 status = ghb_settings_get_int(js, "job_status");
2374                                 if (status == GHB_QUEUE_PENDING)
2375                                 {
2376                                         current = jj;
2377                                         ghb_inhibit_gsm(ud);
2378                                         queue_scan(ud, js);
2379                                         ghb_update_pending(ud);
2380                                         return js;
2381                                 }
2382                         }
2383                 }
2384         }
2385         // No running item found? Maybe it was deleted
2386         // Look for a pending item starting from the last index we started
2387         for (ii = current; ii < count; ii++)
2388         {
2389                 js = ghb_array_get_nth(ud->queue, ii);
2390                 status = ghb_settings_get_int(js, "job_status");
2391                 if (status == GHB_QUEUE_PENDING)
2392                 {
2393                         current = ii;
2394                         ghb_inhibit_gsm(ud);
2395                         queue_scan(ud, js);
2396                         ghb_update_pending(ud);
2397                         return js;
2398                 }
2399         }
2400         // Nothing found
2401         ghb_uninhibit_gsm();
2402         ghb_notify_done(ud);
2403         ghb_update_pending(ud);
2404         gtk_widget_hide(prog);
2405         return NULL;
2406 }
2407
2408 static gint
2409 find_queue_job(GValue *queue, gint unique_id, GValue **job)
2410 {
2411         GValue *js;
2412         gint ii, count;
2413         gint job_unique_id;
2414         
2415         *job = NULL;
2416         g_debug("find_queue_job");
2417         if (unique_id == 0)  // Invalid Id
2418                 return -1;
2419
2420         count = ghb_array_len(queue);
2421         for (ii = 0; ii < count; ii++)
2422         {
2423                 js = ghb_array_get_nth(queue, ii);
2424                 job_unique_id = ghb_settings_get_int(js, "job_unique_id");
2425                 if (job_unique_id == unique_id)
2426                 {
2427                         *job = js;
2428                         return ii;
2429                 }
2430         }
2431         return -1;
2432 }
2433
2434 gchar*
2435 working_status_string(signal_user_data_t *ud, ghb_instance_status_t *status)
2436 {
2437         gchar *task_str, *job_str, *status_str;
2438         gint qcount;
2439         gint index;
2440         GValue *js;
2441         gboolean subtitle_scan = FALSE;
2442
2443         qcount = ghb_array_len(ud->queue);
2444         index = find_queue_job(ud->queue, status->unique_id, &js);
2445         if (js != NULL)
2446         {
2447                 subtitle_scan = ghb_settings_get_boolean(js, "subtitle_scan");
2448         }
2449         if (qcount > 1)
2450         {
2451                 job_str = g_strdup_printf("job %d of %d, ", index+1, qcount);
2452         }
2453         else
2454         {
2455                 job_str = g_strdup("");
2456         }
2457         if (status->job_count > 1)
2458         {
2459                 if (status->job_cur == 1 && subtitle_scan)
2460                 {
2461                         task_str = g_strdup_printf("pass %d (subtitle scan) of %d, ", 
2462                                 status->job_cur, status->job_count);
2463                 }
2464                 else
2465                 {
2466                         task_str = g_strdup_printf("pass %d of %d, ", 
2467                                 status->job_cur, status->job_count);
2468                 }
2469         }
2470         else
2471         {
2472                 task_str = g_strdup("");
2473         }
2474         if(status->seconds > -1)
2475         {
2476                 status_str= g_strdup_printf(
2477                         "Encoding: %s%s%.2f %%"
2478                         " (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)",
2479                         job_str, task_str,
2480                         100.0 * status->progress,
2481                         status->rate_cur, status->rate_avg, status->hours, 
2482                         status->minutes, status->seconds );
2483         }
2484         else
2485         {
2486                 status_str= g_strdup_printf(
2487                         "Encoding: %s%s%.2f %%",
2488                         job_str, task_str,
2489                         100.0 * status->progress );
2490         }
2491         g_free(task_str);
2492         g_free(job_str);
2493         return status_str;
2494 }
2495
2496 gchar*
2497 searching_status_string(signal_user_data_t *ud, ghb_instance_status_t *status)
2498 {
2499         gchar *task_str, *job_str, *status_str;
2500         gint qcount;
2501         gint index;
2502         GValue *js;
2503
2504         qcount = ghb_array_len(ud->queue);
2505         index = find_queue_job(ud->queue, status->unique_id, &js);
2506         if (qcount > 1)
2507         {
2508                 job_str = g_strdup_printf("job %d of %d, ", index+1, qcount);
2509         }
2510         else
2511         {
2512                 job_str = g_strdup("");
2513         }
2514         task_str = g_strdup_printf("Searching for start time, ");
2515         if(status->seconds > -1)
2516         {
2517                 status_str= g_strdup_printf(
2518                         "Encoding: %s%s%.2f %%"
2519                         " (ETA %02dh%02dm%02ds)",
2520                         job_str, task_str,
2521                         100.0 * status->progress,
2522                         status->hours, status->minutes, status->seconds );
2523         }
2524         else
2525         {
2526                 status_str= g_strdup_printf(
2527                         "Encoding: %s%s%.2f %%",
2528                         job_str, task_str,
2529                         100.0 * status->progress );
2530         }
2531         g_free(task_str);
2532         g_free(job_str);
2533         return status_str;
2534 }
2535
2536 static void
2537 ghb_backend_events(signal_user_data_t *ud)
2538 {
2539         ghb_status_t status;
2540         gchar *status_str;
2541         GtkProgressBar *progress;
2542         GtkLabel       *work_status;
2543         gint titleindex;
2544         GValue *js;
2545         gint index;
2546         GtkTreeView *treeview;
2547         GtkTreeStore *store;
2548         GtkTreeIter iter;
2549         static gint prev_scan_state = 0;
2550         static gint prev_queue_state = 0;
2551         
2552         ghb_track_status();
2553         ghb_get_status(&status);
2554         if (prev_scan_state != status.scan.state ||
2555                 prev_queue_state != status.queue.state)
2556         {
2557                 ghb_queue_buttons_grey(ud);
2558                 prev_scan_state = status.scan.state;
2559                 prev_queue_state = status.queue.state;
2560         }
2561         progress = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "progressbar"));
2562         work_status = GTK_LABEL(GHB_WIDGET (ud->builder, "work_status"));
2563         if (status.scan.state == GHB_STATE_IDLE && 
2564                 status.queue.state == GHB_STATE_IDLE)
2565         {
2566                 static gboolean prev_dvdnav;
2567                 gboolean dvdnav = ghb_settings_get_boolean(ud->settings, "use_dvdnav");
2568                 if (dvdnav != prev_dvdnav)
2569                 {
2570                         hb_dvd_set_dvdnav(dvdnav);
2571                         prev_dvdnav = dvdnav;
2572                 }
2573         }
2574         // First handle the status of title scans
2575         // Then handle the status of the queue
2576         if (status.scan.state & GHB_STATE_SCANNING)
2577         {
2578                 GtkProgressBar *scan_prog;
2579                 GtkLabel *label;
2580
2581                 scan_prog = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "scan_prog"));
2582                 label = GTK_LABEL(GHB_WIDGET (ud->builder, "source_title"));
2583
2584                 if (status.scan.title_cur == 0)
2585                 {
2586                         status_str = g_strdup ("Scanning...");
2587                 }
2588                 else
2589                 {
2590                         status_str = g_strdup_printf ("Scanning title %d of %d...", 
2591                                                           status.scan.title_cur, status.scan.title_count );
2592                 }
2593                 gtk_label_set_text (label, status_str);
2594                 g_free(status_str);
2595                 if (status.scan.title_count > 0)
2596                 {
2597                         gtk_progress_bar_set_fraction (scan_prog, 
2598                                 (gdouble)status.scan.title_cur / status.scan.title_count);
2599                 }
2600         }
2601         else if (status.scan.state & GHB_STATE_SCANDONE)
2602         {
2603                 gchar *source;
2604                 GtkProgressBar *scan_prog;
2605                 GtkLabel *label;
2606
2607                 GtkWidget *widget;
2608                 GtkAction *action;
2609
2610                 widget = GHB_WIDGET(ud->builder, "sourcetoolbutton");
2611                 gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(widget), "hb-source");
2612                 gtk_tool_button_set_label(GTK_TOOL_BUTTON(widget), "Source");
2613                 gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(widget), "Choose Video Source");
2614
2615                 action = GHB_ACTION(ud->builder, "source_action");
2616                 gtk_action_set_sensitive(action, TRUE);
2617                 action = GHB_ACTION(ud->builder, "source_single_action");
2618                 gtk_action_set_sensitive(action, TRUE);
2619
2620                 source = ghb_settings_get_string(ud->settings, "scan_source");
2621                 update_source_label(ud, source, FALSE);
2622
2623                 scan_prog = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "scan_prog"));
2624                 gtk_progress_bar_set_fraction (scan_prog, 1.0);
2625                 gtk_widget_hide(GTK_WIDGET(scan_prog));
2626
2627                 if (!ghb_settings_get_boolean(ud->settings, "preset_modified"))
2628                 {
2629                         ghb_refresh_preset(ud);
2630                 }
2631
2632                 ghb_title_info_t tinfo;
2633                         
2634                 ghb_update_ui_combo_box(ud, "title", 0, FALSE);
2635                 titleindex = ghb_longest_title();
2636                 ghb_ui_update(ud, "title", ghb_int64_value(titleindex));
2637
2638                 label = GTK_LABEL(GHB_WIDGET (ud->builder, "source_title"));
2639                 // Are there really any titles.
2640                 if (!ghb_get_title_info(&tinfo, titleindex))
2641                 {
2642                         gtk_label_set_text(label, "None");
2643                 }
2644                 ghb_clear_scan_state(GHB_STATE_SCANDONE);
2645                 if (ghb_queue_edit_settings)
2646                 {
2647                         ghb_settings_to_ui(ud, ghb_queue_edit_settings);
2648                         ghb_set_audio(ud, ghb_queue_edit_settings);
2649                         ghb_reset_subtitles(ud, ghb_queue_edit_settings);
2650                         reset_chapter_list(ud, ghb_queue_edit_settings);
2651                         ghb_value_free(ghb_queue_edit_settings);
2652                         ghb_queue_edit_settings = NULL;
2653                 }
2654         }
2655
2656         if (status.queue.state & GHB_STATE_SCANNING)
2657         {
2658                 // This needs to be in scanning and working since scanning
2659                 // happens fast enough that it can be missed
2660                 gtk_label_set_text (work_status, "Scanning ...");
2661                 gtk_progress_bar_set_fraction (progress, 0);
2662         }
2663         else if (status.queue.state & GHB_STATE_SCANDONE)
2664         {
2665                 ghb_clear_queue_state(GHB_STATE_SCANDONE);
2666                 usleep(2000000);
2667                 submit_job(ud->current_job);
2668                 ghb_update_pending(ud);
2669         }
2670         else if (status.queue.state & GHB_STATE_PAUSED)
2671         {
2672                 gtk_label_set_text (work_status, "Paused");
2673         }
2674         else if (status.queue.state & GHB_STATE_SEARCHING)
2675         {
2676                 static gint working = 0;
2677
2678                 // This needs to be in scanning and working since scanning
2679                 // happens fast enough that it can be missed
2680                 index = find_queue_job(ud->queue, status.queue.unique_id, &js);
2681                 if (status.queue.unique_id != 0 && index >= 0)
2682                 {
2683                         gchar working_icon[] = "hb-working0";
2684                         working_icon[10] = '0' + working;
2685                         working = (working+1) % 6;
2686                         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
2687                         store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
2688                         gchar *path = g_strdup_printf ("%d", index);
2689                         if (gtk_tree_model_get_iter_from_string(
2690                                         GTK_TREE_MODEL(store), &iter, path))
2691                         {
2692                                 gtk_tree_store_set(store, &iter, 0, working_icon, -1);
2693                         }
2694                         g_free(path);
2695                 }
2696                 GtkLabel *label;
2697                 gchar *status_str;
2698
2699                 status_str = searching_status_string(ud, &status.queue);
2700                 label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_status"));
2701                 gtk_label_set_text (label, status_str);
2702 #if !GTK_CHECK_VERSION(2, 16, 0)
2703                 GtkStatusIcon *si;
2704
2705                 si = GTK_STATUS_ICON(GHB_OBJECT(ud->builder, "hb_status"));
2706                 gtk_status_icon_set_tooltip(si, status_str);
2707 #endif
2708                 gtk_label_set_text (work_status, status_str);
2709                 gtk_progress_bar_set_fraction (progress, status.queue.progress);
2710                 g_free(status_str);
2711         }
2712         else if (status.queue.state & GHB_STATE_WORKING)
2713         {
2714                 static gint working = 0;
2715
2716                 // This needs to be in scanning and working since scanning
2717                 // happens fast enough that it can be missed
2718                 index = find_queue_job(ud->queue, status.queue.unique_id, &js);
2719                 if (status.queue.unique_id != 0 && index >= 0)
2720                 {
2721                         gchar working_icon[] = "hb-working0";
2722                         working_icon[10] = '0' + working;
2723                         working = (working+1) % 6;
2724                         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
2725                         store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
2726                         gchar *path = g_strdup_printf ("%d", index);
2727                         if (gtk_tree_model_get_iter_from_string(
2728                                         GTK_TREE_MODEL(store), &iter, path))
2729                         {
2730                                 gtk_tree_store_set(store, &iter, 0, working_icon, -1);
2731                         }
2732                         g_free(path);
2733                 }
2734                 GtkLabel *label;
2735                 gchar *status_str;
2736
2737                 status_str = working_status_string(ud, &status.queue);
2738                 label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_status"));
2739                 gtk_label_set_text (label, status_str);
2740 #if !GTK_CHECK_VERSION(2, 16, 0)
2741                 GtkStatusIcon *si;
2742
2743                 si = GTK_STATUS_ICON(GHB_OBJECT(ud->builder, "hb_status"));
2744                 gtk_status_icon_set_tooltip(si, status_str);
2745 #endif
2746                 gtk_label_set_text (work_status, status_str);
2747                 gtk_progress_bar_set_fraction (progress, status.queue.progress);
2748                 g_free(status_str);
2749         }
2750         else if (status.queue.state & GHB_STATE_WORKDONE)
2751         {
2752                 gint qstatus;
2753
2754                 index = find_queue_job(ud->queue, status.queue.unique_id, &js);
2755                 treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "queue_list"));
2756                 store = GTK_TREE_STORE(gtk_tree_view_get_model(treeview));
2757                 if (ud->cancel_encode == GHB_CANCEL_ALL || 
2758                         ud->cancel_encode == GHB_CANCEL_CURRENT)
2759                         status.queue.error = GHB_ERROR_CANCELED;
2760                 switch( status.queue.error )
2761                 {
2762                         case GHB_ERROR_NONE:
2763                                 gtk_label_set_text (work_status, "Rip Done!");
2764                                 qstatus = GHB_QUEUE_DONE;
2765                                 if (js != NULL)
2766                                 {
2767                                         gchar *path = g_strdup_printf ("%d", index);
2768                                         if (gtk_tree_model_get_iter_from_string(
2769                                                         GTK_TREE_MODEL(store), &iter, path))
2770                                         {
2771                                                 gtk_tree_store_set(store, &iter, 0, "hb-complete", -1);
2772                                         }
2773                                         g_free(path);
2774                                 }
2775                                 break;
2776                         case GHB_ERROR_CANCELED:
2777                                 gtk_label_set_text (work_status, "Rip Canceled.");
2778                                 qstatus = GHB_QUEUE_CANCELED;
2779                                 if (js != NULL)
2780                                 {
2781                                         gchar *path = g_strdup_printf ("%d", index);
2782                                         if (gtk_tree_model_get_iter_from_string(
2783                                                         GTK_TREE_MODEL(store), &iter, path))
2784                                         {
2785                                                 gtk_tree_store_set(store, &iter, 0, "hb-canceled", -1);
2786                                         }
2787                                         g_free(path);
2788                                 }
2789                                 break;
2790                         default:
2791                                 gtk_label_set_text (work_status, "Rip Failed.");
2792                                 qstatus = GHB_QUEUE_CANCELED;
2793                                 if (js != NULL)
2794                                 {
2795                                         gchar *path = g_strdup_printf ("%d", index);
2796                                         if (gtk_tree_model_get_iter_from_string(
2797                                                         GTK_TREE_MODEL(store), &iter, path))
2798                                         {
2799                                                 gtk_tree_store_set(store, &iter, 0, "hb-canceled", -1);
2800                                         }
2801                                         g_free(path);
2802                                 }
2803                 }
2804                 gtk_progress_bar_set_fraction (progress, 1.0);
2805                 ghb_clear_queue_state(GHB_STATE_WORKDONE);
2806                 if (ud->job_activity_log)
2807                         g_io_channel_unref(ud->job_activity_log);
2808                 ud->job_activity_log = NULL;
2809                 if (ud->cancel_encode != GHB_CANCEL_ALL &&
2810                         ud->cancel_encode != GHB_CANCEL_FINISH)
2811                 {
2812                         ud->current_job = ghb_start_next_job(ud, FALSE);
2813                 }
2814                 else
2815                 {
2816                         ghb_uninhibit_gsm();
2817                         ud->current_job = NULL;
2818                         gtk_widget_hide(GTK_WIDGET(progress));
2819                 }
2820                 if (js)
2821                         ghb_settings_set_int(js, "job_status", qstatus);
2822                 ghb_save_queue(ud->queue);
2823                 ud->cancel_encode = GHB_CANCEL_NONE;
2824 #if !GTK_CHECK_VERSION(2, 16, 0)
2825                 GtkStatusIcon *si;
2826
2827                 si = GTK_STATUS_ICON(GHB_OBJECT(ud->builder, "hb_status"));
2828                 gtk_status_icon_set_tooltip(si, "HandBrake");
2829 #endif
2830         }
2831         else if (status.queue.state & GHB_STATE_MUXING)
2832         {
2833                 gtk_label_set_text (work_status, "Muxing: This may take a while...");
2834         }
2835
2836         if (status.scan.state & GHB_STATE_WORKING)
2837         {
2838                 GtkProgressBar *live_progress;
2839                 live_progress = GTK_PROGRESS_BAR(
2840                         GHB_WIDGET(ud->builder, "live_encode_progress"));
2841                 status_str = working_status_string(ud, &status.scan);
2842                 gtk_progress_bar_set_text (live_progress, status_str);
2843                 gtk_progress_bar_set_fraction (live_progress, status.scan.progress);
2844                 g_free(status_str);
2845         }
2846         if (status.scan.state & GHB_STATE_WORKDONE)
2847         {
2848                 switch( status.scan.error )
2849                 {
2850                         case GHB_ERROR_NONE:
2851                         {
2852                                 ghb_live_encode_done(ud, TRUE);
2853                         } break;
2854                         default:
2855                         {
2856                                 ghb_live_encode_done(ud, FALSE);
2857                         } break;
2858                 }
2859                 ghb_clear_scan_state(GHB_STATE_WORKDONE);
2860         }
2861 }
2862
2863 #if GTK_CHECK_VERSION(2, 16, 0)
2864 G_MODULE_EXPORT gboolean
2865 status_icon_query_tooltip_cb(
2866         GtkStatusIcon *si,
2867         gint           x,
2868         gint           y,
2869         gboolean       kbd_mode,
2870         GtkTooltip    *tt,
2871         signal_user_data_t *ud)
2872 {
2873         ghb_status_t status;
2874         gchar *status_str;
2875
2876         ghb_get_status(&status);
2877         if (status.queue.state & GHB_STATE_WORKING)
2878                 status_str = working_status_string(ud, &status.queue);
2879         else if (status.queue.state & GHB_STATE_SEARCHING)
2880                 status_str = searching_status_string(ud, &status.queue);
2881         else if (status.queue.state & GHB_STATE_WORKDONE)
2882                 status_str = g_strdup("Encode Complete");
2883         else
2884                 status_str = g_strdup("HandBrake");
2885
2886         gtk_tooltip_set_text(tt, status_str);
2887         gtk_tooltip_set_icon_from_icon_name(tt, "hb-icon", GTK_ICON_SIZE_BUTTON);
2888         g_free(status_str);
2889         return TRUE;
2890 }
2891 #endif
2892
2893 G_MODULE_EXPORT gboolean
2894 ghb_timer_cb(gpointer data)
2895 {
2896         signal_user_data_t *ud = (signal_user_data_t*)data;
2897
2898         ghb_live_preview_progress(ud);
2899         ghb_backend_events(ud);
2900         if (update_default_destination)
2901         {
2902                 gchar *dest, *dest_dir, *def_dest;
2903                 dest = ghb_settings_get_string(ud->settings, "destination");
2904                 dest_dir = g_path_get_dirname (dest);
2905                 def_dest = ghb_settings_get_string(ud->settings, "destination_dir");
2906                 if (strcmp(dest_dir, def_dest) != 0)
2907                 {
2908                         ghb_settings_set_string (ud->settings, "destination_dir", dest_dir);
2909                         ghb_pref_save (ud->settings, "destination_dir");
2910                 }
2911                 g_free(dest);
2912                 g_free(dest_dir);
2913                 g_free(def_dest);
2914                 update_default_destination = FALSE;
2915         }
2916         if (update_preview)
2917         {
2918                 g_debug("Updating preview\n");
2919                 ghb_set_preview_image (ud);
2920                 update_preview = FALSE;
2921         }
2922
2923 #if !defined(_NO_UPDATE_CHECK)
2924         if (!appcast_busy)
2925         {
2926                 gchar *updates;
2927                 updates = ghb_settings_get_string(ud->settings, "check_updates");
2928                 gint64 duration = 0;
2929                 if (strcmp(updates, "daily") == 0)
2930                         duration = 60 * 60 * 24;
2931                 else if (strcmp(updates, "weekly") == 0)
2932                         duration = 60 * 60 * 24 * 7;
2933                 else if (strcmp(updates, "monthly") == 0)
2934                         duration = 60 * 60 * 24 * 7;
2935
2936                 g_free(updates);
2937                 if (duration != 0)
2938                 {
2939                         gint64 last;
2940                         time_t tt;
2941
2942                         last = ghb_settings_get_int64(ud->settings, "last_update_check");
2943                         time(&tt);
2944                         if (last + duration < tt)
2945                         {
2946                                 ghb_settings_set_int64(ud->settings, 
2947                                                                                 "last_update_check", tt);
2948                                 ghb_pref_save(ud->settings, "last_update_check");
2949                                 g_thread_create((GThreadFunc)ghb_check_update, ud, 
2950                                                                 FALSE, NULL);
2951                         }
2952                 }
2953         }
2954 #endif
2955         return TRUE;
2956 }
2957
2958 G_MODULE_EXPORT gboolean
2959 ghb_log_cb(GIOChannel *source, GIOCondition cond, gpointer data)
2960 {
2961         gchar *text = NULL;
2962         gsize length, outlength;
2963         GtkTextView *textview;
2964         GtkTextBuffer *buffer;
2965         GtkTextIter iter;
2966         GtkTextMark *mark;
2967         GError *gerror = NULL;
2968         GIOStatus status;
2969         
2970         signal_user_data_t *ud = (signal_user_data_t*)data;
2971
2972         status = g_io_channel_read_line (source, &text, &length, NULL, &gerror);
2973         // Trim nils from end of text, they cause g_io_channel_write_chars to
2974         // fail with an assertion that aborts
2975         while (length > 0 && text[length-1] == 0)
2976                 length--;
2977         if (text != NULL && length > 0)
2978         {
2979                 GdkWindow *window;
2980                 gint width, height;
2981                 gint x, y;
2982                 gboolean bottom = FALSE;
2983                 gchar *utf8_text;
2984
2985                 textview = GTK_TEXT_VIEW(GHB_WIDGET (ud->builder, "activity_view"));
2986                 buffer = gtk_text_view_get_buffer (textview);
2987                 // I would like to auto-scroll the window when the scrollbar
2988                 // is at the bottom, 
2989                 // must determine whether the insert point is at
2990                 // the bottom of the window 
2991                 window = gtk_text_view_get_window(textview, GTK_TEXT_WINDOW_TEXT);
2992                 if (window != NULL)
2993                 {
2994                         gdk_drawable_get_size(GDK_DRAWABLE(window), &width, &height);
2995                         gtk_text_view_window_to_buffer_coords(textview, 
2996                                 GTK_TEXT_WINDOW_TEXT, width, height, &x, &y);
2997                         gtk_text_view_get_iter_at_location(textview, &iter, x, y);
2998                         if (gtk_text_iter_is_end(&iter))
2999                         {
3000                                 bottom = TRUE;
3001                         }
3002                 }
3003                 else
3004                 {
3005                         // If the window isn't available, assume bottom
3006                         bottom = TRUE;
3007                 }
3008                 gtk_text_buffer_get_end_iter(buffer, &iter);
3009                 utf8_text = g_convert_with_fallback(text, -1, "UTF-8", "ISO-8859-1",
3010                                                                                         "?", NULL, &length, NULL);
3011                 if (utf8_text != NULL)
3012                 {
3013                         gtk_text_buffer_insert(buffer, &iter, utf8_text, -1);
3014                         if (bottom)
3015                         {
3016                                 gtk_text_buffer_get_end_iter(buffer, &iter);
3017                                 mark = gtk_text_buffer_create_mark(buffer, NULL, &iter, FALSE);
3018                                 gtk_text_view_scroll_mark_onscreen(textview, mark);
3019                                 gtk_text_buffer_delete_mark(buffer, mark);
3020                         }
3021 #if defined(_WIN32)
3022                         gsize one = 1;
3023                         utf8_text[length-1] = '\r';
3024 #endif
3025                         g_io_channel_write_chars (ud->activity_log, utf8_text, 
3026                                                                         length, &outlength, NULL);
3027 #if defined(_WIN32)
3028                         g_io_channel_write_chars (ud->activity_log, "\n", 
3029                                                                         one, &one, NULL);
3030 #endif
3031                         g_io_channel_flush(ud->activity_log, NULL);
3032                         if (ud->job_activity_log)
3033                         {
3034                                 g_io_channel_write_chars (ud->job_activity_log, utf8_text, 
3035                                                                                 length, &outlength, NULL);
3036 #if defined(_WIN32)
3037                                 g_io_channel_write_chars (ud->activity_log, "\n", 
3038                                                                                 one, &outlength, NULL);
3039 #endif
3040                                 g_io_channel_flush(ud->job_activity_log, NULL);
3041                         }
3042                         g_free(utf8_text);
3043                 }
3044         }
3045         if (text != NULL)
3046                 g_free(text);
3047
3048         if (status != G_IO_STATUS_NORMAL)
3049         {
3050                 // This should never happen, but if it does I would get into an
3051                 // infinite loop.  Returning false removes this callback.
3052                 g_warning("Error while reading activity from pipe");
3053                 if (gerror != NULL)
3054                 {
3055                         g_warning("%s", gerror->message);
3056                         g_error_free (gerror);
3057                 }
3058                 return FALSE;
3059         }
3060         if (gerror != NULL)
3061                 g_error_free (gerror);
3062         return TRUE;
3063 }
3064
3065 static void
3066 set_visible(GtkWidget *widget, gboolean visible)
3067 {
3068         if (visible)
3069         {
3070                 gtk_widget_show_now(widget);
3071         }
3072         else
3073         {
3074                 gtk_widget_hide(widget);
3075         }
3076 }
3077
3078 G_MODULE_EXPORT void
3079 show_activity_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
3080 {
3081         GtkWidget *widget = GHB_WIDGET (ud->builder, "activity_window");
3082         set_visible(widget, gtk_toggle_tool_button_get_active(
3083                                                 GTK_TOGGLE_TOOL_BUTTON(xwidget)));
3084 }
3085
3086 G_MODULE_EXPORT void
3087 show_activity_menu_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
3088 {
3089         GtkWidget *widget = GHB_WIDGET (ud->builder, "activity_window");
3090         set_visible(widget, TRUE);
3091         widget = GHB_WIDGET (ud->builder, "show_activity");
3092         gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), TRUE);
3093 }
3094
3095 G_MODULE_EXPORT gboolean
3096 activity_window_delete_cb(GtkWidget *xwidget, GdkEvent *event, signal_user_data_t *ud)
3097 {
3098         set_visible(xwidget, FALSE);
3099         GtkWidget *widget = GHB_WIDGET (ud->builder, "show_activity");
3100         gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), FALSE);
3101         return TRUE;
3102 }
3103
3104 void
3105 ghb_log(gchar *log, ...)
3106 {
3107         va_list args;
3108         time_t _now;
3109     struct tm *now;
3110         gchar fmt[362];
3111
3112         _now = time(NULL);
3113         now = localtime( &_now );
3114         snprintf(fmt, 362, "[%02d:%02d:%02d] gtkgui: %s\n", 
3115                         now->tm_hour, now->tm_min, now->tm_sec, log);
3116         va_start(args, log);
3117         vfprintf(stderr, fmt, args);
3118         va_end(args);
3119 }
3120
3121 static void
3122 browse_url(const gchar *url)
3123 {
3124 #if defined(_WIN32)
3125         HINSTANCE r;
3126         r = ShellExecute(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL);
3127 #else
3128         gboolean result;
3129         char *argv[] = 
3130                 {"xdg-open",NULL,NULL,NULL};
3131         argv[1] = (gchar*)url;
3132         result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
3133                                 NULL, NULL, NULL);
3134         if (result) return;
3135
3136         argv[0] = "gnome-open";
3137         result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
3138                                 NULL, NULL, NULL);
3139         if (result) return;
3140
3141         argv[0] = "kfmclient";
3142         argv[1] = "exec";
3143         argv[2] = "http://trac.handbrake.fr/wiki/HandBrakeGuide";
3144         result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
3145                                 NULL, NULL, NULL);
3146         if (result) return;
3147
3148         argv[0] = "firefox";
3149         argv[1] = "http://trac.handbrake.fr/wiki/HandBrakeGuide";
3150         argv[2] = NULL;
3151         result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
3152                                 NULL, NULL, NULL);
3153 #endif
3154 }
3155
3156 void
3157 about_web_hook(GtkAboutDialog *about, const gchar *link, gpointer data)
3158 {
3159         browse_url(link);
3160 }
3161
3162 G_MODULE_EXPORT void
3163 about_activate_cb(GtkWidget *xwidget, signal_user_data_t *ud)
3164 {
3165         GtkWidget *widget = GHB_WIDGET (ud->builder, "hb_about");
3166         gchar *ver;
3167
3168         ver = g_strdup_printf("%s (%s)", HB_PROJECT_VERSION, HB_PROJECT_BUILD_ARCH);
3169         gtk_about_dialog_set_url_hook(about_web_hook, NULL, NULL);
3170         gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(widget), ver);
3171         g_free(ver);
3172         gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(widget), 
3173                                                                 HB_PROJECT_URL_WEBSITE);
3174         gtk_about_dialog_set_website_label(GTK_ABOUT_DIALOG(widget), 
3175                                                                                 HB_PROJECT_URL_WEBSITE);
3176         gtk_widget_show (widget);
3177 }
3178
3179 G_MODULE_EXPORT void
3180 guide_activate_cb(GtkWidget *xwidget, signal_user_data_t *ud)
3181 {
3182         browse_url("http://trac.handbrake.fr/wiki/HandBrakeGuide");
3183 }
3184
3185 G_MODULE_EXPORT void
3186 hb_about_response_cb(GtkWidget *widget, gint response, signal_user_data_t *ud)
3187 {
3188         gtk_widget_hide (widget);
3189 }
3190
3191 G_MODULE_EXPORT void
3192 show_queue_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
3193 {
3194         GtkWidget *widget = GHB_WIDGET (ud->builder, "queue_window");
3195         set_visible(widget, gtk_toggle_tool_button_get_active(
3196                                                 GTK_TOGGLE_TOOL_BUTTON(xwidget)));
3197 }
3198
3199 G_MODULE_EXPORT void
3200 show_queue_menu_clicked_cb(GtkWidget *xwidget, signal_user_data_t *ud)
3201 {
3202         GtkWidget *widget = GHB_WIDGET (ud->builder, "queue_window");
3203         set_visible(widget, TRUE);
3204         widget = GHB_WIDGET (ud->builder, "show_queue");
3205         gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), TRUE);
3206 }
3207
3208 G_MODULE_EXPORT gboolean
3209 queue_window_delete_cb(GtkWidget *xwidget, GdkEvent *event, signal_user_data_t *ud)
3210 {
3211         set_visible(xwidget, FALSE);
3212         GtkWidget *widget = GHB_WIDGET (ud->builder, "show_queue");
3213         gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), FALSE);
3214         return TRUE;
3215 }
3216
3217 G_MODULE_EXPORT void
3218 show_presets_toggled_cb(GtkWidget *action, signal_user_data_t *ud)
3219 {
3220         GtkWidget *widget;
3221         GtkWindow *hb_window;
3222         
3223         g_debug("show_presets_clicked_cb ()");
3224         widget = GHB_WIDGET (ud->builder, "presets_frame");
3225         ghb_widget_to_setting(ud->settings, action);
3226         if (ghb_settings_get_boolean(ud->settings, "show_presets"))
3227         {
3228                 gtk_widget_show_now(widget);
3229         }
3230         else
3231         {
3232                 gtk_widget_hide(widget);
3233                 hb_window = GTK_WINDOW(GHB_WIDGET (ud->builder, "hb_window"));
3234                 gtk_window_resize(hb_window, 16, 16);
3235         }
3236         ghb_pref_save(ud->settings, "show_presets");
3237 }
3238
3239 static void
3240 reset_chapter_list(signal_user_data_t *ud, GValue *settings)
3241 {
3242         GtkTreeView *treeview;
3243         GtkTreeIter iter;
3244         GtkListStore *store;
3245         gboolean done;
3246         GValue *chapters;
3247         gint titleindex, ii;
3248         gint count;
3249         
3250         g_debug("reset_chapter_list ()");
3251         chapters = ghb_value_dup(ghb_settings_get_value(settings, "chapter_list"));
3252         count = ghb_array_len(chapters);
3253         ghb_settings_set_value(ud->settings, "chapter_list", chapters);
3254         titleindex = ghb_settings_combo_int(ud->settings, "title");
3255         
3256         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "chapters_list"));
3257         store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview));
3258         ii = 0;
3259         if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter))
3260         {
3261                 do
3262                 {
3263
3264                         if (ii < count)
3265                         {
3266                                 gchar *chapter, *duration;
3267                                 gint hh, mm, ss;
3268
3269                                 // Update row with settings data
3270                                 g_debug("Updating row");
3271                                 chapter = ghb_value_string(ghb_array_get_nth(chapters, ii));
3272                                 ghb_get_chapter_duration(titleindex, ii, &hh, &mm, &ss);
3273                                 duration = g_strdup_printf("%02d:%02d:%02d", hh, mm, ss);
3274                                 gtk_list_store_set(store, &iter, 
3275                                         0, ii+1,
3276                                         1, duration,
3277                                         2, chapter,
3278                                         3, TRUE,
3279                                         -1);
3280                                 g_free(chapter);
3281                                 g_free(duration);
3282                                 ii++;
3283                                 done = !gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
3284                         }
3285                         else
3286                         {
3287                                 // No more settings data, remove row
3288                                 g_debug("Removing row");
3289                                 done = !gtk_list_store_remove(store, &iter);
3290                         }
3291                 } while (!done);
3292         }
3293         while (ii < count)
3294         {
3295                 gchar *chapter, *duration;
3296                 gint hh, mm, ss;
3297
3298                 // Additional settings, add row
3299                 g_debug("Adding row");
3300                 chapter = ghb_value_string(ghb_array_get_nth(chapters, ii));
3301                 ghb_get_chapter_duration(titleindex, ii, &hh, &mm, &ss);
3302                 duration = g_strdup_printf("%02d:%02d:%02d", hh, mm, ss);
3303                 gtk_list_store_append(store, &iter);
3304                 gtk_list_store_set(store, &iter, 
3305                         0, ii+1,
3306                         1, duration,
3307                         2, chapter,
3308                         3, TRUE,
3309                         -1);
3310                 g_free(chapter);
3311                 g_free(duration);
3312                 ii++;
3313         }
3314 }
3315
3316 static void
3317 update_chapter_list(signal_user_data_t *ud)
3318 {
3319         GtkTreeView *treeview;
3320         GtkTreeIter iter;
3321         GtkListStore *store;
3322         gboolean done;
3323         GValue *chapters;
3324         gint titleindex, ii;
3325         gint count;
3326         
3327         g_debug("update_chapter_list ()");
3328         titleindex = ghb_settings_combo_int(ud->settings, "title");
3329         chapters = ghb_get_chapters(titleindex);
3330         count = ghb_array_len(chapters);
3331         if (chapters)
3332                 ghb_settings_set_value(ud->settings, "chapter_list", chapters);
3333         
3334         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "chapters_list"));
3335         store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview));
3336         ii = 0;
3337         if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter))
3338         {
3339                 do
3340                 {
3341
3342                         if (ii < count)
3343                         {
3344                                 gchar *chapter, *duration;
3345                                 gint hh, mm, ss;
3346
3347                                 // Update row with settings data
3348                                 g_debug("Updating row");
3349                                 chapter = ghb_value_string(ghb_array_get_nth(chapters, ii));
3350                                 ghb_get_chapter_duration(titleindex, ii, &hh, &mm, &ss);
3351                                 duration = g_strdup_printf("%02d:%02d:%02d", hh, mm, ss);
3352                                 gtk_list_store_set(store, &iter, 
3353                                         0, ii+1,
3354                                         1, duration,
3355                                         2, chapter,
3356                                         3, TRUE,
3357                                         -1);
3358                                 g_free(chapter);
3359                                 g_free(duration);
3360                                 ii++;
3361                                 done = !gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter);
3362                         }
3363                         else
3364                         {
3365                                 // No more settings data, remove row
3366                                 g_debug("Removing row");
3367                                 done = !gtk_list_store_remove(store, &iter);
3368                         }
3369                 } while (!done);
3370         }
3371         while (ii < count)
3372         {
3373                 gchar *chapter, *duration;
3374                 gint hh, mm, ss;
3375
3376                 // Additional settings, add row
3377                 g_debug("Adding row");
3378                 chapter = ghb_value_string(ghb_array_get_nth(chapters, ii));
3379                 ghb_get_chapter_duration(titleindex, ii, &hh, &mm, &ss);
3380                 duration = g_strdup_printf("%02d:%02d:%02d", hh, mm, ss);
3381                 gtk_list_store_append(store, &iter);
3382                 gtk_list_store_set(store, &iter, 
3383                         0, ii+1,
3384                         1, duration,
3385                         2, chapter,
3386                         3, TRUE,
3387                         -1);
3388                 g_free(chapter);
3389                 g_free(duration);
3390                 ii++;
3391         }
3392 }
3393
3394 static gint chapter_edit_key = 0;
3395
3396 G_MODULE_EXPORT gboolean
3397 chapter_keypress_cb(
3398         GhbCellRendererText *cell,
3399         GdkEventKey *event,
3400         signal_user_data_t *ud)
3401 {
3402         chapter_edit_key = event->keyval;
3403         return FALSE;
3404 }
3405
3406 G_MODULE_EXPORT void
3407 chapter_edited_cb(
3408         GhbCellRendererText *cell, 
3409         gchar *path, 
3410         gchar *text, 
3411         signal_user_data_t *ud)
3412 {
3413         GtkTreePath *treepath;
3414         GtkListStore *store;
3415         GtkTreeView *treeview;
3416         GtkTreeIter iter;
3417         gint index;
3418         gint *pi;
3419         gint row;
3420         
3421         g_debug("chapter_edited_cb ()");
3422         g_debug("path (%s)", path);
3423         g_debug("text (%s)", text);
3424         treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "chapters_list"));
3425         store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview));
3426         treepath = gtk_tree_path_new_from_string (path);
3427         pi = gtk_tree_path_get_indices(treepath);
3428         row = pi[0];
3429         gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, treepath);
3430         gtk_list_store_set(store, &iter, 
3431                 2, text,
3432                 3, TRUE,
3433                 -1);
3434         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 0, &index, -1);
3435
3436         const GValue *chapters;
3437         GValue *chapter;
3438
3439         chapters = ghb_settings_get_value(ud->settings, "chapter_list");
3440         chapter = ghb_array_get_nth(chapters, index-1);
3441         g_value_set_string(chapter, text);
3442         if ((chapter_edit_key == GDK_Return || chapter_edit_key == GDK_Down) &&
3443                 gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter))
3444         {
3445                 GtkTreeViewColumn *column;
3446
3447                 gtk_tree_path_next(treepath);
3448                 // When a cell has been edited, I want to advance to the
3449                 // next cell and start editing it automaitcally.
3450                 // Unfortunately, we may not be in a state here where
3451                 // editing is allowed.  This happens when the user selects
3452                 // a new cell with the mouse instead of just hitting enter.
3453                 // Some kind of Gtk quirk.  widget_editable==NULL assertion.
3454                 // Editing is enabled again once the selection event has been
3455                 // processed.  So I'm queueing up a callback to be called
3456                 // when things go idle.  There, I will advance to the next
3457                 // cell and initiate editing.
3458                 //
3459                 // Now, you might be asking why I don't catch the keypress
3460                 // event and determine what action to take based on that.
3461                 // The Gtk developers in their infinite wisdom have made the 
3462                 // actual GtkEdit widget being used a private member of
3463                 // GtkCellRendererText, so it can not be accessed to hang a
3464                 // signal handler off of.  And they also do not propagate the
3465                 // keypress signals in any other way.  So that information is lost.
3466                 //g_idle_add((GSourceFunc)next_cell, ud);
3467                 //
3468                 // Keeping the above comment for posterity.
3469                 // I got industrious and made my own CellTextRendererText that
3470                 // passes on the key-press-event. So now I have much better
3471                 // control of this.
3472                 column = gtk_tree_view_get_column(treeview, 2);
3473                 gtk_tree_view_set_cursor(treeview, treepath, column, TRUE);
3474         }
3475         else if (chapter_edit_key == GDK_Up && row > 0)
3476         {
3477                 GtkTreeViewColumn *column;
3478                 gtk_tree_path_prev(treepath);
3479                 column = gtk_tree_view_get_column(treeview, 2);
3480                 gtk_tree_view_set_cursor(treeview, treepath, column, TRUE);
3481         }
3482         gtk_tree_path_free (treepath);
3483 }
3484
3485 void
3486 debug_log_handler(const gchar *domain, GLogLevelFlags flags, const gchar *msg, gpointer data)
3487 {
3488         signal_user_data_t *ud = (signal_user_data_t*)data;
3489         
3490         if (ud->debug)
3491         {
3492                 printf("%s: %s\n", domain, msg);
3493         }
3494 }
3495
3496 void
3497 warn_log_handler(const gchar *domain, GLogLevelFlags flags, const gchar *msg, gpointer data)
3498 {
3499         printf("%s: %s\n", domain, msg);
3500 }
3501
3502 void
3503 ghb_hbfd(signal_user_data_t *ud, gboolean hbfd)
3504 {
3505         GtkWidget *widget;
3506         g_debug("ghb_hbfd");
3507         widget = GHB_WIDGET(ud->builder, "queue_pause1");
3508         set_visible(widget, !hbfd);
3509         widget = GHB_WIDGET(ud->builder, "queue_add");
3510         set_visible(widget, !hbfd);
3511         widget = GHB_WIDGET(ud->builder, "show_queue");
3512         set_visible(widget, !hbfd);
3513         widget = GHB_WIDGET(ud->builder, "show_activity");
3514         set_visible(widget, !hbfd);
3515
3516         widget = GHB_WIDGET(ud->builder, "chapter_box");
3517         set_visible(widget, !hbfd);
3518         widget = GHB_WIDGET(ud->builder, "container_box");
3519         set_visible(widget, !hbfd);
3520         widget = GHB_WIDGET(ud->builder, "settings_box");
3521         set_visible(widget, !hbfd);
3522         widget = GHB_WIDGET(ud->builder, "presets_save");
3523         set_visible(widget, !hbfd);
3524         widget = GHB_WIDGET(ud->builder, "presets_remove");
3525         set_visible(widget, !hbfd);
3526         widget = GHB_WIDGET (ud->builder, "hb_window");
3527         gtk_window_resize(GTK_WINDOW(widget), 16, 16);
3528
3529 }
3530
3531 G_MODULE_EXPORT void
3532 hbfd_toggled_cb(GtkWidget *widget, signal_user_data_t *ud)
3533 {
3534         g_debug("hbfd_toggled_cb");
3535         ghb_widget_to_setting (ud->settings, widget);
3536         gboolean hbfd = ghb_settings_get_boolean(ud->settings, "hbfd");
3537         ghb_hbfd(ud, hbfd);
3538         ghb_pref_save(ud->settings, "hbfd");
3539 }
3540
3541 G_MODULE_EXPORT void
3542 pref_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3543 {
3544         g_debug("pref_changed_cb");
3545         ghb_widget_to_setting (ud->settings, widget);
3546         ghb_check_dependency(ud, widget, NULL);
3547         const gchar *name = ghb_get_setting_key(widget);
3548         ghb_pref_save(ud->settings, name);
3549 }
3550
3551 G_MODULE_EXPORT void
3552 use_m4v_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3553 {
3554         g_debug("use_m4v_changed_cb");
3555         ghb_widget_to_setting (ud->settings, widget);
3556         ghb_check_dependency(ud, widget, NULL);
3557         const gchar *name = ghb_get_setting_key(widget);
3558         ghb_pref_save(ud->settings, name);
3559         ghb_update_destination_extension(ud);
3560 }
3561
3562 G_MODULE_EXPORT void
3563 show_status_cb(GtkWidget *widget, signal_user_data_t *ud)
3564 {
3565         g_debug("show_status_cb");
3566         ghb_widget_to_setting (ud->settings, widget);
3567         ghb_check_dependency(ud, widget, NULL);
3568         const gchar *name = ghb_get_setting_key(widget);
3569         ghb_pref_save(ud->settings, name);
3570
3571         GtkStatusIcon *si;
3572
3573         si = GTK_STATUS_ICON(GHB_OBJECT (ud->builder, "hb_status"));
3574         gtk_status_icon_set_visible(si,
3575                         ghb_settings_get_boolean(ud->settings, "show_status"));
3576 }
3577
3578 G_MODULE_EXPORT void
3579 vqual_granularity_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3580 {
3581         g_debug("vqual_granularity_changed_cb");
3582         ghb_widget_to_setting (ud->settings, widget);
3583         ghb_check_dependency(ud, widget, NULL);
3584
3585         const gchar *name = ghb_get_setting_key(widget);
3586         ghb_pref_save(ud->settings, name);
3587
3588         gdouble vqmin, vqmax, step, page;
3589         gboolean inverted;
3590         gint digits;
3591
3592         ghb_vquality_range(ud, &vqmin, &vqmax, &step, &page, &digits, &inverted);
3593         GtkWidget *qp = GHB_WIDGET(ud->builder, "VideoQualitySlider");
3594         gtk_range_set_increments (GTK_RANGE(qp), step, page);
3595 }
3596
3597 G_MODULE_EXPORT void
3598 tweaks_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3599 {
3600         g_debug("tweaks_changed_cb");
3601         ghb_widget_to_setting (ud->settings, widget);
3602         const gchar *name = ghb_get_setting_key(widget);
3603         ghb_pref_save(ud->settings, name);
3604 }
3605
3606 G_MODULE_EXPORT void
3607 hbfd_feature_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3608 {
3609         g_debug("hbfd_feature_changed_cb");
3610         ghb_widget_to_setting (ud->settings, widget);
3611         const gchar *name = ghb_get_setting_key(widget);
3612         ghb_pref_save(ud->settings, name);
3613
3614         gboolean hbfd = ghb_settings_get_boolean(ud->settings, "hbfd_feature");
3615         GtkAction *action;
3616         if (hbfd)
3617         {
3618                 const GValue *val;
3619                 val = ghb_settings_get_value(ud->settings, "hbfd");
3620                 ghb_ui_update(ud, "hbfd", val);
3621         }
3622         action = GHB_ACTION (ud->builder, "hbfd");
3623         gtk_action_set_visible(action, hbfd);
3624 }
3625
3626 gboolean
3627 ghb_file_menu_add_dvd(signal_user_data_t *ud)
3628 {
3629         GList *link, *drives;
3630         static GtkActionGroup *agroup = NULL;
3631         static gint merge_id;
3632
3633         g_debug("ghb_file_menu_add_dvd()");
3634         link = drives = dvd_device_list();
3635         if (drives != NULL)
3636         {
3637                 GtkUIManager *ui = GTK_UI_MANAGER(
3638                         gtk_builder_get_object(ud->builder, "uimanager1"));
3639
3640                 if (agroup == NULL)
3641                 {
3642                         agroup = gtk_action_group_new("dvdgroup");
3643                         gtk_ui_manager_insert_action_group(ui, agroup, 0);
3644                 }
3645                 else
3646                         gtk_ui_manager_remove_ui(ui, merge_id);
3647
3648                 merge_id = gtk_ui_manager_new_merge_id(ui);
3649                 // Add separator
3650                 gtk_ui_manager_add_ui(ui, merge_id, 
3651                         "ui/menubar1/menuitem1/quit1", "dvdsep", NULL,
3652                         GTK_UI_MANAGER_SEPARATOR, TRUE);
3653
3654                 while (link != NULL)
3655                 {
3656                         GtkAction *action;
3657                         gchar *drive = get_dvd_device_name(link->data);
3658                         gchar *name = get_dvd_volume_name(link->data);
3659                 
3660                         action = gtk_action_group_get_action(agroup, drive);
3661                         if (action != NULL)
3662                         {
3663                                 gtk_action_group_remove_action(agroup, action);
3664                                 g_object_unref(G_OBJECT(action));
3665                         }
3666                         // Create action for this drive
3667                         action = gtk_action_new(drive, name,
3668                                 "Scan this DVD source", "gtk-cdrom");
3669                         // Add action to action group
3670                         gtk_action_group_add_action_with_accel(agroup, action, NULL);
3671                         // Add to ui manager
3672                         gtk_ui_manager_add_ui(ui, merge_id, 
3673                                 "ui/menubar1/menuitem1/dvdsep", drive, drive,
3674                                 GTK_UI_MANAGER_AUTO, TRUE);
3675                         // Connect signal to action (menu item)
3676                         g_signal_connect(action, "activate", 
3677                                 (GCallback)dvd_source_activate_cb, ud);
3678                         g_free(name);
3679                         g_free(drive);
3680                         free_drive(link->data);
3681                         link = link->next;
3682                 }
3683                 g_list_free(drives);
3684         }
3685         return FALSE;
3686 }
3687
3688 gboolean ghb_is_cd(GDrive *gd);
3689
3690 static GList*
3691 dvd_device_list()
3692 {
3693         GList *dvd_devices = NULL;
3694
3695 #if defined(_WIN32)
3696         gint ii, drives;
3697         gchar drive[5];
3698
3699         strcpy(drive, "A:" G_DIR_SEPARATOR_S);
3700         drives = GetLogicalDrives();
3701         for (ii = 0; ii < 26; ii++)
3702         {
3703                 if (drives & 0x01)
3704                 {
3705                         guint dtype;
3706
3707                         drive[0] = 'A' + ii;
3708                         dtype = GetDriveType(drive);
3709                         if (dtype == DRIVE_CDROM)
3710                         {
3711                                 dvd_devices = g_list_append(dvd_devices, 
3712                                                 (gpointer)g_strdup(drive));
3713                         }
3714                 }
3715                 drives >>= 1;
3716         }
3717 #else
3718         GVolumeMonitor *gvm;
3719         GList *drives, *link;
3720         
3721         gvm = g_volume_monitor_get ();
3722         drives = g_volume_monitor_get_connected_drives (gvm);
3723         link = drives;
3724         while (link != NULL)
3725         {
3726                 GDrive *gd;
3727                 
3728                 gd = (GDrive*)link->data;
3729                 if (ghb_is_cd(gd))
3730                 {
3731                         dvd_devices = g_list_append(dvd_devices, gd);
3732                 }
3733                 else
3734                         g_object_unref (gd);
3735                 link = link->next;
3736         }
3737         g_list_free(drives);
3738 #endif
3739
3740         return dvd_devices;
3741 }
3742
3743 #if !defined(_WIN32)
3744 static GUdevClient *udev_ctx = NULL;
3745 #endif
3746
3747 gboolean
3748 ghb_is_cd(GDrive *gd)
3749 {
3750 #if !defined(_WIN32)
3751         gchar *device;
3752         GUdevDevice *udd;
3753
3754         if (udev_ctx == NULL)
3755                 return FALSE;
3756
3757         device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
3758         if (device == NULL)
3759                 return FALSE;
3760
3761         udd = g_udev_client_query_by_device_file(udev_ctx, device);
3762         g_free(device);
3763
3764         if (udd == NULL)
3765         {
3766                 g_message("udev: Failed to lookup device %s", device);
3767                 return FALSE;
3768         }
3769
3770         gint val;
3771         val = g_udev_device_get_property_as_int(udd, "ID_CDROM_DVD");
3772         if (val == 1)
3773                 return TRUE;
3774
3775         return FALSE;
3776 #else
3777         return FALSE;
3778 #endif
3779 }
3780
3781 void
3782 ghb_udev_init()
3783 {
3784 #if !defined(_WIN32)
3785         udev_ctx = g_udev_client_new(NULL);
3786 #endif
3787 }
3788
3789 #if defined(_WIN32)
3790 static void
3791 handle_media_change(const gchar *device, gboolean insert, signal_user_data_t *ud)
3792 {
3793         guint dtype;
3794         static gint ins_count = 0;
3795         static gint rem_count = 0;
3796
3797         // The media change event in windows bounces around a bit
3798         // so I debounce it here
3799         // DVD insertion detected.  Scan it.
3800         dtype = GetDriveType(device);
3801         if (dtype != DRIVE_CDROM)
3802                 return;
3803         if (insert)
3804         {
3805                 rem_count = 0;
3806                 ins_count++;
3807                 if (ins_count == 2)
3808                 {
3809                         g_thread_create((GThreadFunc)ghb_cache_volnames, ud, FALSE, NULL);
3810                         if (ghb_settings_get_boolean(ud->settings, "AutoScan") &&
3811                                 ud->current_dvd_device != NULL &&
3812                                 strcmp(device, ud->current_dvd_device) == 0)
3813                         {
3814                                 show_scan_progress(ud);
3815                                 update_source_label(ud, device, TRUE);
3816                                 gint preview_count;
3817                                 preview_count = ghb_settings_get_int(ud->settings, "preview_count");
3818                                 ghb_settings_set_string(ud->settings, "scan_source", device);
3819                                 start_scan(ud, device, 0, preview_count);
3820                         }
3821                 }
3822         }
3823         else
3824         {
3825                 ins_count = 0;
3826                 rem_count++;
3827                 if (rem_count == 2)
3828                 {
3829                         g_thread_create((GThreadFunc)ghb_cache_volnames, ud, FALSE, NULL);
3830                         if (ud->current_dvd_device != NULL &&
3831                                 strcmp(device, ud->current_dvd_device) == 0)
3832                         {
3833                                 ghb_hb_cleanup(TRUE);
3834                                 prune_logs(ud);
3835                                 ghb_settings_set_string(ud->settings, "scan_source", "/dev/null");
3836                                 start_scan(ud, "/dev/null", 0, 1);
3837                         }
3838                 }
3839         }
3840 }
3841
3842 static gchar
3843 FindDriveFromMask(ULONG unitmask)
3844 {
3845         gchar cc;
3846         for (cc = 0; cc < 26; cc++)
3847         {
3848                 if (unitmask & 0x01)
3849                         return 'A' + cc;
3850                 unitmask >>= 1;
3851         }
3852         return 0;
3853 }
3854
3855 void
3856 wm_drive_changed(MSG *msg, signal_user_data_t *ud)
3857 {
3858         PDEV_BROADCAST_HDR bch = (PDEV_BROADCAST_HDR)msg->lParam;
3859         gchar drive[4];
3860
3861         g_strlcpy(drive, "A:" G_DIR_SEPARATOR_S, 4);
3862         switch (msg->wParam)
3863         {
3864                 case DBT_DEVICEARRIVAL:
3865                 {
3866                         if (bch->dbch_devicetype == DBT_DEVTYP_VOLUME)
3867                         {
3868                                 PDEV_BROADCAST_VOLUME bcv = (PDEV_BROADCAST_VOLUME)bch;
3869
3870                                 if (bcv->dbcv_flags & DBTF_MEDIA)
3871                                 {
3872                                         drive[0] = FindDriveFromMask(bcv->dbcv_unitmask);
3873                                         handle_media_change(drive, TRUE, ud);
3874                                 }
3875                         }
3876                 } break;
3877
3878                 case DBT_DEVICEREMOVECOMPLETE:
3879                 {
3880                         if (bch->dbch_devicetype == DBT_DEVTYP_VOLUME)
3881                         {
3882                                 PDEV_BROADCAST_VOLUME bcv = (PDEV_BROADCAST_VOLUME)bch;
3883
3884                                 if (bcv->dbcv_flags & DBTF_MEDIA)
3885                                 {
3886                                         drive[0] = FindDriveFromMask(bcv->dbcv_unitmask);
3887                                         handle_media_change(drive, FALSE, ud);
3888                                 }
3889                         }
3890                 } break;
3891                 default: ;
3892         }
3893 }
3894
3895 #else
3896
3897 G_MODULE_EXPORT void
3898 drive_changed_cb(GVolumeMonitor *gvm, GDrive *gd, signal_user_data_t *ud)
3899 {
3900         gchar *device;
3901         gint state;
3902
3903         g_debug("drive_changed_cb()");
3904         g_thread_create((GThreadFunc)ghb_cache_volnames, ud, FALSE, NULL);
3905
3906         state = ghb_get_scan_state();
3907         device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
3908         if (ud->current_dvd_device == NULL ||
3909                 strcmp(device, ud->current_dvd_device) != 0 ||
3910                 state != GHB_STATE_IDLE )
3911         {
3912                 return;
3913         }
3914         if (g_drive_has_media(gd))
3915         {
3916                 if (ghb_settings_get_boolean(ud->settings, "AutoScan"))
3917                 {
3918                         show_scan_progress(ud);
3919                         update_source_label(ud, device, TRUE);
3920                         gint preview_count;
3921                         preview_count = ghb_settings_get_int(ud->settings, "preview_count");
3922                         ghb_settings_set_string(ud->settings, "scan_source", device);
3923                         start_scan(ud, device, 0, preview_count);
3924                 }
3925         }
3926         else
3927         {
3928                 ghb_hb_cleanup(TRUE);
3929                 prune_logs(ud);
3930                 ghb_settings_set_string(ud->settings, "scan_source", "/dev/null");
3931                 start_scan(ud, "/dev/null", 0, 1);
3932         }
3933 }
3934 #endif
3935
3936 #if !defined(_WIN32)
3937 #define GPM_DBUS_PM_SERVICE                     "org.freedesktop.PowerManagement"
3938 #define GPM_DBUS_PM_PATH                        "/org/freedesktop/PowerManagement"
3939 #define GPM_DBUS_PM_INTERFACE           "org.freedesktop.PowerManagement"
3940 #define GPM_DBUS_INHIBIT_PATH           "/org/freedesktop/PowerManagement/Inhibit"
3941 #define GPM_DBUS_INHIBIT_INTERFACE      "org.freedesktop.PowerManagement.Inhibit" 
3942 static gboolean gpm_inhibited = FALSE;
3943 static guint gpm_cookie = -1;
3944 #endif
3945
3946 static gboolean
3947 ghb_can_suspend_gpm()
3948 {
3949         gboolean can_suspend = FALSE;
3950 #if !defined(_WIN32)
3951         DBusGConnection *conn;
3952         DBusGProxy      *proxy;
3953         GError *error = NULL;
3954         gboolean res;
3955         
3956
3957         g_debug("ghb_can_suspend_gpm()");
3958         conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
3959         if (error != NULL)
3960         {
3961                 g_warning("DBUS cannot connect: %s", error->message);
3962                 g_error_free(error);
3963                 return FALSE;
3964         }
3965         proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE,
3966                                                         GPM_DBUS_PM_PATH, GPM_DBUS_PM_INTERFACE);
3967         if (proxy == NULL)
3968         {
3969                 g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE);
3970                 dbus_g_connection_unref(conn);
3971                 return FALSE;
3972         }
3973         res = dbus_g_proxy_call(proxy, "CanSuspend", &error,
3974                                                         G_TYPE_INVALID,
3975                                                         G_TYPE_BOOLEAN, &can_suspend,
3976                                                         G_TYPE_INVALID);
3977         if (!res)
3978         {
3979                 if (error != NULL)
3980                 {
3981                         g_warning("CanSuspend failed: %s", error->message);
3982                         g_error_free(error);
3983                 }
3984                 else
3985                         g_warning("CanSuspend failed");
3986                 // Try to shutdown anyway
3987                 can_suspend = TRUE;
3988         }
3989         g_object_unref(G_OBJECT(proxy));
3990         dbus_g_connection_unref(conn);
3991 #endif
3992         return can_suspend;
3993 }
3994
3995 static void
3996 ghb_suspend_gpm()
3997 {
3998 #if !defined(_WIN32)
3999         DBusGConnection *conn;
4000         DBusGProxy      *proxy;
4001         GError *error = NULL;
4002         gboolean res;
4003         
4004
4005         g_debug("ghb_suspend_gpm()");
4006         conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
4007         if (error != NULL)
4008         {
4009                 g_warning("DBUS cannot connect: %s", error->message);
4010                 g_error_free(error);
4011                 return;
4012         }
4013         proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE,
4014                                                         GPM_DBUS_PM_PATH, GPM_DBUS_PM_INTERFACE);
4015         if (proxy == NULL)
4016         {
4017                 g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE);
4018                 dbus_g_connection_unref(conn);
4019                 return;
4020         }
4021         res = dbus_g_proxy_call(proxy, "Suspend", &error,
4022                                                         G_TYPE_INVALID,
4023                                                         G_TYPE_INVALID);
4024         if (!res)
4025         {
4026                 if (error != NULL)
4027                 {
4028                         g_warning("Suspend failed: %s", error->message);
4029                         g_error_free(error);
4030                 }
4031                 else
4032                         g_warning("Suspend failed");
4033         }
4034         g_object_unref(G_OBJECT(proxy));
4035         dbus_g_connection_unref(conn);
4036 #endif
4037 }
4038
4039 #if !defined(_WIN32)
4040 static gboolean
4041 ghb_can_shutdown_gpm()
4042 {
4043         gboolean can_shutdown = FALSE;
4044         DBusGConnection *conn;
4045         DBusGProxy      *proxy;
4046         GError *error = NULL;
4047         gboolean res;
4048         
4049
4050         g_debug("ghb_can_shutdown_gpm()");
4051         conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
4052         if (error != NULL)
4053         {
4054                 g_warning("DBUS cannot connect: %s", error->message);
4055                 g_error_free(error);
4056                 return FALSE;
4057         }
4058         proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE,
4059                                                         GPM_DBUS_PM_PATH, GPM_DBUS_PM_INTERFACE);
4060         if (proxy == NULL)
4061         {
4062                 g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE);
4063                 dbus_g_connection_unref(conn);
4064                 return FALSE;
4065         }
4066         res = dbus_g_proxy_call(proxy, "CanShutdown", &error,
4067                                                         G_TYPE_INVALID,
4068                                                         G_TYPE_BOOLEAN, &can_shutdown,
4069                                                         G_TYPE_INVALID);
4070         if (!res)
4071         {
4072                 if (error != NULL)
4073                 {
4074                         g_warning("CanShutdown failed: %s", error->message);
4075                         g_error_free(error);
4076                 }
4077                 else
4078                         g_warning("CanShutdown failed");
4079                 // Try to shutdown anyway
4080                 can_shutdown = TRUE;
4081         }
4082         g_object_unref(G_OBJECT(proxy));
4083         dbus_g_connection_unref(conn);
4084         return can_shutdown;
4085 }
4086 #endif
4087
4088 #if !defined(_WIN32)
4089 static void
4090 ghb_shutdown_gpm()
4091 {
4092         DBusGConnection *conn;
4093         DBusGProxy      *proxy;
4094         GError *error = NULL;
4095         gboolean res;
4096         
4097
4098         g_debug("ghb_shutdown_gpm()");
4099         conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
4100         if (error != NULL)
4101         {
4102                 g_warning("DBUS cannot connect: %s", error->message);
4103                 g_error_free(error);
4104                 return;
4105         }
4106         proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE,
4107                                                         GPM_DBUS_PM_PATH, GPM_DBUS_PM_INTERFACE);
4108         if (proxy == NULL)
4109         {
4110                 g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE);
4111                 dbus_g_connection_unref(conn);
4112                 return;
4113         }
4114         res = dbus_g_proxy_call(proxy, "Shutdown", &error,
4115                                                         G_TYPE_INVALID,
4116                                                         G_TYPE_INVALID);
4117         if (!res)
4118         {
4119                 if (error != NULL)
4120                 {
4121                         g_warning("Shutdown failed: %s", error->message);
4122                         g_error_free(error);
4123                 }
4124                 else
4125                         g_warning("Shutdown failed");
4126         }
4127         g_object_unref(G_OBJECT(proxy));
4128         dbus_g_connection_unref(conn);
4129 }
4130 #endif
4131
4132 void
4133 ghb_inhibit_gpm()
4134 {
4135 #if !defined(_WIN32)
4136         DBusGConnection *conn;
4137         DBusGProxy      *proxy;
4138         GError *error = NULL;
4139         gboolean res;
4140         
4141
4142         if (gpm_inhibited)
4143         {
4144                 // Already inhibited
4145                 return;
4146         }
4147         g_debug("ghb_inhibit_gpm()");
4148         conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
4149         if (error != NULL)
4150         {
4151                 g_warning("DBUS cannot connect: %s", error->message);
4152                 g_error_free(error);
4153                 return;
4154         }
4155         proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE,
4156                                                         GPM_DBUS_INHIBIT_PATH, GPM_DBUS_INHIBIT_INTERFACE);
4157         if (proxy == NULL)
4158         {
4159                 g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE);
4160                 dbus_g_connection_unref(conn);
4161                 return;
4162         }
4163         res = dbus_g_proxy_call(proxy, "Inhibit", &error,
4164                                                         G_TYPE_STRING, "ghb",
4165                                                         G_TYPE_STRING, "Encoding",
4166                                                         G_TYPE_INVALID,
4167                                                         G_TYPE_UINT, &gpm_cookie,
4168                                                         G_TYPE_INVALID);
4169         gpm_inhibited = TRUE;
4170         if (!res)
4171         {
4172                 if (error != NULL)
4173                 {
4174                         g_warning("Inhibit failed: %s", error->message);
4175                         g_error_free(error);
4176                         gpm_cookie = -1;
4177                 }
4178                 else
4179                         g_warning("Inhibit failed");
4180                 gpm_cookie = -1;
4181                 gpm_inhibited = FALSE;
4182         }
4183         g_object_unref(G_OBJECT(proxy));
4184         dbus_g_connection_unref(conn);
4185 #endif
4186 }
4187
4188 void
4189 ghb_uninhibit_gpm()
4190 {
4191 #if !defined(_WIN32)
4192         DBusGConnection *conn;
4193         DBusGProxy      *proxy;
4194         GError *error = NULL;
4195         gboolean res;
4196         
4197         g_debug("ghb_uninhibit_gpm() gpm_cookie %u", gpm_cookie);
4198
4199         if (!gpm_inhibited)
4200         {
4201                 // Not inhibited
4202                 return;
4203         }
4204         conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
4205         if (error != NULL)
4206         {
4207                 g_warning("DBUS cannot connect: %s", error->message);
4208                 g_error_free(error);
4209                 return;
4210         }
4211         proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE,
4212                                                         GPM_DBUS_INHIBIT_PATH, GPM_DBUS_INHIBIT_INTERFACE);
4213         if (proxy == NULL)
4214         {
4215                 g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE);
4216                 dbus_g_connection_unref(conn);
4217                 return;
4218         }
4219         res = dbus_g_proxy_call(proxy, "UnInhibit", &error,
4220                                                         G_TYPE_UINT, gpm_cookie,
4221                                                         G_TYPE_INVALID,
4222                                                         G_TYPE_INVALID);
4223         if (!res)
4224         {
4225                 if (error != NULL)
4226                 {
4227                         g_warning("UnInhibit failed: %s", error->message);
4228                         g_error_free(error);
4229                 }
4230                 else
4231                         g_warning("UnInhibit failed");
4232         }
4233         gpm_inhibited = FALSE;
4234         dbus_g_connection_unref(conn);
4235         g_object_unref(G_OBJECT(proxy));
4236 #endif
4237 }
4238
4239 #if !defined(_WIN32)
4240
4241 // For inhibit and shutdown
4242 #define GPM_DBUS_SM_SERVICE                     "org.gnome.SessionManager"
4243 #define GPM_DBUS_SM_PATH                        "/org/gnome/SessionManager"
4244 #define GPM_DBUS_SM_INTERFACE           "org.gnome.SessionManager"
4245
4246 #endif
4247
4248 static gboolean
4249 ghb_can_shutdown_gsm()
4250 {
4251         gboolean can_shutdown = FALSE;
4252 #if !defined(_WIN32)
4253         DBusGConnection *conn;
4254         DBusGProxy      *proxy;
4255         GError *error = NULL;
4256         gboolean res;
4257         
4258
4259         g_debug("ghb_can_shutdown_gpm()");
4260         conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
4261         if (error != NULL)
4262         {
4263                 g_warning("DBUS cannot connect: %s", error->message);
4264                 g_error_free(error);
4265                 return FALSE;
4266         }
4267         proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SM_SERVICE,
4268                                                         GPM_DBUS_SM_PATH, GPM_DBUS_SM_INTERFACE);
4269         if (proxy == NULL)
4270         {
4271                 g_warning("Could not get DBUS proxy: %s", GPM_DBUS_SM_SERVICE);
4272                 dbus_g_connection_unref(conn);
4273                 return FALSE;
4274         }
4275         res = dbus_g_proxy_call(proxy, "CanShutdown", &error,
4276                                                         G_TYPE_INVALID,
4277                                                         G_TYPE_BOOLEAN, &can_shutdown,
4278                                                         G_TYPE_INVALID);
4279         g_object_unref(G_OBJECT(proxy));
4280         dbus_g_connection_unref(conn);
4281         if (!res)
4282         {
4283                 if (error != NULL)
4284                 {
4285                         g_error_free(error);
4286                 }
4287                 // Try to shutdown anyway
4288                 can_shutdown = TRUE;
4289                 // Try the gpm version
4290                 return ghb_can_shutdown_gpm();
4291         }
4292 #endif
4293         return can_shutdown;
4294 }
4295
4296 static void
4297 ghb_shutdown_gsm()
4298 {
4299 #if !defined(_WIN32)
4300         DBusGConnection *conn;
4301         DBusGProxy      *proxy;
4302         GError *error = NULL;
4303         gboolean res;
4304         
4305
4306         g_debug("ghb_shutdown_gpm()");
4307         conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
4308         if (error != NULL)
4309         {
4310                 g_warning("DBUS cannot connect: %s", error->message);
4311                 g_error_free(error);
4312                 return;
4313         }
4314         proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SM_SERVICE,
4315                                                         GPM_DBUS_SM_PATH, GPM_DBUS_SM_INTERFACE);
4316         if (proxy == NULL)
4317         {
4318                 g_warning("Could not get DBUS proxy: %s", GPM_DBUS_SM_SERVICE);
4319                 dbus_g_connection_unref(conn);
4320                 return;
4321         }
4322         res = dbus_g_proxy_call(proxy, "Shutdown", &error,
4323                                                         G_TYPE_INVALID,
4324                                                         G_TYPE_INVALID);
4325         g_object_unref(G_OBJECT(proxy));
4326         dbus_g_connection_unref(conn);
4327         if (!res)
4328         {
4329                 if (error != NULL)
4330                 {
4331                         g_error_free(error);
4332                 }
4333                 // Try the gpm version
4334                 ghb_shutdown_gpm();
4335         }
4336 #endif
4337 }
4338
4339 void
4340 ghb_inhibit_gsm(signal_user_data_t *ud)
4341 {
4342 #if !defined(_WIN32)
4343         DBusGConnection *conn;
4344         DBusGProxy      *proxy;
4345         GError *error = NULL;
4346         gboolean res;
4347         guint xid;
4348         GtkWidget *widget;
4349         
4350
4351         if (gpm_inhibited)
4352         {
4353                 // Already inhibited
4354                 return;
4355         }
4356         g_debug("ghb_inhibit_gsm()");
4357         conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
4358         if (error != NULL)
4359         {
4360                 g_warning("DBUS cannot connect: %s", error->message);
4361                 g_error_free(error);
4362                 return;
4363         }
4364         proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SM_SERVICE,
4365                                                         GPM_DBUS_SM_PATH, GPM_DBUS_SM_INTERFACE);
4366         if (proxy == NULL)
4367         {
4368                 g_warning("Could not get DBUS proxy: %s", GPM_DBUS_SM_SERVICE);
4369                 dbus_g_connection_unref(conn);
4370                 return;
4371         }
4372         widget = GHB_WIDGET(ud->builder, "hb_window");
4373         xid = GDK_DRAWABLE_XID(widget->window);
4374         res = dbus_g_proxy_call(proxy, "Inhibit", &error,
4375                                                         G_TYPE_STRING, "ghb",
4376                                                         G_TYPE_UINT, xid,
4377                                                         G_TYPE_STRING, "Encoding",
4378                                                         G_TYPE_UINT, 1 | 4,
4379                                                         G_TYPE_INVALID,
4380                                                         G_TYPE_UINT, &gpm_cookie,
4381                                                         G_TYPE_INVALID);
4382         gpm_inhibited = TRUE;
4383         g_object_unref(G_OBJECT(proxy));
4384         dbus_g_connection_unref(conn);
4385         if (!res)
4386         {
4387                 if (error != NULL)
4388                 {
4389                         g_error_free(error);
4390                         gpm_cookie = -1;
4391                 }
4392                 gpm_cookie = -1;
4393                 gpm_inhibited = FALSE;
4394                 // Try the gpm version
4395                 ghb_inhibit_gpm();
4396         }
4397 #endif
4398 }
4399
4400 void
4401 ghb_uninhibit_gsm()
4402 {
4403 #if !defined(_WIN32)
4404         DBusGConnection *conn;
4405         DBusGProxy      *proxy;
4406         GError *error = NULL;
4407         gboolean res;
4408         
4409         g_debug("ghb_uninhibit_gsm() gpm_cookie %u", gpm_cookie);
4410
4411         if (!gpm_inhibited)
4412         {
4413                 // Not inhibited
4414                 return;
4415         }
4416         conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
4417         if (error != NULL)
4418         {
4419                 g_warning("DBUS cannot connect: %s", error->message);
4420                 g_error_free(error);
4421                 return;
4422         }
4423         proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SM_SERVICE,
4424                                                         GPM_DBUS_SM_PATH, GPM_DBUS_SM_INTERFACE);
4425         if (proxy == NULL)
4426         {
4427                 g_warning("Could not get DBUS proxy: %s", GPM_DBUS_SM_SERVICE);
4428                 dbus_g_connection_unref(conn);
4429                 return;
4430         }
4431         res = dbus_g_proxy_call(proxy, "Uninhibit", &error,
4432                                                         G_TYPE_UINT, gpm_cookie,
4433                                                         G_TYPE_INVALID,
4434                                                         G_TYPE_INVALID);
4435         dbus_g_connection_unref(conn);
4436         g_object_unref(G_OBJECT(proxy));
4437         if (!res)
4438         {
4439                 if (error != NULL)
4440                 {
4441                         g_error_free(error);
4442                 }
4443                 ghb_uninhibit_gpm();
4444         }
4445         gpm_inhibited = FALSE;
4446 #endif
4447 }
4448
4449 G_MODULE_EXPORT gboolean 
4450 tweak_setting_cb(
4451         GtkWidget *widget, 
4452         GdkEventButton *event, 
4453         signal_user_data_t *ud)
4454 {
4455         const gchar *name;
4456         gchar *tweak_name;
4457         gboolean ret = FALSE;
4458         gboolean allow_tweaks;
4459
4460         g_debug("press %d %d", event->type, event->button);
4461         allow_tweaks = ghb_settings_get_boolean(ud->settings, "allow_tweaks");
4462         if (allow_tweaks && event->type == GDK_BUTTON_PRESS && event->button == 3)
4463         { // Its a right mouse click
4464                 GtkWidget *dialog;
4465                 GtkEntry *entry;
4466                 GtkResponseType response;
4467                 gchar *tweak = NULL;
4468
4469                 name = ghb_get_setting_key(widget);
4470                 if (g_str_has_prefix(name, "tweak_"))
4471                 {
4472                         tweak_name = g_strdup(name);
4473                 }
4474                 else
4475                 {
4476                         tweak_name = g_strdup_printf("tweak_%s", name);
4477                 }
4478
4479                 tweak = ghb_settings_get_string (ud->settings, tweak_name);
4480                 dialog = GHB_WIDGET(ud->builder, "tweak_dialog");
4481                 gtk_window_set_title(GTK_WINDOW(dialog), tweak_name);
4482                 entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "tweak_setting"));
4483                 if (tweak)
4484                 {
4485                         gtk_entry_set_text(entry, tweak);
4486                         g_free(tweak);
4487                 }
4488                 response = gtk_dialog_run(GTK_DIALOG(dialog));
4489                 gtk_widget_hide(dialog);
4490                 if (response == GTK_RESPONSE_OK)
4491                 {
4492                         tweak = (gchar*)gtk_entry_get_text(entry);
4493                         if (ghb_validate_filter_string(tweak, -1))
4494                                 ghb_settings_set_string(ud->settings, tweak_name, tweak);
4495                         else
4496                         {
4497                                 gchar *message;
4498                                 message = g_strdup_printf(
4499                                                         "Invalid Settings:\n%s",
4500                                                         tweak);
4501                                 ghb_message_dialog(GTK_MESSAGE_ERROR, message, "Cancel", NULL);
4502                                 g_free(message);
4503                         }
4504                 }
4505                 g_free(tweak_name);
4506                 ret = TRUE;
4507         }
4508         return ret;
4509 }
4510
4511 G_MODULE_EXPORT gboolean 
4512 easter_egg_cb(
4513         GtkWidget *widget, 
4514         GdkEventButton *event, 
4515         signal_user_data_t *ud)
4516 {
4517         g_debug("press %d %d", event->type, event->button);
4518         if (event->type == GDK_3BUTTON_PRESS && event->button == 1)
4519         { // Its a tripple left mouse button click
4520                 GtkWidget *widget;
4521                 widget = GHB_WIDGET(ud->builder, "allow_tweaks");
4522                 gtk_widget_show(widget);
4523                 widget = GHB_WIDGET(ud->builder, "hbfd_feature");
4524                 gtk_widget_show(widget);
4525         }
4526         else if (event->type == GDK_BUTTON_PRESS && event->button == 1)
4527         {
4528                 GtkWidget *widget;
4529                 widget = GHB_WIDGET(ud->builder, "allow_tweaks");
4530                 gtk_widget_hide(widget);
4531                 widget = GHB_WIDGET(ud->builder, "hbfd_feature");
4532                 gtk_widget_hide(widget);
4533         }
4534         return FALSE;
4535 }
4536
4537 G_MODULE_EXPORT gchar*
4538 format_deblock_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud)
4539 {
4540         if (val < 5.0)
4541         {
4542                 return g_strdup_printf("Off");
4543         }
4544         else
4545         {
4546                 return g_strdup_printf("%d", (gint)val);
4547         }
4548 }
4549
4550 G_MODULE_EXPORT gchar*
4551 format_drc_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud)
4552 {
4553         if (val <= 0.0)
4554         {
4555                 return g_strdup_printf("Off");
4556         }
4557         else
4558         {
4559                 return g_strdup_printf("%.1f", val);
4560         }
4561 }
4562
4563 G_MODULE_EXPORT gchar*
4564 format_vquality_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud)
4565 {
4566         gint vcodec = ghb_settings_combo_int(ud->settings, "VideoEncoder");
4567         switch (vcodec)
4568         {
4569                 case HB_VCODEC_X264:
4570                 {
4571                         return g_strdup_printf("RF: %.4g", val);
4572                 } break;
4573
4574                 case HB_VCODEC_FFMPEG:
4575                 {
4576                         return g_strdup_printf("QP: %d", (int)val);
4577                 } break;
4578
4579                 case HB_VCODEC_THEORA:
4580                 {
4581                         return g_strdup_printf("QP: %d", (int)val);
4582                 } break;
4583
4584                 default:
4585                 {
4586                 } break;
4587         }
4588         return g_strdup_printf("QP: %.4g", val);
4589 }
4590
4591 static void
4592 process_appcast(signal_user_data_t *ud)
4593 {
4594         gchar *description = NULL, *build = NULL, *version = NULL, *msg;
4595 #if !defined(_WIN32)
4596         GtkWidget *window;
4597         static GtkWidget *html = NULL;
4598 #endif
4599         GtkWidget *dialog, *label;
4600         gint    response, ibuild = 0, skip;
4601
4602         if (ud->appcast == NULL || ud->appcast_len < 15 || 
4603                 strncmp(&(ud->appcast[9]), "200 OK", 6))
4604         {
4605                 goto done;
4606         }
4607         ghb_appcast_parse(ud->appcast, &description, &build, &version);
4608         if (build)
4609                 ibuild = g_strtod(build, NULL);
4610         skip = ghb_settings_get_int(ud->settings, "update_skip_version");
4611         if (description == NULL || build == NULL || version == NULL 
4612                 || ibuild <= hb_get_build(NULL) || skip == ibuild)
4613         {
4614                 goto done;
4615         }
4616         msg = g_strdup_printf("HandBrake %s/%s is now available (you have %s/%d).",
4617                         version, build, hb_get_version(NULL), hb_get_build(NULL));
4618         label = GHB_WIDGET(ud->builder, "update_message");
4619         gtk_label_set_text(GTK_LABEL(label), msg);
4620
4621 #if !defined(_WIN32)
4622 #if !defined(_NO_UPDATE_CHECK)
4623         if (html == NULL)
4624         {
4625                 html = webkit_web_view_new();
4626                 window = GHB_WIDGET(ud->builder, "update_scroll");
4627                 gtk_container_add(GTK_CONTAINER(window), html);
4628                 // Show it
4629                 gtk_widget_set_size_request(html, 420, 240);
4630                 gtk_widget_show(html);
4631         }
4632         webkit_web_view_open(WEBKIT_WEB_VIEW(html), description);
4633 #endif
4634 #endif
4635         dialog = GHB_WIDGET(ud->builder, "update_dialog");
4636         response = gtk_dialog_run(GTK_DIALOG(dialog));
4637         gtk_widget_hide(dialog);
4638         if (response == GTK_RESPONSE_OK)
4639         {
4640                 // Skip
4641                 ghb_settings_set_int(ud->settings, "update_skip_version", ibuild);
4642                 ghb_pref_save(ud->settings, "update_skip_version");
4643         }
4644         g_free(msg);
4645
4646 done:
4647         if (description) g_free(description);
4648         if (build) g_free(build);
4649         if (version) g_free(version);
4650         g_free(ud->appcast);
4651         ud->appcast_len = 0;
4652         ud->appcast = NULL;
4653         appcast_busy = FALSE;
4654 }
4655
4656 void
4657 ghb_net_close(GIOChannel *ioc)
4658 {
4659         gint fd;
4660
4661         g_debug("ghb_net_close");
4662         if (ioc == NULL) return;
4663         fd = g_io_channel_unix_get_fd(ioc);
4664         close(fd);
4665         g_io_channel_unref(ioc);
4666 }
4667
4668 G_MODULE_EXPORT gboolean
4669 ghb_net_recv_cb(GIOChannel *ioc, GIOCondition cond, gpointer data)
4670 {
4671         gchar buf[2048];
4672         gsize len;
4673         GError *gerror = NULL;
4674         GIOStatus status;
4675         
4676         g_debug("ghb_net_recv_cb");
4677         signal_user_data_t *ud = (signal_user_data_t*)data;
4678
4679         status = g_io_channel_read_chars (ioc, buf, 2048, &len, &gerror);
4680         if ((status == G_IO_STATUS_NORMAL || status == G_IO_STATUS_EOF) &&
4681                 len > 0)
4682         {
4683                 gint new_len = ud->appcast_len + len;
4684                 ud->appcast = g_realloc(ud->appcast, new_len + 1);
4685                 memcpy(&(ud->appcast[ud->appcast_len]), buf, len);
4686                 ud->appcast_len = new_len;
4687         }
4688         if (status == G_IO_STATUS_EOF)
4689         {
4690                 if ( ud->appcast != NULL )
4691                 {
4692                         ud->appcast[ud->appcast_len] = 0;
4693                 }
4694                 ghb_net_close(ioc);
4695                 process_appcast(ud);
4696                 return FALSE;
4697         }
4698         return TRUE;
4699 }
4700
4701 GIOChannel*
4702 ghb_net_open(signal_user_data_t *ud, gchar *address, gint port)
4703 {
4704         GIOChannel *ioc;
4705         gint fd;
4706
4707         struct sockaddr_in   sock;
4708         struct hostent     * host;
4709
4710         g_debug("ghb_net_open");
4711         if( !( host = gethostbyname( address ) ) )
4712         {
4713                 g_warning( "gethostbyname failed (%s)", address );
4714                 appcast_busy = FALSE;
4715                 return NULL;
4716         }
4717
4718         memset( &sock, 0, sizeof( struct sockaddr_in ) );
4719         sock.sin_family = host->h_addrtype;
4720         sock.sin_port   = htons( port );
4721         memcpy( &sock.sin_addr, host->h_addr, host->h_length );
4722
4723         fd = socket(host->h_addrtype, SOCK_STREAM, 0);
4724         if( fd < 0 )
4725         {
4726                 g_debug( "socket failed" );
4727                 appcast_busy = FALSE;
4728                 return NULL;
4729         }
4730
4731         if(connect(fd, (struct sockaddr*)&sock, sizeof(struct sockaddr_in )) < 0 )
4732         {
4733                 g_debug( "connect failed" );
4734                 appcast_busy = FALSE;
4735                 return NULL;
4736         }
4737         ioc = g_io_channel_unix_new(fd);
4738         g_io_channel_set_encoding (ioc, NULL, NULL);
4739         g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
4740         g_io_add_watch (ioc, G_IO_IN, ghb_net_recv_cb, (gpointer)ud );
4741
4742         return ioc;
4743 }
4744
4745 gpointer
4746 ghb_check_update(signal_user_data_t *ud)
4747 {
4748         gchar *query;
4749         gsize len;
4750         GIOChannel *ioc;
4751         GError *gerror = NULL;
4752         GRegex *regex;
4753         GMatchInfo *mi;
4754         gchar *host, *appcast;
4755
4756         g_debug("ghb_check_update");
4757         appcast_busy = TRUE;
4758         regex = g_regex_new("^http://(.+)/(.+)$", 0, 0, NULL);
4759         if (!g_regex_match(regex, HB_PROJECT_URL_APPCAST, 0, &mi))
4760         {
4761                 return NULL;
4762         }
4763
4764         host = g_match_info_fetch(mi, 1);
4765         appcast = g_match_info_fetch(mi, 2);
4766
4767         if (host == NULL || appcast == NULL)
4768                 return NULL;
4769
4770         query = g_strdup_printf( "GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n",
4771                                                         appcast, host);
4772
4773         ioc = ghb_net_open(ud, host, 80);
4774         if (ioc == NULL)
4775                 return NULL;
4776
4777         g_io_channel_write_chars(ioc, query, strlen(query), &len, &gerror);
4778         g_io_channel_flush(ioc, &gerror);
4779         g_free(query);
4780         g_free(host);
4781         g_free(appcast);
4782         g_match_info_free(mi);
4783         g_regex_unref(regex);
4784         return NULL;
4785 }
4786
4787 G_MODULE_EXPORT gboolean
4788 hb_visibility_event_cb(
4789         GtkWidget *widget, 
4790         GdkEventVisibility *vs, 
4791         signal_user_data_t *ud)
4792 {
4793         ud->hb_visibility = vs->state;
4794         return FALSE;
4795 }
4796
4797 G_MODULE_EXPORT void
4798 status_activate_cb(GtkStatusIcon *si, signal_user_data_t *ud)
4799 {
4800         GtkWindow *window;
4801         GdkWindowState state;
4802
4803         window = GTK_WINDOW(GHB_WIDGET(ud->builder, "hb_window"));
4804         state = gdk_window_get_state(GTK_WIDGET(window)->window);
4805         if ((state & GDK_WINDOW_STATE_ICONIFIED) ||
4806                 (ud->hb_visibility != GDK_VISIBILITY_UNOBSCURED))
4807         {
4808                 gtk_window_present(window);
4809                 gtk_window_set_skip_taskbar_hint(window, FALSE);
4810         }
4811         else
4812         {
4813                 gtk_window_set_skip_taskbar_hint(window, TRUE);
4814                 gtk_window_iconify(window);
4815         }
4816 }
4817
4818 #if !defined(_WIN32)
4819 G_MODULE_EXPORT void
4820 notify_closed_cb(NotifyNotification *notification, signal_user_data_t *ud)
4821 {
4822         g_object_unref(G_OBJECT(notification));
4823 }
4824 #endif
4825
4826 void
4827 ghb_notify_done(signal_user_data_t *ud)
4828 {
4829         GtkStatusIcon *si;
4830
4831         if (ghb_settings_combo_int(ud->settings, "WhenComplete") == 0)
4832                 return;
4833
4834         si = GTK_STATUS_ICON(GHB_OBJECT(ud->builder, "hb_status"));
4835
4836 #if !defined(_WIN32)
4837         NotifyNotification *notification;
4838         notification = notify_notification_new(
4839                 "Encode Complete",
4840                 "Put down that cocktail, Your HandBrake queue is done!",
4841                 "hb-icon",
4842                 NULL);
4843         notify_notification_attach_to_status_icon(notification, si);
4844         g_signal_connect(notification, "closed", (GCallback)notify_closed_cb, ud);
4845         notify_notification_show(notification, NULL);
4846 #endif
4847
4848         if (ghb_settings_combo_int(ud->settings, "WhenComplete") == 3)
4849         {
4850                 if (ghb_can_shutdown_gsm())
4851                 {
4852                         ghb_countdown_dialog(GTK_MESSAGE_WARNING, 
4853                                 "Your encode is complete.",
4854                                 "Shutting down the computer", 
4855                                 "Cancel", (GSourceFunc)shutdown_cb, 60);
4856                 }
4857         }
4858         if (ghb_settings_combo_int(ud->settings, "WhenComplete") == 2)
4859         {
4860                 if (ghb_can_suspend_gpm())
4861                 {
4862                         ghb_countdown_dialog(GTK_MESSAGE_WARNING, 
4863                                 "Your encode is complete.",
4864                                 "Putting computer to sleep", 
4865                                 "Cancel", (GSourceFunc)suspend_cb, 60);
4866                 }
4867         }
4868 }