OSDN Git Service

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