OSDN Git Service

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