1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * callbacks.c
4 * Copyright (C) John Stebbins 2008-2021 <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 version 2, as published by the Free Software
10 * Foundation.
11 *
12 * callbacks.c is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 * See the GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with main.c. If not, write to:
19 * The Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor
21 * Boston, MA 02110-1301, USA.
22 */
23
24 #ifdef HAVE_CONFIG_H
25 # include <config.h>
26 #endif
27
28 #include <string.h>
29 #include <fcntl.h>
30 #include <sys/stat.h>
31 #include <time.h>
32
33 #include <glib/gstdio.h>
34 #include <glib/gi18n.h>
35 #include <gio/gio.h>
36
37 #include "ghbcompat.h"
38
39 #if !defined(_WIN32)
40 #include <poll.h>
41 #define G_UDEV_API_IS_SUBJECT_TO_CHANGE 1
42 #if defined(__linux__) && defined(_HAVE_GUDEV)
43 #include <gudev/gudev.h>
44 #endif
45
46 #if defined( __FreeBSD__ )
47 #include <sys/socket.h>
48 #endif
49 #include <netinet/in.h>
50 #include <netdb.h>
51
52 #if !defined(_NO_UPDATE_CHECK)
53 #if defined(_OLD_WEBKIT)
54 #include <webkit.h>
55 #else
56 #include <webkit/webkit.h>
57 #endif
58 #endif
59
60 #ifndef NOTIFY_CHECK_VERSION
61 #define NOTIFY_CHECK_VERSION(x,y,z) 0
62 #endif
63
64 #if !GTK_CHECK_VERSION(3, 4, 0)
65 #include <gdk/gdkx.h>
66 #endif
67
68 #ifndef NOTIFY_CHECK_VERSION
69 #define NOTIFY_CHECK_VERSION(x,y,z) 0
70 #endif
71 #else
72 #include <winsock2.h>
73 #include <dbt.h>
74 #endif
75
76 #include "handbrake/handbrake.h"
77 #include "callbacks.h"
78 #include "chapters.h"
79 #include "queuehandler.h"
80 #include "audiohandler.h"
81 #include "subtitlehandler.h"
82 #include "resources.h"
83 #include "settings.h"
84 #include "jobdict.h"
85 #include "presets.h"
86 #include "preview.h"
87 #include "values.h"
88 #include "plist.h"
89 #include "appcast.h"
90 #include "hb-backend.h"
91 #include "ghb-dvd.h"
92 #include "libavutil/parseutils.h"
93
94 static void update_queue_labels(signal_user_data_t *ud);
95 static void load_all_titles(signal_user_data_t *ud, int titleindex);
96 static GList* dvd_device_list();
97 static void prune_logs(signal_user_data_t *ud);
98 void ghb_notify_done(signal_user_data_t *ud);
99 gpointer ghb_check_update(signal_user_data_t *ud);
100 static gboolean ghb_can_shutdown_gsm();
101 static void ghb_shutdown_gsm();
102 static gboolean ghb_can_suspend_gpm();
103 static void ghb_suspend_gpm();
104 static gboolean appcast_busy = FALSE;
105
106 #if !defined(_WIN32)
107 #define GPM_DBUS_PM_SERVICE "org.freedesktop.PowerManagement"
108 #define GPM_DBUS_PM_PATH "/org/freedesktop/PowerManagement"
109 #define GPM_DBUS_PM_INTERFACE "org.freedesktop.PowerManagement"
110 #define GPM_DBUS_INHIBIT_PATH "/org/freedesktop/PowerManagement/Inhibit"
111 #define GPM_DBUS_INHIBIT_INTERFACE "org.freedesktop.PowerManagement.Inhibit"
112 #endif
113
114 #if !defined(_WIN32)
115 static GDBusProxy *
ghb_get_session_dbus_proxy(const gchar * name,const gchar * path,const gchar * interface)116 ghb_get_session_dbus_proxy(const gchar *name, const gchar *path, const gchar *interface)
117 {
118 GDBusConnection *conn;
119 GDBusProxy *proxy;
120 GError *error = NULL;
121
122 g_debug("ghb_get_session_dbus_proxy()");
123 conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
124 if (conn == NULL)
125 {
126 if (error != NULL)
127 {
128 g_warning("DBUS cannot connect: %s", error->message);
129 g_error_free(error);
130 }
131 return NULL;
132 }
133
134 proxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE, NULL,
135 name, path, interface, NULL, NULL);
136 if (proxy == NULL)
137 g_warning("Could not get DBUS proxy: %s", name);
138
139 g_object_unref(conn);
140 return proxy;
141 }
142 #endif
143
144 static gboolean
ghb_can_suspend_gpm()145 ghb_can_suspend_gpm()
146 {
147 gboolean can_suspend = FALSE;
148 #if !defined(_WIN32)
149 GDBusProxy *proxy;
150 GError *error = NULL;
151 GVariant *res;
152
153
154 g_debug("ghb_can_suspend_gpm()");
155 proxy = ghb_get_session_dbus_proxy(GPM_DBUS_PM_SERVICE,
156 GPM_DBUS_PM_PATH, GPM_DBUS_PM_INTERFACE);
157 if (proxy == NULL)
158 return FALSE;
159
160 res = g_dbus_proxy_call_sync(proxy, "CanSuspend", NULL,
161 G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
162 if (!res)
163 {
164 if (error != NULL)
165 {
166 g_warning("CanSuspend failed: %s", error->message);
167 g_error_free(error);
168 }
169 else
170 g_warning("CanSuspend failed");
171 // Try to shutdown anyway
172 can_suspend = TRUE;
173 }
174 else
175 {
176 g_variant_get(res, "(b)", &can_suspend);
177 g_variant_unref(res);
178 }
179 g_object_unref(G_OBJECT(proxy));
180 #endif
181 return can_suspend;
182 }
183
184 static void
ghb_suspend_gpm()185 ghb_suspend_gpm()
186 {
187 #if !defined(_WIN32)
188 GDBusProxy *proxy;
189 GError *error = NULL;
190 GVariant *res;
191
192
193 g_debug("ghb_suspend_gpm()");
194 proxy = ghb_get_session_dbus_proxy(GPM_DBUS_PM_SERVICE,
195 GPM_DBUS_PM_PATH, GPM_DBUS_PM_INTERFACE);
196 if (proxy == NULL)
197 return;
198
199 res = g_dbus_proxy_call_sync(proxy, "Suspend", NULL,
200 G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
201 if (!res)
202 {
203 if (error != NULL)
204 {
205 g_warning("Suspend failed: %s", error->message);
206 g_error_free(error);
207 }
208 else
209 g_warning("Suspend failed");
210 }
211 else
212 {
213 g_variant_unref(res);
214 }
215 g_object_unref(G_OBJECT(proxy));
216 #endif
217 }
218
219 #if !defined(_WIN32)
220 static gboolean
ghb_can_shutdown_gpm()221 ghb_can_shutdown_gpm()
222 {
223 gboolean can_shutdown = FALSE;
224 GDBusProxy *proxy;
225 GError *error = NULL;
226 GVariant *res;
227
228
229 g_debug("ghb_can_shutdown_gpm()");
230 proxy = ghb_get_session_dbus_proxy(GPM_DBUS_PM_SERVICE,
231 GPM_DBUS_PM_PATH, GPM_DBUS_PM_INTERFACE);
232 if (proxy == NULL)
233 return FALSE;
234
235 res = g_dbus_proxy_call_sync(proxy, "CanShutdown", NULL,
236 G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
237 if (!res)
238 {
239 if (error != NULL)
240 {
241 g_warning("CanShutdown failed: %s", error->message);
242 g_error_free(error);
243 }
244 else
245 g_warning("CanShutdown failed");
246 // Try to shutdown anyway
247 can_shutdown = TRUE;
248 }
249 else
250 {
251 g_variant_get(res, "(b)", &can_shutdown);
252 g_variant_unref(res);
253 }
254 g_object_unref(G_OBJECT(proxy));
255 return can_shutdown;
256 }
257 #endif
258
259 #if !defined(_WIN32)
260 static void
ghb_shutdown_gpm()261 ghb_shutdown_gpm()
262 {
263 GDBusProxy *proxy;
264 GError *error = NULL;
265 GVariant *res;
266
267
268 g_debug("ghb_shutdown_gpm()");
269 proxy = ghb_get_session_dbus_proxy(GPM_DBUS_PM_SERVICE,
270 GPM_DBUS_PM_PATH, GPM_DBUS_PM_INTERFACE);
271 if (proxy == NULL)
272 return;
273
274 res = g_dbus_proxy_call_sync(proxy, "Shutdown", NULL,
275 G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
276 if (!res)
277 {
278 if (error != NULL)
279 {
280 g_warning("Shutdown failed: %s", error->message);
281 g_error_free(error);
282 }
283 else
284 g_warning("Shutdown failed");
285 }
286 else
287 {
288 g_variant_unref(res);
289 }
290 g_object_unref(G_OBJECT(proxy));
291 }
292 #endif
293
294 #if !GTK_CHECK_VERSION(3, 4, 0)
295 guint
ghb_inhibit_gpm()296 ghb_inhibit_gpm()
297 {
298 guint cookie = -1;
299
300 #if !defined(_WIN32)
301 GDBusProxy *proxy;
302 GError *error = NULL;
303 GVariant *res;
304
305 g_debug("ghb_inhibit_gpm()");
306 proxy = ghb_get_session_dbus_proxy(GPM_DBUS_PM_SERVICE,
307 GPM_DBUS_INHIBIT_PATH, GPM_DBUS_INHIBIT_INTERFACE);
308 if (proxy == NULL)
309 return 0;
310
311 res = g_dbus_proxy_call_sync(proxy, "Inhibit",
312 g_variant_new("(ss)", "ghb", "Encoding"),
313 G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
314 if (!res)
315 {
316 if (error != NULL)
317 {
318 g_error_free(error);
319 }
320 }
321 else
322 {
323 g_variant_get(res, "(u)", &cookie);
324 g_variant_unref(res);
325 }
326 g_object_unref(G_OBJECT(proxy));
327 #endif
328
329 return cookie;
330 }
331
332 void
ghb_uninhibit_gpm(guint cookie)333 ghb_uninhibit_gpm(guint cookie)
334 {
335 #if !defined(_WIN32)
336 GDBusProxy *proxy;
337 GError *error = NULL;
338 GVariant *res;
339
340 g_debug("ghb_uninhibit_gpm() cookie %u", cookie);
341
342 proxy = ghb_get_session_dbus_proxy(GPM_DBUS_PM_SERVICE,
343 GPM_DBUS_INHIBIT_PATH, GPM_DBUS_INHIBIT_INTERFACE);
344 if (proxy == NULL)
345 return;
346
347 res = g_dbus_proxy_call_sync(proxy, "UnInhibit",
348 g_variant_new("(u)", cookie),
349 G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
350 if (!res)
351 {
352 if (error != NULL)
353 {
354 g_error_free(error);
355 }
356 }
357 else
358 {
359 g_variant_unref(res);
360 }
361 g_object_unref(G_OBJECT(proxy));
362 #endif
363 }
364 #endif
365
366 #if !defined(_WIN32)
367 // For inhibit and shutdown
368 #define GPM_DBUS_SM_SERVICE "org.gnome.SessionManager"
369 #define GPM_DBUS_SM_PATH "/org/gnome/SessionManager"
370 #define GPM_DBUS_SM_INTERFACE "org.gnome.SessionManager"
371 #endif
372
373 static gboolean
ghb_can_shutdown_gsm()374 ghb_can_shutdown_gsm()
375 {
376 gboolean can_shutdown = FALSE;
377 #if !defined(_WIN32)
378 GDBusProxy *proxy;
379 GError *error = NULL;
380 GVariant *res;
381
382 proxy = ghb_get_session_dbus_proxy(GPM_DBUS_SM_SERVICE,
383 GPM_DBUS_SM_PATH, GPM_DBUS_SM_INTERFACE);
384 if (proxy == NULL)
385 return FALSE;
386
387 res = g_dbus_proxy_call_sync(proxy, "CanShutdown", NULL,
388 G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
389 g_object_unref(G_OBJECT(proxy));
390 if (!res)
391 {
392 if (error != NULL)
393 {
394 g_error_free(error);
395 }
396 // Try the gpm version
397 can_shutdown = ghb_can_shutdown_gpm();
398 }
399 else
400 {
401 g_variant_get(res, "(b)", &can_shutdown);
402 g_variant_unref(res);
403 }
404 #endif
405 return can_shutdown;
406 }
407
408 static void
ghb_shutdown_gsm()409 ghb_shutdown_gsm()
410 {
411 #if !defined(_WIN32)
412 GDBusProxy *proxy;
413 GError *error = NULL;
414 GVariant *res;
415
416
417 g_debug("ghb_shutdown_gpm()");
418 proxy = ghb_get_session_dbus_proxy(GPM_DBUS_SM_SERVICE,
419 GPM_DBUS_SM_PATH, GPM_DBUS_SM_INTERFACE);
420 if (proxy == NULL)
421 return;
422
423 res = g_dbus_proxy_call_sync(proxy, "Shutdown", NULL,
424 G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
425 g_object_unref(G_OBJECT(proxy));
426 if (!res)
427 {
428 if (error != NULL)
429 {
430 g_error_free(error);
431 }
432 // Try the gpm version
433 ghb_shutdown_gpm();
434 }
435 else
436 {
437 g_variant_unref(res);
438 }
439 #endif
440 }
441
442 #if !GTK_CHECK_VERSION(3, 4, 0)
443 guint
ghb_inhibit_gsm(signal_user_data_t * ud)444 ghb_inhibit_gsm(signal_user_data_t *ud)
445 {
446 guint cookie = -1;
447
448 #if !defined(_WIN32)
449 GDBusProxy *proxy;
450 GError *error = NULL;
451 GVariant *res;
452 guint xid;
453 GtkWidget *widget;
454
455 g_debug("ghb_inhibit_gsm()");
456 proxy = ghb_get_session_dbus_proxy(GPM_DBUS_SM_SERVICE,
457 GPM_DBUS_SM_PATH, GPM_DBUS_SM_INTERFACE);
458 if (proxy == NULL)
459 return -1;
460
461 widget = GHB_WIDGET(ud->builder, "hb_window");
462 xid = GDK_WINDOW_XID(gtk_widget_get_window(widget));
463 res = g_dbus_proxy_call_sync(proxy, "Inhibit",
464 g_variant_new("(susu)", "ghb", xid, "Encoding", 1 | 4),
465 G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
466 g_object_unref(G_OBJECT(proxy));
467 if (!res)
468 {
469 if (error != NULL)
470 {
471 g_error_free(error);
472 }
473 }
474 else
475 {
476 g_variant_get(res, "(u)", &cookie);
477 g_variant_unref(res);
478 }
479 #endif
480
481 return cookie;
482 }
483
484 void
ghb_uninhibit_gsm(guint cookie)485 ghb_uninhibit_gsm(guint cookie)
486 {
487 #if !defined(_WIN32)
488 GDBusProxy *proxy;
489 GError *error = NULL;
490 GVariant *res;
491
492 g_debug("ghb_uninhibit_gsm() cookie %u", cookie);
493
494 proxy = ghb_get_session_dbus_proxy(GPM_DBUS_SM_SERVICE,
495 GPM_DBUS_SM_PATH, GPM_DBUS_SM_INTERFACE);
496 if (proxy == NULL)
497 return;
498
499 res = g_dbus_proxy_call_sync(proxy, "Uninhibit",
500 g_variant_new("(u)", cookie),
501 G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
502 if (!res)
503 {
504 if (error != NULL)
505 {
506 g_error_free(error);
507 }
508 }
509 else
510 {
511 g_variant_unref(res);
512 }
513 g_object_unref(G_OBJECT(proxy));
514 #endif
515 }
516 #endif
517
518 enum {
519 GHB_SUSPEND_UNINHIBITED = 0,
520 GHB_SUSPEND_INHIBITED_GPM,
521 GHB_SUSPEND_INHIBITED_GSM,
522 GHB_SUSPEND_INHIBITED_GTK
523 };
524 static int suspend_inhibited = GHB_SUSPEND_UNINHIBITED;
525 static guint suspend_cookie;
526
527 void
ghb_inhibit_suspend(signal_user_data_t * ud)528 ghb_inhibit_suspend(signal_user_data_t *ud)
529 {
530 if (suspend_inhibited != GHB_SUSPEND_UNINHIBITED)
531 {
532 // Already inhibited
533 return;
534 }
535 #if GTK_CHECK_VERSION(3, 4, 0)
536 suspend_cookie = gtk_application_inhibit(ud->app, NULL,
537 GTK_APPLICATION_INHIBIT_SUSPEND, "Encoding");
538 if (suspend_cookie != 0)
539 {
540 suspend_inhibited = GHB_SUSPEND_INHIBITED_GTK;
541 return;
542 }
543 #else
544 suspend_cookie = ghb_inhibit_gsm(ud);
545 if (suspend_cookie != -1)
546 {
547 suspend_inhibited = GHB_SUSPEND_INHIBITED_GSM;
548 return;
549 }
550 suspend_cookie = ghb_inhibit_gpm(ud);
551 if (suspend_cookie != -1)
552 {
553 suspend_inhibited = GHB_SUSPEND_INHIBITED_GPM;
554 return;
555 }
556 #endif
557 }
558
559 void
ghb_uninhibit_suspend(signal_user_data_t * ud)560 ghb_uninhibit_suspend(signal_user_data_t *ud)
561 {
562 switch (suspend_inhibited)
563 {
564 #if GTK_CHECK_VERSION(3, 4, 0)
565 case GHB_SUSPEND_INHIBITED_GTK:
566 gtk_application_uninhibit(ud->app, suspend_cookie);
567 break;
568 #else
569 case GHB_SUSPEND_INHIBITED_GPM:
570 ghb_uninhibit_gpm(suspend_cookie);
571 break;
572
573 case GHB_SUSPEND_INHIBITED_GSM:
574 ghb_uninhibit_gsm(suspend_cookie);
575 break;
576 #endif
577 default:
578 break;
579 }
580 suspend_inhibited = GHB_SUSPEND_UNINHIBITED;
581 }
582
583 // This is a dependency map used for greying widgets
584 // that are dependent on the state of another widget.
585 // The enable_value comes from the values that are
586 // obtained from ghb_widget_value(). For combo boxes
587 // you will have to look further to combo box options
588 // maps in hb-backend.c
589
590 GhbValue *dep_map;
591 GhbValue *rev_map;
592
593 void
ghb_init_dep_map()594 ghb_init_dep_map()
595 {
596 dep_map = ghb_resource_get("widget-deps");
597 rev_map = ghb_resource_get("widget-reverse-deps");
598 }
599
600 static gboolean
dep_check(signal_user_data_t * ud,const gchar * name,gboolean * out_hide)601 dep_check(signal_user_data_t *ud, const gchar *name, gboolean *out_hide)
602 {
603 GtkWidget *widget;
604 GObject *dep_object;
605 gint ii;
606 gint count;
607 gboolean result = TRUE;
608 GhbValue *array, *data;
609 const gchar *widget_name;
610
611 g_debug("dep_check () %s", name);
612
613 if (rev_map == NULL) return TRUE;
614 array = ghb_dict_get(rev_map, name);
615 count = ghb_array_len(array);
616 *out_hide = FALSE;
617 for (ii = 0; ii < count; ii++)
618 {
619 data = ghb_array_get(array, ii);
620 widget_name = ghb_value_get_string(ghb_array_get(data, 0));
621 widget = GHB_WIDGET(ud->builder, widget_name);
622 dep_object = gtk_builder_get_object(ud->builder, name);
623 if (widget != NULL && !gtk_widget_is_sensitive(widget))
624 {
625 continue;
626 }
627 if (dep_object == NULL)
628 {
629 g_message("Failed to find widget");
630 }
631 else
632 {
633 gchar *value;
634 gint jj = 0;
635 gchar **values;
636 gboolean sensitive = FALSE;
637 gboolean die, hide;
638
639 die = ghb_value_get_bool(ghb_array_get(data, 2));
640 hide = ghb_value_get_bool(ghb_array_get(data, 3));
641 const char *tmp = ghb_value_get_string(ghb_array_get(data, 1));
642 values = g_strsplit(tmp, "|", 10);
643
644 if (widget)
645 value = ghb_widget_string(widget);
646 else
647 value = ghb_dict_get_string_xform(ud->settings, widget_name);
648 while (values && values[jj])
649 {
650 if (values[jj][0] == '>')
651 {
652 gdouble dbl = g_strtod (&values[jj][1], NULL);
653 gdouble dvalue = ghb_widget_double(widget);
654 if (dvalue > dbl)
655 {
656 sensitive = TRUE;
657 break;
658 }
659 }
660 else if (values[jj][0] == '<')
661 {
662 gdouble dbl = g_strtod (&values[jj][1], NULL);
663 gdouble dvalue = ghb_widget_double(widget);
664 if (dvalue < dbl)
665 {
666 sensitive = TRUE;
667 break;
668 }
669 }
670 if (strcmp(values[jj], value) == 0)
671 {
672 sensitive = TRUE;
673 break;
674 }
675 jj++;
676 }
677 sensitive = die ^ sensitive;
678 if (!sensitive)
679 {
680 result = FALSE;
681 *out_hide |= hide;
682 }
683 g_strfreev (values);
684 g_free(value);
685 }
686 }
687 return result;
688 }
689
690 void
ghb_check_dependency(signal_user_data_t * ud,GtkWidget * widget,const char * alt_name)691 ghb_check_dependency(
692 signal_user_data_t *ud,
693 GtkWidget *widget,
694 const char *alt_name)
695 {
696 GObject *dep_object;
697 const gchar *name;
698 GhbValue *array, *data;
699 gint count, ii;
700 const gchar *dep_name;
701 GType type;
702
703 if (widget != NULL)
704 {
705 type = G_OBJECT_TYPE(widget);
706 if (type == GTK_TYPE_COMBO_BOX)
707 if (gtk_combo_box_get_active(GTK_COMBO_BOX(widget)) < 0) return;
708 name = ghb_get_setting_key(widget);
709 }
710 else
711 name = alt_name;
712
713 g_debug("ghb_check_dependency() %s", name);
714
715 if (dep_map == NULL) return;
716 array = ghb_dict_get(dep_map, name);
717 count = ghb_array_len(array);
718 for (ii = 0; ii < count; ii++)
719 {
720 gboolean sensitive;
721 gboolean hide;
722
723 data = ghb_array_get(array, ii);
724 dep_name = ghb_value_get_string(data);
725 dep_object = gtk_builder_get_object(ud->builder, dep_name);
726 if (dep_object == NULL)
727 {
728 g_message("Failed to find dependent widget %s", dep_name);
729 continue;
730 }
731 sensitive = dep_check(ud, dep_name, &hide);
732 gtk_widget_set_sensitive(GTK_WIDGET(dep_object), sensitive);
733 gtk_widget_set_can_focus(GTK_WIDGET(dep_object), sensitive);
734 if (!sensitive && hide)
735 {
736 if (gtk_widget_get_visible(GTK_WIDGET(dep_object)))
737 {
738 gtk_widget_hide(GTK_WIDGET(dep_object));
739 }
740 }
741 else
742 {
743 if (!gtk_widget_get_visible(GTK_WIDGET(dep_object)))
744 {
745 gtk_widget_show(GTK_WIDGET(dep_object));
746 }
747 }
748 }
749 }
750
751 void
ghb_check_all_dependencies(signal_user_data_t * ud)752 ghb_check_all_dependencies(signal_user_data_t *ud)
753 {
754 GhbDictIter iter;
755 const gchar *dep_name;
756 GhbValue *value;
757 GObject *dep_object;
758
759 g_debug("ghb_check_all_dependencies ()");
760 if (rev_map == NULL) return;
761 iter = ghb_dict_iter_init(rev_map);
762 while (ghb_dict_iter_next(rev_map, &iter, &dep_name, &value))
763 {
764 gboolean sensitive;
765 gboolean hide;
766
767 dep_object = gtk_builder_get_object (ud->builder, dep_name);
768 if (dep_object == NULL)
769 {
770 g_message("Failed to find dependent widget %s", dep_name);
771 continue;
772 }
773 sensitive = dep_check(ud, dep_name, &hide);
774 gtk_widget_set_sensitive(GTK_WIDGET(dep_object), sensitive);
775 gtk_widget_set_can_focus(GTK_WIDGET(dep_object), sensitive);
776 if (!sensitive && hide)
777 {
778 gtk_widget_hide(GTK_WIDGET(dep_object));
779 }
780 else
781 {
782 gtk_widget_show(GTK_WIDGET(dep_object));
783 }
784 }
785 }
786
787 G_MODULE_EXPORT void
quit_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)788 quit_action_cb(GSimpleAction *action, GVariant *param, signal_user_data_t *ud)
789 {
790 gint state = ghb_get_queue_state();
791 if (state & (GHB_STATE_WORKING|GHB_STATE_SEARCHING))
792 {
793 if (ghb_cancel_encode2(ud, _("Closing HandBrake will terminate encoding.\n")))
794 {
795 ghb_hb_cleanup(FALSE);
796 prune_logs(ud);
797 g_application_quit(G_APPLICATION(ud->app));
798 return;
799 }
800 return;
801 }
802 ghb_hb_cleanup(FALSE);
803 prune_logs(ud);
804 g_application_quit(G_APPLICATION(ud->app));
805 }
806
807 static gboolean
uppers_and_unders(gchar * str)808 uppers_and_unders(gchar *str)
809 {
810 if (str == NULL) return FALSE;
811 str = g_strchomp(g_strchug(str));
812 while (*str)
813 {
814 if (*str == ' ')
815 {
816 return FALSE;
817 }
818 if (*str >= 'a' && *str <= 'z')
819 {
820 return FALSE;
821 }
822 str++;
823 }
824 return TRUE;
825 }
826
827 enum
828 {
829 CAMEL_FIRST_UPPER,
830 CAMEL_OTHER
831 };
832
833 static void
camel_convert(gchar * str)834 camel_convert(gchar *str)
835 {
836 gint state = CAMEL_OTHER;
837
838 if (str == NULL) return;
839 while (*str)
840 {
841 if (*str == '_') *str = ' ';
842 switch (state)
843 {
844 case CAMEL_OTHER:
845 {
846 if (*str >= 'A' && *str <= 'Z')
847 state = CAMEL_FIRST_UPPER;
848 else
849 state = CAMEL_OTHER;
850
851 } break;
852 case CAMEL_FIRST_UPPER:
853 {
854 if (*str >= 'A' && *str <= 'Z')
855 *str = *str - 'A' + 'a';
856 else
857 state = CAMEL_OTHER;
858 } break;
859 }
860 str++;
861 }
862 }
863
864 #if defined(_WIN32)
865 static gchar*
get_dvd_device_name(gchar * device)866 get_dvd_device_name(gchar *device)
867 {
868 return g_strdup(device);
869 }
870 #else
871 static gchar*
get_dvd_device_name(GDrive * gd)872 get_dvd_device_name(GDrive *gd)
873 {
874 return g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
875 }
876 #endif
877
878 static GHashTable *volname_hash = NULL;
879 #if GLIB_CHECK_VERSION(2, 32, 0)
880 static GMutex volname_mutex_static;
881 #endif
882 static GMutex *volname_mutex;
883
884 static void
free_volname_key(gpointer data)885 free_volname_key(gpointer data)
886 {
887 if (data != NULL)
888 g_free(data);
889 }
890
891 static void
free_volname_value(gpointer data)892 free_volname_value(gpointer data)
893 {
894 if (data != NULL)
895 g_free(data);
896 }
897
898 #if defined(_WIN32)
899 static gchar*
get_direct_dvd_volume_name(const gchar * drive)900 get_direct_dvd_volume_name(const gchar *drive)
901 {
902 gchar *result = NULL;
903 gchar vname[51], fsname[51];
904
905 if (GetVolumeInformation(drive, vname, 50, NULL, NULL, NULL, fsname, 50))
906 {
907 result = g_strdup_printf("%s", vname);
908 }
909 return result;
910 }
911 #else
912 static gchar*
get_direct_dvd_volume_name(const gchar * drive)913 get_direct_dvd_volume_name(const gchar *drive)
914 {
915 gchar *result;
916
917 result = ghb_dvd_volname (drive);
918 return result;
919 }
920 #endif
921
922 static gchar*
get_dvd_volume_name(gpointer gd)923 get_dvd_volume_name(gpointer gd)
924 {
925 gchar *label = NULL;
926 gchar *result;
927 gchar *drive;
928
929 drive = get_dvd_device_name(gd);
930 g_mutex_lock(volname_mutex);
931 label = g_strdup(g_hash_table_lookup(volname_hash, drive));
932 g_mutex_unlock(volname_mutex);
933 if (label != NULL)
934 {
935 if (uppers_and_unders(label))
936 {
937 camel_convert(label);
938 }
939 #if defined(_WIN32)
940 result = g_strdup_printf("%s (%s)", label, drive);
941 #else
942 result = g_strdup_printf("%s - %s", drive, label);
943 #endif
944 g_free(label);
945 }
946 else
947 {
948 result = g_strdup_printf("%s", drive);
949 }
950 g_free(drive);
951 return result;
952 }
953
954 void
ghb_volname_cache_init(void)955 ghb_volname_cache_init(void)
956 {
957 #if GLIB_CHECK_VERSION(2, 32, 0)
958 g_mutex_init(&volname_mutex_static);
959 volname_mutex = &volname_mutex_static;
960 #else
961 volname_mutex = g_mutex_new();
962 #endif
963 volname_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
964 free_volname_key, free_volname_value);
965 }
966
967 static void
free_drive(gpointer drive)968 free_drive(gpointer drive)
969 {
970 #if defined(_WIN32)
971 g_free(drive);
972 #else
973 g_object_unref(drive);
974 #endif
975 }
976
977 gpointer
ghb_cache_volnames(signal_user_data_t * ud)978 ghb_cache_volnames(signal_user_data_t *ud)
979 {
980 GList *link, *drives;
981
982 g_debug("ghb_cache_volnames()");
983 link = drives = dvd_device_list();
984 if (drives == NULL)
985 return NULL;
986
987 g_mutex_lock(volname_mutex);
988 g_hash_table_remove_all(volname_hash);
989 while (link != NULL)
990 {
991 gchar *name, *drive;
992
993 #if !defined(_WIN32)
994 if (!g_drive_has_media (link->data))
995 {
996 g_object_unref(link->data);
997 link = link->next;
998 continue;
999 }
1000 #endif
1001 drive = get_dvd_device_name(link->data);
1002 name = get_direct_dvd_volume_name(drive);
1003
1004 if (drive != NULL && name != NULL)
1005 {
1006 g_hash_table_insert(volname_hash, drive, name);
1007 }
1008 else
1009 {
1010 if (drive != NULL)
1011 g_free(drive);
1012 if (name != NULL)
1013 g_free(name);
1014 }
1015
1016 free_drive(link->data);
1017 link = link->next;
1018 }
1019 g_mutex_unlock(volname_mutex);
1020
1021 g_list_free(drives);
1022
1023 g_idle_add((GSourceFunc)ghb_file_menu_add_dvd, ud);
1024
1025 return NULL;
1026 }
1027
1028 static const gchar*
get_extension(signal_user_data_t * ud,GhbValue * settings)1029 get_extension(signal_user_data_t *ud, GhbValue *settings)
1030 {
1031 const char *mux_id;
1032 const hb_container_t *mux;
1033
1034 mux_id = ghb_dict_get_string(settings, "FileFormat");
1035 mux = ghb_lookup_container_by_name(mux_id);
1036
1037 if ((mux->format & HB_MUX_MASK_MP4) &&
1038 ghb_dict_get_bool(ud->prefs, "UseM4v"))
1039 {
1040 return "m4v";
1041 }
1042 return mux->default_extension;
1043 }
1044
1045 static gboolean
check_name_template(signal_user_data_t * ud,const char * str)1046 check_name_template(signal_user_data_t *ud, const char *str)
1047 {
1048 if (ghb_dict_get_bool(ud->prefs, "auto_name"))
1049 {
1050 const gchar *template;
1051
1052 template = ghb_dict_get_string(ud->prefs, "auto_name_template");
1053 if (strstr(template, str) != NULL)
1054 return TRUE;
1055 }
1056 return FALSE;
1057 }
1058
1059 typedef struct {
1060 const char *pattern;
1061 const char *format;
1062 } datemap;
1063
1064 static int
parse_datestring(const char * src,struct tm * tm)1065 parse_datestring(const char *src, struct tm *tm)
1066 {
1067 datemap ymdThmsZ = {"[0-9]{4}-[0-1]?[0-9]-[0-3]?[0-9]T[0-9]{2}:[0-9]{2}:[0-9]{2}Z", "%Y-%m-%dT%H:%M:%SZ"};
1068
1069 datemap maps[1] = { ymdThmsZ };
1070
1071 for (int i = 0; i < sizeof(maps); i++)
1072 {
1073 if (hb_validate_param_string(maps[i].pattern, src))
1074 {
1075 av_small_strptime(src, maps[i].format, tm);
1076 return 1;
1077 }
1078 }
1079 return 0;
1080 }
1081
1082 static char*
get_creation_date(const char * pattern,const char * metaValue,const char * file)1083 get_creation_date(const char *pattern, const char *metaValue, const char *file)
1084 {
1085 char date[11] = "";
1086 if (metaValue != NULL && strlen(metaValue) > 1)
1087 {
1088 struct tm tm;
1089 if (parse_datestring(metaValue, &tm))
1090 {
1091 strftime(date, 11, pattern, &tm);
1092 }
1093 }
1094 else
1095 {
1096 struct stat stbuf;
1097 if (g_stat(file, &stbuf) == 0){
1098 struct tm *tm;
1099 tm = localtime(&(stbuf.st_mtime));
1100 strftime(date, 11, pattern, tm);
1101 }
1102 }
1103 return strdup(date);
1104 }
1105
1106 static void
make_unique_dest(const gchar * dest_dir,GString * str,const gchar * extension)1107 make_unique_dest(const gchar *dest_dir, GString *str, const gchar * extension)
1108 {
1109 GString * uniq = g_string_new(str->str);
1110 int copy = 0;
1111
1112 g_string_printf(uniq, "%s/%s.%s", dest_dir, str->str, extension);
1113 while (g_file_test(uniq->str, G_FILE_TEST_EXISTS))
1114 {
1115 g_string_printf(uniq, "%s/%s (%d).%s", dest_dir, str->str, ++copy, extension);
1116 }
1117 if (copy)
1118 {
1119 g_string_append_printf(str, " (%d)", copy);
1120 }
1121 g_string_free(uniq, TRUE);
1122 }
1123
1124 static void
set_destination_settings(signal_user_data_t * ud,GhbValue * settings)1125 set_destination_settings(signal_user_data_t *ud, GhbValue *settings)
1126 {
1127 const gchar *extension, *dest_file, *dest_dir;
1128 gchar *filename;
1129
1130 extension = get_extension(ud, settings);
1131
1132 g_debug("set_destination_settings");
1133 dest_file = ghb_dict_get_string(ud->settings, "dest_file");
1134 if (dest_file == NULL)
1135 {
1136 // Initialize destination filename if it has no value yet.
1137 // If auto-naming is disabled, this will be the default filename.
1138 GString *str = g_string_new("");
1139 const gchar *vol_name;
1140 vol_name = ghb_dict_get_string(settings, "volume");
1141 g_string_append_printf(str, "%s", vol_name);
1142 g_string_append_printf(str, ".%s", extension);
1143 filename = g_string_free(str, FALSE);
1144 ghb_dict_set_string(settings, "dest_file", filename);
1145 g_free(filename);
1146 }
1147 ghb_dict_set(settings, "dest_dir", ghb_value_dup(
1148 ghb_dict_get_value(ud->prefs, "destination_dir")));
1149 if (ghb_dict_get_bool(ud->prefs, "auto_name"))
1150 {
1151 GString *str = g_string_new("");
1152 const gchar *p;
1153
1154 p = ghb_dict_get_string(ud->prefs, "auto_name_template");
1155 // {source-path} is only allowed as the first element of the
1156 // template since the path must come first in the filename
1157 if (p != NULL &&
1158 (!strncasecmp(p, "{source-path}", strlen("{source-path}")) ||
1159 !strncasecmp(p, "{source_path}", strlen("{source_path}"))))
1160 {
1161 const gchar * source;
1162
1163 source = ghb_dict_get_string(ud->globals, "scan_source");
1164 if (source != NULL)
1165 {
1166 char * dirname = g_path_get_dirname(source);
1167 // if dirname is a directory and it is writable...
1168 if (dirname != NULL &&
1169 g_file_test(dirname, G_FILE_TEST_IS_DIR) &&
1170 access(dirname, W_OK) == 0)
1171 {
1172 ghb_dict_set_string(settings, "dest_dir", dirname);
1173 }
1174 g_free(dirname);
1175 }
1176 p += strlen("{source-path}");
1177 }
1178 while (*p)
1179 {
1180 if (!strncasecmp(p, "{source}", strlen("{source}")))
1181 {
1182 const gchar *vol_name;
1183 vol_name = ghb_dict_get_string(settings, "volume");
1184 g_string_append_printf(str, "%s", vol_name);
1185 p += strlen("{source}");
1186 }
1187 else if (!strncasecmp(p, "{title}", strlen("{title}")))
1188 {
1189 gint title = ghb_dict_get_int(settings, "title");
1190 if (title >= 0)
1191 g_string_append_printf(str, "%d", title);
1192 p += strlen("{title}");
1193 }
1194 else if (!strncasecmp(p, "{preset}", strlen("{preset}")))
1195 {
1196 const gchar *preset_name;
1197 preset_name = ghb_dict_get_string(settings, "PresetName");
1198 g_string_append_printf(str, "%s", preset_name);
1199 p += strlen("{preset}");
1200 }
1201 else if (!strncasecmp(p, "{chapters}", strlen("{chapters}")))
1202 {
1203 if (ghb_settings_combo_int(settings, "PtoPType") == 0)
1204 {
1205 gint start, end;
1206 start = ghb_dict_get_int(settings, "start_point");
1207 end = ghb_dict_get_int(settings, "end_point");
1208 if (start == end)
1209 g_string_append_printf(str, "%d", start);
1210 else
1211 g_string_append_printf(str, "%d-%d", start, end);
1212 }
1213 p += strlen("{chapters}");
1214 }
1215 else if (!strncasecmp(p, "{time}", strlen("{time}")))
1216 {
1217 char st[6];
1218 struct tm *lt;
1219 time_t t = time(NULL);
1220 lt = localtime(&t);
1221 st[0] = 0;
1222 strftime(st, 6, "%H:%M", lt);
1223 g_string_append_printf(str, "%s", st);
1224 p += strlen("{time}");
1225 }
1226 else if (!strncasecmp(p, "{date}", strlen("{date}")))
1227 {
1228 char dt[11];
1229 struct tm *lt;
1230 time_t t = time(NULL);
1231 lt = localtime(&t);
1232 dt[0] = 0;
1233 strftime(dt, 11, "%Y-%m-%d", lt);
1234 g_string_append_printf(str, "%s", dt);
1235 p += strlen("{date}");
1236 }
1237 else if (!strncasecmp(p, "{creation-date}", strlen("{creation-date}")))
1238 {
1239 gchar *val;
1240 const gchar *source = ghb_dict_get_string(ud->globals, "scan_source");
1241 val = get_creation_date("%Y-%m-%d", ghb_dict_get_string(settings, "MetaReleaseDate"), source);
1242 g_string_append_printf(str, "%s", val);
1243 p += strlen("{creation-date}");
1244 g_free(val);
1245 }
1246 else if (!strncasecmp(p, "{creation-time}", strlen("{creation-time}")))
1247 {
1248 gchar *val;
1249 const gchar *source = ghb_dict_get_string(ud->globals, "scan_source");
1250 val = get_creation_date("%H:%M", ghb_dict_get_string(settings, "MetaReleaseDate"), source);
1251 g_string_append_printf(str, "%s", val);
1252 p += strlen("{creation-time}");
1253 g_free(val);
1254 }
1255 else if (!strncasecmp(p, "{quality}", strlen("{quality}")))
1256 {
1257 if (ghb_dict_get_bool(settings, "vquality_type_constant"))
1258 {
1259 gint vcodec;
1260 const char *vqname;
1261 double vquality;
1262 vcodec = ghb_settings_video_encoder_codec(settings, "VideoEncoder");
1263 vqname = hb_video_quality_get_name(vcodec);
1264 vquality = ghb_dict_get_double(settings, "VideoQualitySlider");
1265 g_string_append_printf(str, "%s%.3g", vqname, vquality);
1266 }
1267 p += strlen("{quality}");
1268 }
1269 else if (!strncasecmp(p, "{bitrate}", strlen("{bitrate}")))
1270 {
1271 if (ghb_dict_get_bool(settings, "vquality_type_bitrate"))
1272 {
1273 int vbitrate;
1274 vbitrate = ghb_dict_get_int(settings, "VideoAvgBitrate");
1275 g_string_append_printf(str, "%dkbps", vbitrate);
1276 }
1277 p += strlen("{bitrate}");
1278 }
1279 else
1280 {
1281 g_string_append_printf(str, "%c", *p);
1282 p++;
1283 }
1284 }
1285 dest_dir = ghb_dict_get_string(settings, "dest_dir");
1286 make_unique_dest(dest_dir, str, extension);
1287 g_string_append_printf(str, ".%s", extension);
1288 filename = g_string_free(str, FALSE);
1289 ghb_dict_set_string(settings, "dest_file", filename);
1290 g_free(filename);
1291 }
1292 }
1293
1294 static void
set_destination(signal_user_data_t * ud)1295 set_destination(signal_user_data_t *ud)
1296 {
1297 set_destination_settings(ud, ud->settings);
1298 ghb_ui_update(ud, "dest_file",
1299 ghb_dict_get_value(ud->settings, "dest_file"));
1300 }
1301
1302 G_MODULE_EXPORT void
chooser_file_selected_cb(GtkFileChooser * dialog,signal_user_data_t * ud)1303 chooser_file_selected_cb(GtkFileChooser *dialog, signal_user_data_t *ud)
1304 {
1305 gchar *name = gtk_file_chooser_get_filename (dialog);
1306 GtkTreeModel *store;
1307 GtkTreeIter iter;
1308 const gchar *device;
1309 gboolean foundit = FALSE;
1310 GtkComboBox *combo;
1311
1312 g_debug("chooser_file_selected_cb ()");
1313
1314 if (name == NULL) return;
1315 combo = GTK_COMBO_BOX(GHB_WIDGET(ud->builder, "source_device"));
1316 store = gtk_combo_box_get_model(combo);
1317 if (gtk_tree_model_get_iter_first(store, &iter))
1318 {
1319 do
1320 {
1321 gtk_tree_model_get(store, &iter, 0, &device, -1);
1322 if (strcmp(name, device) == 0)
1323 {
1324 foundit = TRUE;
1325 break;
1326 }
1327 } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter));
1328 }
1329 if (foundit)
1330 gtk_combo_box_set_active_iter (combo, &iter);
1331 else
1332 gtk_combo_box_set_active (combo, 0);
1333
1334 g_free(name);
1335 }
1336
1337 G_MODULE_EXPORT void
dvd_device_changed_cb(GtkComboBoxText * combo,signal_user_data_t * ud)1338 dvd_device_changed_cb(GtkComboBoxText *combo, signal_user_data_t *ud)
1339 {
1340 GtkWidget *dialog;
1341 gint ii;
1342
1343 g_debug("dvd_device_changed_cb ()");
1344 ii = gtk_combo_box_get_active (GTK_COMBO_BOX(combo));
1345 if (ii > 0)
1346 {
1347 const gchar *device;
1348 gchar *name;
1349
1350 dialog = GHB_WIDGET(ud->builder, "source_dialog");
1351 device = gtk_combo_box_text_get_active_text(combo);
1352 // Protects against unexpected NULL return value
1353 if (device != NULL)
1354 {
1355 name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog));
1356 if (name == NULL || strcmp(name, device) != 0)
1357 gtk_file_chooser_select_filename (GTK_FILE_CHOOSER(dialog), device);
1358 if (name != NULL)
1359 g_free(name);
1360 }
1361 }
1362 }
1363
1364 static void
source_dialog_extra_widgets(signal_user_data_t * ud,GtkWidget * dialog)1365 source_dialog_extra_widgets(
1366 signal_user_data_t *ud,
1367 GtkWidget *dialog)
1368 {
1369 GtkComboBoxText *combo;
1370 GList *drives, *link;
1371
1372 g_debug("source_dialog_extra_widgets ()");
1373 combo = GTK_COMBO_BOX_TEXT(GHB_WIDGET(ud->builder, "source_device"));
1374 gtk_list_store_clear(GTK_LIST_STORE(
1375 gtk_combo_box_get_model(GTK_COMBO_BOX(combo))));
1376
1377 link = drives = dvd_device_list();
1378 gtk_combo_box_text_append_text (combo, _("Not Selected"));
1379 while (link != NULL)
1380 {
1381 gchar *name = get_dvd_device_name(link->data);
1382 gtk_combo_box_text_append_text(combo, name);
1383 g_free(name);
1384 free_drive(link->data);
1385 link = link->next;
1386 }
1387 g_list_free(drives);
1388 gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
1389 }
1390
ghb_break_pts_duration(gint64 ptsDuration,gint * hh,gint * mm,gdouble * ss)1391 void ghb_break_pts_duration(gint64 ptsDuration, gint *hh, gint *mm, gdouble *ss)
1392 {
1393 *hh = ptsDuration / (90000 * 60 * 60);
1394 ptsDuration -= *hh * 90000 * 60 * 60;
1395 *mm = ptsDuration / (90000 * 60);
1396 ptsDuration -= *mm * 90000 * 60;
1397 *ss = (gdouble)ptsDuration / 90000;
1398 }
1399
ghb_break_duration(gint64 duration,gint * hh,gint * mm,gint * ss)1400 void ghb_break_duration(gint64 duration, gint *hh, gint *mm, gint *ss)
1401 {
1402 *hh = duration / (60*60);
1403 *mm = (duration / 60) % 60;
1404 *ss = duration % 60;
1405 }
1406
1407 gint64
ghb_title_range_get_duration(GhbValue * settings,const hb_title_t * title)1408 ghb_title_range_get_duration(GhbValue * settings, const hb_title_t * title)
1409 {
1410 gint64 start, end;
1411
1412 if (ghb_settings_combo_int(settings, "PtoPType") == 0)
1413 {
1414 start = ghb_dict_get_int(settings, "start_point");
1415 end = ghb_dict_get_int(settings, "end_point");
1416 return ghb_chapter_range_get_duration(title, start, end) / 90000;
1417 }
1418 else if (ghb_settings_combo_int(settings, "PtoPType") == 1)
1419 {
1420 start = ghb_dict_get_int(settings, "start_point");
1421 end = ghb_dict_get_int(settings, "end_point");
1422 return end - start;
1423 }
1424 else if (ghb_settings_combo_int(settings, "PtoPType") == 2)
1425 {
1426 if (title != NULL)
1427 {
1428 gint64 frames;
1429
1430 start = ghb_dict_get_int(settings, "start_point");
1431 end = ghb_dict_get_int(settings, "end_point");
1432 frames = end - start + 1;
1433 return frames * title->vrate.den / title->vrate.num;
1434 }
1435 else
1436 {
1437 return 0;
1438 }
1439 }
1440 return 0;
1441 }
1442
1443 static void
update_title_duration(signal_user_data_t * ud)1444 update_title_duration(signal_user_data_t *ud)
1445 {
1446 gint hh, mm, ss;
1447 gint64 duration;
1448 gchar *text;
1449 GtkWidget *widget;
1450 int title_id, titleindex;
1451 const hb_title_t *title;
1452
1453 title_id = ghb_dict_get_int(ud->settings, "title");
1454 title = ghb_lookup_title(title_id, &titleindex);
1455 widget = GHB_WIDGET (ud->builder, "title_duration");
1456
1457 duration = ghb_title_range_get_duration(ud->settings, title);
1458 ghb_break_duration(duration, &hh, &mm, &ss);
1459
1460 text = g_strdup_printf("%02d:%02d:%02d", hh, mm, ss);
1461 gtk_label_set_text(GTK_LABEL(widget), text);
1462 g_free(text);
1463 }
1464
ghb_show_container_options(signal_user_data_t * ud)1465 void ghb_show_container_options(signal_user_data_t *ud)
1466 {
1467 GtkWidget *w1, *w2, *w3;
1468 w1 = GHB_WIDGET(ud->builder, "AlignAVStart");
1469 w2 = GHB_WIDGET(ud->builder, "Mp4HttpOptimize");
1470 w3 = GHB_WIDGET(ud->builder, "Mp4iPodCompatible");
1471
1472 const char *mux_id;
1473 const hb_container_t *mux;
1474
1475 mux_id = ghb_dict_get_string(ud->settings, "FileFormat");
1476 mux = ghb_lookup_container_by_name(mux_id);
1477
1478 gint enc = ghb_settings_video_encoder_codec(ud->settings, "VideoEncoder");
1479
1480 gtk_widget_set_visible(w1, (mux->format & HB_MUX_MASK_MP4));
1481 gtk_widget_set_visible(w2, (mux->format & HB_MUX_MASK_MP4));
1482 gtk_widget_set_visible(w3, (mux->format & HB_MUX_MASK_MP4) &&
1483 (enc == HB_VCODEC_X264_8BIT));
1484 }
1485
1486 static void
adjustment_configure(GtkAdjustment * adj,double val,double min,double max,double step,double page,double page_sz)1487 adjustment_configure(
1488 GtkAdjustment *adj,
1489 double val,
1490 double min, double max,
1491 double step, double page, double page_sz)
1492 {
1493 gtk_adjustment_configure(adj, val, min, max, step, page, page_sz);
1494 }
1495
1496 static void
spin_configure(signal_user_data_t * ud,char * name,double val,double min,double max)1497 spin_configure(signal_user_data_t *ud, char *name, double val, double min, double max)
1498 {
1499 GtkSpinButton *spin;
1500 GtkAdjustment *adj;
1501 double step, page, page_sz;
1502
1503 spin = GTK_SPIN_BUTTON(GHB_WIDGET(ud->builder, name));
1504
1505 adj = gtk_spin_button_get_adjustment(spin);
1506 step = gtk_adjustment_get_step_increment(adj);
1507 page = gtk_adjustment_get_page_increment(adj);
1508 page_sz = gtk_adjustment_get_page_size(adj);
1509
1510 adjustment_configure(adj, val, min, max, step, page, page_sz);
1511 }
1512
1513 void
ghb_scale_configure(signal_user_data_t * ud,char * name,double val,double min,double max,double step,double page,int digits,gboolean inverted)1514 ghb_scale_configure(
1515 signal_user_data_t *ud,
1516 char *name,
1517 double val, double min, double max,
1518 double step, double page,
1519 int digits, gboolean inverted)
1520 {
1521 GtkScale *scale;
1522 GtkAdjustment *adj;
1523 double page_sz;
1524
1525 scale = GTK_SCALE(GHB_WIDGET(ud->builder, name));
1526
1527 gtk_scale_set_draw_value(scale, FALSE);
1528 adj = gtk_range_get_adjustment(GTK_RANGE(scale));
1529 page_sz = gtk_adjustment_get_page_size(adj);
1530
1531 adjustment_configure(adj, val, min, max, step, page, page_sz);
1532
1533 gtk_scale_set_digits(scale, digits);
1534 gtk_range_set_inverted(GTK_RANGE(scale), inverted);
1535 gtk_scale_set_draw_value(scale, TRUE);
1536 }
1537
1538 void
ghb_set_widget_ranges(signal_user_data_t * ud,GhbValue * settings)1539 ghb_set_widget_ranges(signal_user_data_t *ud, GhbValue *settings)
1540 {
1541 int title_id, titleindex;
1542 const hb_title_t * title;
1543 double val;
1544
1545 title_id = ghb_dict_get_int(settings, "title");
1546 title = ghb_lookup_title(title_id, &titleindex);
1547
1548 // Reconfigure the UI combo boxes
1549 ghb_update_ui_combo_box(ud, "AudioTrack", title, FALSE);
1550 ghb_update_ui_combo_box(ud, "SubtitleTrack", title, FALSE);
1551
1552 if (title != NULL)
1553 {
1554
1555 // Set the limits of cropping. hb_set_anamorphic_size crashes if
1556 // you pass it a cropped width or height == 0.
1557 gint vbound, hbound;
1558 vbound = title->geometry.height;
1559 hbound = title->geometry.width;
1560
1561 val = ghb_dict_get_int(ud->settings, "PictureTopCrop");
1562 spin_configure(ud, "PictureTopCrop", val, 0, vbound);
1563 val = ghb_dict_get_int(ud->settings, "PictureBottomCrop");
1564 spin_configure(ud, "PictureBottomCrop", val, 0, vbound);
1565 val = ghb_dict_get_int(ud->settings, "PictureLeftCrop");
1566 spin_configure(ud, "PictureLeftCrop", val, 0, hbound);
1567 val = ghb_dict_get_int(ud->settings, "PictureRightCrop");
1568 spin_configure(ud, "PictureRightCrop", val, 0, hbound);
1569
1570 gint duration = title->duration / 90000;
1571
1572 if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0)
1573 {
1574 gint num_chapters = hb_list_count(title->list_chapter);
1575
1576 val = ghb_dict_get_int(ud->settings, "start_point");
1577 spin_configure(ud, "start_point", val, 1, num_chapters);
1578 val = ghb_dict_get_int(ud->settings, "end_point");
1579 spin_configure(ud, "end_point", val, 1, num_chapters);
1580 }
1581 else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1)
1582 {
1583 val = ghb_dict_get_int(ud->settings, "start_point");
1584 spin_configure(ud, "start_point", val, 0, duration * 2 - 1);
1585 val = ghb_dict_get_int(ud->settings, "end_point");
1586 spin_configure(ud, "end_point", val, 0, duration * 2);
1587 }
1588 else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 2)
1589 {
1590 gdouble max_frames;
1591 max_frames = (gdouble)duration *
1592 title->vrate.num / title->vrate.den;
1593
1594 val = ghb_dict_get_int(ud->settings, "start_point");
1595 spin_configure(ud, "start_point", val, 1, max_frames * 2);
1596 val = ghb_dict_get_int(ud->settings, "end_point");
1597 spin_configure(ud, "end_point", val, 1, max_frames * 2);
1598 }
1599
1600 val = ghb_dict_get_int(ud->settings, "angle");
1601 spin_configure(ud, "angle", val, 1, title->angle_count);
1602 }
1603
1604 float vqmin, vqmax, step, page;
1605 int inverted, digits;
1606
1607 ghb_vquality_range(ud, &vqmin, &vqmax, &step, &page, &digits, &inverted);
1608 val = ghb_dict_get_double(ud->settings, "VideoQualitySlider");
1609 ghb_scale_configure(ud, "VideoQualitySlider", val, vqmin, vqmax,
1610 step, page, digits, inverted);
1611 }
1612
1613 static void
check_chapter_markers(signal_user_data_t * ud)1614 check_chapter_markers(signal_user_data_t *ud)
1615 {
1616 GtkWidget *widget;
1617 gint start, end;
1618
1619 if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0)
1620 {
1621 start = ghb_dict_get_int(ud->settings, "start_point");
1622 end = ghb_dict_get_int(ud->settings, "end_point");
1623 widget = GHB_WIDGET (ud->builder, "ChapterMarkers");
1624 gtk_widget_set_sensitive(widget, end > start);
1625 }
1626 }
1627
1628 void
ghb_load_settings(signal_user_data_t * ud)1629 ghb_load_settings(signal_user_data_t * ud)
1630 {
1631 const char * fullname;
1632 int type;
1633 gboolean preset_modified;
1634 static gboolean busy = FALSE;
1635
1636 if (busy)
1637 return;
1638 busy = TRUE;
1639
1640 fullname = ghb_dict_get_string(ud->settings, "PresetFullName");
1641 type = ghb_dict_get_int(ud->settings, "Type");
1642 preset_modified = ghb_dict_get_bool(ud->settings, "preset_modified");
1643 if (preset_modified)
1644 {
1645 ghb_clear_presets_selection(ud);
1646 ghb_preset_menu_button_refresh(ud, fullname, type);
1647 }
1648 else
1649 {
1650 ghb_dict_set_bool(ud->settings, "preset_reload", TRUE);
1651 ghb_select_preset(ud, fullname, type);
1652 ghb_dict_set_bool(ud->settings, "preset_reload", FALSE);
1653 }
1654
1655 busy = FALSE;
1656
1657 ghb_load_post_settings(ud);
1658 }
1659
1660 void
ghb_load_post_settings(signal_user_data_t * ud)1661 ghb_load_post_settings(signal_user_data_t * ud)
1662 {
1663 static gboolean busy = FALSE;
1664 if (busy)
1665 return;
1666 busy = TRUE;
1667
1668 ud->dont_clear_presets = TRUE;
1669 ud->scale_busy = TRUE;
1670
1671 ghb_set_widget_ranges(ud, ud->settings);
1672 ghb_check_all_dependencies(ud);
1673 ghb_show_container_options(ud);
1674 check_chapter_markers(ud);
1675
1676 ghb_clear_audio_selection(ud->builder);
1677 ghb_clear_subtitle_selection(ud->builder);
1678 ghb_settings_to_ui(ud, ud->settings);
1679 ghb_audio_defaults_to_ui(ud);
1680 ghb_subtitle_defaults_to_ui(ud);
1681 ghb_audio_list_refresh_all(ud);
1682 ghb_subtitle_list_refresh_all(ud);
1683 ghb_chapter_list_refresh_all(ud);
1684 update_title_duration(ud);
1685 ghb_update_title_info(ud);
1686
1687 ud->dont_clear_presets = FALSE;
1688 ud->scale_busy = FALSE;
1689 busy = FALSE;
1690
1691 ghb_picture_settings_deps(ud);
1692 }
1693
1694 static void
show_scan_progress(signal_user_data_t * ud)1695 show_scan_progress(signal_user_data_t *ud)
1696 {
1697 GtkWidget * widget;
1698 GtkProgressBar * progress;
1699 GtkLabel * label;
1700
1701 widget = GHB_WIDGET(ud->builder, "SourceInfoBox");
1702 gtk_widget_hide(widget);
1703
1704 widget = GHB_WIDGET(ud->builder, "SourceScanBox");
1705 gtk_widget_show(widget);
1706
1707 progress = GTK_PROGRESS_BAR(GHB_WIDGET(ud->builder, "scan_prog"));
1708 gtk_progress_bar_set_fraction(progress, 0);
1709
1710 label = GTK_LABEL(GHB_WIDGET(ud->builder, "source_scan_label"));
1711 gtk_label_set_text( label, _("Scanning ...") );
1712
1713 }
1714
1715 static void
hide_scan_progress(signal_user_data_t * ud)1716 hide_scan_progress(signal_user_data_t *ud)
1717 {
1718 GtkWidget * widget;
1719 GtkProgressBar * progress;
1720
1721 progress = GTK_PROGRESS_BAR(GHB_WIDGET(ud->builder, "scan_prog"));
1722 gtk_progress_bar_set_fraction(progress, 1.0);
1723
1724 widget = GHB_WIDGET(ud->builder, "SourceScanBox");
1725 gtk_widget_hide(widget);
1726
1727 widget = GHB_WIDGET(ud->builder, "SourceInfoBox");
1728 gtk_widget_show(widget);
1729 }
1730
1731 static void
start_scan(signal_user_data_t * ud,const gchar * path,gint title_id,gint preview_count)1732 start_scan(
1733 signal_user_data_t *ud,
1734 const gchar *path,
1735 gint title_id,
1736 gint preview_count)
1737 {
1738 GtkWidget *widget;
1739 ghb_status_t status;
1740
1741 ghb_get_status(&status);
1742 if (status.scan.state != GHB_STATE_IDLE)
1743 return;
1744
1745 widget = GHB_WIDGET(ud->builder, "sourcetoolbutton");
1746 gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(widget), "hb-stop");
1747 gtk_tool_button_set_label(GTK_TOOL_BUTTON(widget), _("Stop Scan"));
1748 gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(widget), _("Stop Scan"));
1749
1750 widget = GHB_WIDGET(ud->builder, "source_open");
1751 gtk_widget_set_sensitive(widget, FALSE);
1752 widget = GHB_WIDGET(ud->builder, "source_title_open");
1753 gtk_widget_set_sensitive(widget, FALSE);
1754 ghb_backend_scan(path, title_id, preview_count,
1755 90000L * ghb_dict_get_int(ud->prefs, "MinTitleDuration"));
1756 }
1757
1758 gboolean
ghb_idle_scan(signal_user_data_t * ud)1759 ghb_idle_scan(signal_user_data_t *ud)
1760 {
1761 gchar *path;
1762 // ghb_do_scan replaces "scan_source" key in dict, so we must
1763 // make a copy of the string.
1764 path = g_strdup(ghb_dict_get_string(ud->globals, "scan_source"));
1765 ghb_do_scan(ud, path, 0, TRUE);
1766 g_free(path);
1767 return FALSE;
1768 }
1769
1770 extern GhbValue *ghb_queue_edit_settings;
1771 static gchar *last_scan_file = NULL;
1772
1773 void
ghb_do_scan(signal_user_data_t * ud,const gchar * filename,gint title_id,gboolean force)1774 ghb_do_scan(
1775 signal_user_data_t *ud,
1776 const gchar *filename,
1777 gint title_id,
1778 gboolean force)
1779 {
1780 int titleindex;
1781 const hb_title_t *title;
1782
1783 (void)title; // Silence "unused variable" warning
1784
1785 g_debug("ghb_do_scan()");
1786 if (!force && last_scan_file != NULL &&
1787 strcmp(last_scan_file, filename) == 0)
1788 {
1789 if (ghb_queue_edit_settings != NULL)
1790 {
1791 title_id = ghb_dict_get_int(ghb_queue_edit_settings, "title");
1792 title = ghb_lookup_title(title_id, &titleindex);
1793 ghb_array_replace(ud->settings_array, titleindex,
1794 ghb_queue_edit_settings);
1795 ud->settings = ghb_queue_edit_settings;
1796 ghb_load_settings(ud);
1797 ghb_queue_edit_settings = NULL;
1798 }
1799 else
1800 {
1801 title = ghb_lookup_title(title_id, &titleindex);
1802 load_all_titles(ud, titleindex);
1803 }
1804 return;
1805 }
1806 if (last_scan_file != NULL)
1807 g_free(last_scan_file);
1808 last_scan_file = NULL;
1809 if (filename != NULL)
1810 {
1811 const gchar *path;
1812 gint preview_count;
1813
1814 last_scan_file = g_strdup(filename);
1815 ghb_dict_set_string(ud->globals, "scan_source", filename);
1816
1817 show_scan_progress(ud);
1818 path = ghb_dict_get_string(ud->globals, "scan_source");
1819 prune_logs(ud);
1820
1821 preview_count = ghb_dict_get_int(ud->prefs, "preview_count");
1822 start_scan(ud, path, title_id, preview_count);
1823 }
1824 }
1825
1826 static void
do_source_dialog(gboolean single,signal_user_data_t * ud)1827 do_source_dialog(gboolean single, signal_user_data_t *ud)
1828 {
1829 GtkWidget *dialog;
1830 const gchar *sourcename;
1831 gint response;
1832
1833 g_debug("source_browse_clicked_cb ()");
1834 sourcename = ghb_dict_get_string(ud->globals, "scan_source");
1835 GtkWidget *widget;
1836 widget = GHB_WIDGET(ud->builder, "single_title_box");
1837 if (single)
1838 gtk_widget_show(widget);
1839 else
1840 gtk_widget_hide(widget);
1841 dialog = GHB_WIDGET(ud->builder, "source_dialog");
1842 source_dialog_extra_widgets(ud, dialog);
1843
1844 gtk_widget_show(dialog);
1845 gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(dialog), sourcename);
1846
1847 response = gtk_dialog_run(GTK_DIALOG (dialog));
1848 gtk_widget_hide(dialog);
1849 if (response == GTK_RESPONSE_NO)
1850 {
1851 gchar *filename;
1852
1853 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
1854 if (filename != NULL)
1855 {
1856 gint title_id;
1857
1858 if (single)
1859 title_id = ghb_dict_get_int(ud->settings, "single_title");
1860 else
1861 title_id = 0;
1862 // ghb_do_scan replaces "scan_source" key in dict, so we must
1863 // be finished with sourcename before calling ghb_do_scan
1864 // since the memory it references will be freed
1865 if (strcmp(sourcename, filename) != 0)
1866 {
1867 ghb_dict_set_string(ud->prefs, "default_source", filename);
1868 ghb_pref_save(ud->prefs, "default_source");
1869 ghb_dvd_set_current(filename, ud);
1870 }
1871 ghb_do_scan(ud, filename, title_id, TRUE);
1872 g_free(filename);
1873 }
1874 }
1875 }
1876
1877 #if 0
1878 G_MODULE_EXPORT void
1879 source_button_clicked_cb(GtkButton *button, signal_user_data_t *ud)
1880 {
1881 ghb_status_t status;
1882 ghb_get_status(&status);
1883 if (status.scan.state & GHB_STATE_SCANNING)
1884 {
1885 ghb_backend_scan_stop();
1886 }
1887 else
1888 {
1889 do_source_dialog(FALSE, ud);
1890 }
1891 }
1892 #else
1893 G_MODULE_EXPORT void
source_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)1894 source_action_cb(GSimpleAction *action, GVariant *param,
1895 signal_user_data_t *ud)
1896 {
1897 ghb_status_t status;
1898 ghb_get_status(&status);
1899 if (status.scan.state & GHB_STATE_SCANNING)
1900 {
1901 ghb_backend_scan_stop();
1902 }
1903 else
1904 {
1905 do_source_dialog(FALSE, ud);
1906 }
1907 }
1908 #endif
1909
1910 G_MODULE_EXPORT void
single_title_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)1911 single_title_action_cb(GSimpleAction *action, GVariant * param,
1912 signal_user_data_t *ud)
1913 {
1914 do_source_dialog(TRUE, ud);
1915 }
1916
1917 G_MODULE_EXPORT void
dvd_source_activate_cb(GtkWidget * widget,signal_user_data_t * ud)1918 dvd_source_activate_cb(GtkWidget *widget, signal_user_data_t *ud)
1919 {
1920 const gchar *filename;
1921 const gchar *sourcename;
1922
1923 // ghb_do_scan replaces "scan_source" key in dict, so we must
1924 // be finished with sourcename before calling ghb_do_scan
1925 // since the memory it references will be freed
1926 sourcename = ghb_dict_get_string(ud->globals, "scan_source");
1927 filename = gtk_buildable_get_name(GTK_BUILDABLE(widget));
1928 if (strcmp(sourcename, filename) != 0)
1929 {
1930 ghb_dict_set_string(ud->prefs, "default_source", filename);
1931 ghb_pref_save(ud->prefs, "default_source");
1932 ghb_dvd_set_current(filename, ud);
1933 }
1934 ghb_do_scan(ud, filename, 0, TRUE);
1935 }
1936
1937 void
ghb_update_destination_extension(signal_user_data_t * ud)1938 ghb_update_destination_extension(signal_user_data_t *ud)
1939 {
1940 static gchar *containers[] = {".mkv", ".mp4", ".m4v", ".webm", ".error", NULL};
1941 gchar *filename;
1942 const gchar *extension;
1943 gint ii;
1944 GtkEntry *entry;
1945 static gboolean busy = FALSE;
1946
1947 g_debug("ghb_update_destination_extension ()");
1948 // Since this function modifies the thing that triggers it's
1949 // invocation, check to see if busy to prevent accidental infinite
1950 // recursion.
1951 if (busy)
1952 return;
1953 busy = TRUE;
1954 extension = get_extension(ud, ud->settings);
1955 entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "dest_file"));
1956 filename = g_strdup(ghb_editable_get_text(entry));
1957 for (ii = 0; containers[ii] != NULL; ii++)
1958 {
1959 if (g_str_has_suffix(filename, containers[ii]))
1960 {
1961 gchar *pos;
1962 gchar *new_name;
1963
1964 pos = g_strrstr( filename, "." );
1965 if (pos == NULL)
1966 {
1967 // No period? shouldn't happen
1968 break;
1969 }
1970 *pos = 0;
1971 if (strcmp(extension, &pos[1]) == 0)
1972 {
1973 // Extension is already correct
1974 break;
1975 }
1976 new_name = g_strjoin(".", filename, extension, NULL);
1977 ghb_ui_update(ud, "dest_file", ghb_string_value(new_name));
1978 g_free(new_name);
1979 break;
1980 }
1981 }
1982 g_free(filename);
1983 busy = FALSE;
1984 }
1985
1986 static void
destination_select_title(GtkEntry * entry)1987 destination_select_title(GtkEntry *entry)
1988 {
1989 const gchar *dest;
1990 gint start, end;
1991
1992 dest = ghb_editable_get_text(entry);
1993 for (end = strlen(dest)-1; end > 0; end--)
1994 {
1995 if (dest[end] == '.')
1996 {
1997 break;
1998 }
1999 }
2000 for (start = end; start >= 0; start--)
2001 {
2002 if (dest[start] == G_DIR_SEPARATOR)
2003 {
2004 start++;
2005 break;
2006 }
2007 }
2008 if (start < 0) start = 0;
2009 if (start < end)
2010 {
2011 gtk_editable_select_region(GTK_EDITABLE(entry), start, end);
2012 }
2013 }
2014
2015 G_MODULE_EXPORT gboolean
destination_grab_cb(GtkEntry * entry,signal_user_data_t * ud)2016 destination_grab_cb(
2017 GtkEntry *entry,
2018 signal_user_data_t *ud)
2019 {
2020 destination_select_title(entry);
2021 return FALSE;
2022 }
2023
2024 static void
update_default_destination(signal_user_data_t * ud)2025 update_default_destination(signal_user_data_t *ud)
2026 {
2027 const gchar *dest_dir, *def_dest;
2028
2029 dest_dir = ghb_dict_get_string(ud->settings, "dest_dir");
2030 def_dest = ghb_dict_get_string(ud->prefs, "destination_dir");
2031 if (dest_dir != NULL && def_dest != NULL && dest_dir[0] != 0 &&
2032 strcmp(dest_dir, def_dest) != 0)
2033 {
2034 ghb_dict_set_string(ud->prefs, "destination_dir", dest_dir);
2035 ghb_pref_save(ud->prefs, "destination_dir");
2036 }
2037 }
2038
2039 G_MODULE_EXPORT void
dest_dir_set_cb(GtkFileChooserButton * dest_chooser,signal_user_data_t * ud)2040 dest_dir_set_cb(GtkFileChooserButton *dest_chooser, signal_user_data_t *ud)
2041 {
2042 const gchar *dest_file, *dest_dir;
2043 gchar *dest;
2044
2045 g_debug("dest_dir_set_cb ()");
2046 ghb_widget_to_setting(ud->settings, (GtkWidget*)dest_chooser);
2047 dest_file = ghb_dict_get_string(ud->settings, "dest_file");
2048 dest_dir = ghb_dict_get_string(ud->settings, "dest_dir");
2049 dest = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dest_dir, dest_file);
2050 ghb_dict_set_string(ud->settings, "destination", dest);
2051 GhbValue *dest_dict = ghb_get_job_dest_settings(ud->settings);
2052 ghb_dict_set_string(dest_dict, "File", dest);
2053 g_free(dest);
2054 update_default_destination(ud);
2055 }
2056
2057 G_MODULE_EXPORT void
dest_file_changed_cb(GtkEntry * entry,signal_user_data_t * ud)2058 dest_file_changed_cb(GtkEntry *entry, signal_user_data_t *ud)
2059 {
2060 const gchar *dest_file, *dest_dir;
2061 gchar *dest;
2062
2063 g_debug("dest_file_changed_cb ()");
2064 ghb_update_destination_extension(ud);
2065 ghb_widget_to_setting(ud->settings, (GtkWidget*)entry);
2066 // This signal goes off with ever keystroke, so I'm putting this
2067 // update on the timer.
2068 dest_file = ghb_dict_get_string(ud->settings, "dest_file");
2069 dest_dir = ghb_dict_get_string(ud->settings, "dest_dir");
2070 dest = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dest_dir, dest_file);
2071 ghb_dict_set_string(ud->settings, "destination", dest);
2072 GhbValue *dest_dict = ghb_get_job_dest_settings(ud->settings);
2073 ghb_dict_set_string(dest_dict, "File", dest);
2074 g_free(dest);
2075 }
2076
2077 G_MODULE_EXPORT void
destination_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)2078 destination_action_cb(GSimpleAction *action, GVariant *param,
2079 signal_user_data_t *ud)
2080 {
2081 GtkWidget *dialog;
2082 GtkEntry *entry;
2083 const gchar *destname;
2084 gchar *basename;
2085 GtkWindow *hb_window;
2086
2087 hb_window = GTK_WINDOW(GHB_WIDGET(ud->builder, "hb_window"));
2088 destname = ghb_dict_get_string(ud->settings, "destination");
2089 dialog = gtk_file_chooser_dialog_new("Choose Destination",
2090 hb_window,
2091 GTK_FILE_CHOOSER_ACTION_SAVE,
2092 GHB_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2093 GHB_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
2094 NULL);
2095 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), destname);
2096 basename = g_path_get_basename(destname);
2097 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), basename);
2098 g_free(basename);
2099 if (gtk_dialog_run(GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
2100 {
2101 char *filename, *dirname;
2102 GtkFileChooser *dest_chooser;
2103
2104 filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2105 basename = g_path_get_basename(filename);
2106 dirname = g_path_get_dirname(filename);
2107 entry = (GtkEntry*)GHB_WIDGET(ud->builder, "dest_file");
2108 ghb_editable_set_text(entry, basename);
2109 dest_chooser = GTK_FILE_CHOOSER(GHB_WIDGET(ud->builder, "dest_dir"));
2110 gtk_file_chooser_set_filename(dest_chooser, dirname);
2111 g_free (dirname);
2112 g_free (basename);
2113 g_free (filename);
2114 }
2115 gtk_widget_destroy(dialog);
2116 }
2117
2118 G_MODULE_EXPORT gboolean
window_destroy_event_cb(GtkWidget * widget,GdkEvent * event,signal_user_data_t * ud)2119 window_destroy_event_cb(
2120 GtkWidget *widget,
2121 #if !GTK_CHECK_VERSION(3, 90, 0)
2122 GdkEvent *event,
2123 #endif
2124 signal_user_data_t *ud)
2125 {
2126 ghb_hb_cleanup(FALSE);
2127 prune_logs(ud);
2128 g_application_quit(G_APPLICATION(ud->app));
2129 return FALSE;
2130 }
2131
2132 G_MODULE_EXPORT gboolean
window_delete_event_cb(GtkWidget * widget,GdkEvent * event,signal_user_data_t * ud)2133 window_delete_event_cb(
2134 GtkWidget *widget,
2135 #if !GTK_CHECK_VERSION(3, 90, 0)
2136 GdkEvent *event,
2137 #endif
2138 signal_user_data_t *ud)
2139 {
2140 gint state = ghb_get_queue_state();
2141 if (state & (GHB_STATE_WORKING|GHB_STATE_SEARCHING))
2142 {
2143 if (ghb_cancel_encode2(ud,
2144 _("Closing HandBrake will terminate encoding.\n")))
2145 {
2146 ghb_hb_cleanup(FALSE);
2147 prune_logs(ud);
2148 g_application_quit(G_APPLICATION(ud->app));
2149 return FALSE;
2150 }
2151 return TRUE;
2152 }
2153 ghb_hb_cleanup(FALSE);
2154 prune_logs(ud);
2155 g_application_quit(G_APPLICATION(ud->app));
2156 return FALSE;
2157 }
2158
2159 static void
update_acodec(signal_user_data_t * ud)2160 update_acodec(signal_user_data_t *ud)
2161 {
2162 ghb_audio_list_refresh_all(ud);
2163 ghb_grey_combo_options(ud);
2164 }
2165
2166 G_MODULE_EXPORT void
container_changed_cb(GtkWidget * widget,signal_user_data_t * ud)2167 container_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
2168 {
2169 g_debug("container_changed_cb ()");
2170 ghb_widget_to_setting(ud->settings, widget);
2171 const char * mux_id = ghb_dict_get_string(ud->settings, "FileFormat");
2172 GhbValue *dest_dict = ghb_get_job_dest_settings(ud->settings);
2173 ghb_dict_set_string(dest_dict, "Mux", mux_id);
2174
2175 const hb_container_t *mux = ghb_lookup_container_by_name(mux_id);
2176 if (!(mux->format & HB_MUX_MASK_MP4))
2177 {
2178 ghb_ui_update(ud, "AlignAVStart", ghb_boolean_value(FALSE));
2179 }
2180
2181 ghb_check_dependency(ud, widget, NULL);
2182 ghb_show_container_options(ud);
2183 update_acodec(ud);
2184 ghb_update_destination_extension(ud);
2185 ghb_clear_presets_selection(ud);
2186 ghb_live_reset(ud);
2187 ghb_subtitle_prune(ud);
2188 ghb_subtitle_list_refresh_all(ud);
2189 ghb_audio_list_refresh_selected(ud);
2190 }
2191
2192 static gchar*
get_aspect_string(gint aspect_n,gint aspect_d)2193 get_aspect_string(gint aspect_n, gint aspect_d)
2194 {
2195 gchar *aspect;
2196
2197 if (aspect_d < 10)
2198 {
2199 aspect = g_strdup_printf("%d:%d", aspect_n, aspect_d);
2200 }
2201 else
2202 {
2203 gdouble aspect_nf = (gdouble)aspect_n / aspect_d;
2204 aspect = g_strdup_printf("%.2f:1", aspect_nf);
2205 }
2206 return aspect;
2207 }
2208
2209 void
ghb_update_title_info(signal_user_data_t * ud)2210 ghb_update_title_info(signal_user_data_t *ud)
2211 {
2212 GtkWidget * widget;
2213 gchar * text;
2214 gchar * aspect;
2215 gchar * rate;
2216 int title_id, titleindex;
2217 int audio_count, subtitle_count;
2218 const hb_title_t * title;
2219 const hb_geometry_t * geo;
2220 gint aspect_n, aspect_d;
2221
2222 title_id = ghb_dict_get_int(ud->settings, "title");
2223 title = ghb_lookup_title(title_id, &titleindex);
2224 if (title == NULL)
2225 return;
2226
2227 update_title_duration(ud);
2228
2229 geo = &title->geometry;
2230 hb_reduce(&aspect_n, &aspect_d, geo->width * geo->par.num,
2231 geo->height * geo->par.den);
2232 aspect = get_aspect_string(aspect_n, aspect_d);
2233 rate = g_strdup_printf("%.6g", (gdouble)title->vrate.num /
2234 title->vrate.den);
2235 audio_count = hb_list_count(title->list_audio);
2236 subtitle_count = hb_list_count(title->list_subtitle);
2237
2238 text = g_strdup_printf(
2239 ", %dx%d (%dx%d), %s, %s FPS, %d Audio Track%s, %d Subtitle Track%s",
2240 geo->width, geo->height,
2241 geo->width * geo->par.num / geo->par.den, geo->height,
2242 aspect, rate,
2243 audio_count, audio_count == 1 ? "" : "s",
2244 subtitle_count, subtitle_count == 1 ? "" : "s");
2245
2246 widget = GHB_WIDGET(ud->builder, "source_info_label");
2247 gtk_label_set_text(GTK_LABEL(widget), text);
2248 free(text);
2249 free(rate);
2250
2251 text = g_strdup_printf("%d x %d", geo->width, geo->height);
2252 ghb_ui_update(ud, "source_storage_size", ghb_string_value(text));
2253 g_free(text);
2254
2255 text = g_strdup_printf("%d x %d", geo->width * geo->par.num / geo->par.den,
2256 geo->height);
2257 ghb_ui_update(ud, "source_display_size", ghb_string_value(text));
2258 g_free(text);
2259
2260 ghb_ui_update(ud, "source_aspect_ratio", ghb_string_value(aspect));
2261 free(aspect);
2262 }
2263
update_meta(GhbValue * settings,const char * name,const char * val)2264 static void update_meta(GhbValue *settings, const char *name, const char *val)
2265 {
2266 GhbValue *metadata = ghb_get_job_metadata_settings(settings);
2267
2268 if (val == NULL || val[0] == 0)
2269 ghb_dict_remove(metadata, name);
2270 else
2271 ghb_dict_set_string(metadata, name, val);
2272 }
2273
2274 void
ghb_update_summary_info(signal_user_data_t * ud)2275 ghb_update_summary_info(signal_user_data_t *ud)
2276 {
2277 GString * str;
2278 char * text;
2279 int title_id;
2280 GtkWidget * widget;
2281 GhbValue * titleDict;
2282
2283 title_id = ghb_dict_get_int(ud->settings, "title");
2284 titleDict = ghb_get_title_dict(title_id);
2285 if (titleDict == NULL)
2286 {
2287 // No title, clear summary
2288 widget = GHB_WIDGET(ud->builder, "tracks_summary");
2289 gtk_label_set_text(GTK_LABEL(widget), "");
2290 widget = GHB_WIDGET(ud->builder, "filters_summary");
2291 gtk_label_set_text(GTK_LABEL(widget), "");
2292 widget = GHB_WIDGET(ud->builder, "dimensions_summary");
2293 gtk_label_set_text(GTK_LABEL(widget), "--");
2294 widget = GHB_WIDGET(ud->builder, "summary_image");
2295 gtk_widget_show(widget);
2296 widget = GHB_WIDGET(ud->builder, "preview_button_image");
2297 gtk_widget_hide(widget);
2298 return;
2299 }
2300
2301 widget = GHB_WIDGET(ud->builder, "summary_image");
2302 gtk_widget_hide(widget);
2303 widget = GHB_WIDGET(ud->builder, "preview_button_image");
2304 gtk_widget_show(widget);
2305
2306 // Video Track
2307 const hb_encoder_t * video_encoder;
2308 const hb_rate_t * fps;
2309 hb_rational_t vrate;
2310 char * rate_str;
2311
2312 str = g_string_new("");
2313 video_encoder = ghb_settings_video_encoder(ud->settings, "VideoEncoder");
2314 fps = ghb_settings_video_framerate(ud->settings, "VideoFramerate");
2315 if (fps->rate == 0)
2316 {
2317 hb_dict_extract_rational(&vrate, titleDict, "FrameRate");
2318 }
2319 else
2320 {
2321 vrate.num = 27000000;
2322 vrate.den = fps->rate;
2323 }
2324 rate_str = g_strdup_printf("%.6g", (gdouble)vrate.num / vrate.den);
2325 g_string_append_printf(str, "%s, %s FPS", video_encoder->name, rate_str);
2326 g_free(rate_str);
2327 if (ghb_dict_get_bool(ud->settings, "VideoFramerateCFR"))
2328 {
2329 g_string_append_printf(str, " CFR");
2330 }
2331 else if (ghb_dict_get_bool(ud->settings, "VideoFrameratePFR"))
2332 {
2333 g_string_append_printf(str, " PFR");
2334 }
2335 else if (ghb_dict_get_bool(ud->settings, "VideoFramerateVFR"))
2336 {
2337 g_string_append_printf(str, " VFR");
2338 }
2339
2340 // Audio Tracks (show at most 3 tracks)
2341 GhbValue * audioList;
2342 GhbValue * sourceAudioList;
2343 int ii, count, show;
2344
2345 sourceAudioList = ghb_dict_get(titleDict, "AudioList");
2346 audioList = ghb_get_job_audio_list(ud->settings);
2347 show = count = ghb_array_len(audioList);
2348 if (count > 3)
2349 {
2350 show = 2;
2351 }
2352 for (ii = 0; ii < show; ii++)
2353 {
2354 GhbValue * asettings, * asource;
2355 const hb_mixdown_t * audio_mix;
2356 const hb_encoder_t * audio_encoder;
2357 const char * lang;
2358 int track;
2359
2360 asettings = ghb_array_get(audioList, ii);
2361 track = ghb_dict_get_int(asettings, "Track");
2362 asource = ghb_array_get(sourceAudioList, track);
2363 lang = ghb_dict_get_string(asource, "Language");
2364 audio_encoder = ghb_settings_audio_encoder(asettings, "Encoder");
2365 if (audio_encoder->codec & HB_ACODEC_PASS_FLAG)
2366 {
2367 g_string_append_printf(str, "\n%s, %s", lang, audio_encoder->name);
2368 }
2369 else
2370 {
2371 audio_mix = ghb_settings_mixdown(asettings, "Mixdown");
2372 g_string_append_printf(str, "\n%s, %s, %s", lang,
2373 audio_encoder->name, audio_mix->name);
2374 }
2375 }
2376 if (show < count)
2377 {
2378 g_string_append_printf(str, "\n+ %d more audio track%s", count - show,
2379 count - show > 1 ? "s" : "");
2380 }
2381
2382 // Subtitle Tracks (show at most 3 tracks)
2383 GhbValue * subtitleDict;
2384 GhbValue * searchDict;
2385 GhbValue * subtitleList;
2386 GhbValue * sourceSubtitleList;
2387 gboolean search;
2388
2389 sourceSubtitleList = ghb_dict_get(titleDict, "SubtitleList");
2390 subtitleDict = ghb_get_job_subtitle_settings(ud->settings);
2391 subtitleList = ghb_dict_get(subtitleDict, "SubtitleList");
2392 searchDict = ghb_dict_get(subtitleDict, "Search");
2393 search = ghb_dict_get_bool(searchDict, "Enable");
2394 show = count = ghb_array_len(subtitleList) + search;
2395 if (count > 3)
2396 {
2397 show = 2;
2398 }
2399 if (search)
2400 {
2401 gboolean force, burn, def;
2402
2403 force = ghb_dict_get_bool(searchDict, "Forced");
2404 burn = ghb_dict_get_bool(searchDict, "Burn");
2405 def = ghb_dict_get_bool(searchDict, "Default");
2406
2407 g_string_append_printf(str, "\nForeign Audio Scan");
2408 if (force)
2409 {
2410 g_string_append_printf(str, ", Forced Only");
2411 }
2412 if (burn)
2413 {
2414 g_string_append_printf(str, ", Burned");
2415 }
2416 else if (def)
2417 {
2418 g_string_append_printf(str, ", Default");
2419 }
2420 show--;
2421 count--;
2422 }
2423 for (ii = 0; ii < show; ii++)
2424 {
2425 GhbValue * subsettings, * subsource;
2426 int track;
2427 char * desc;
2428 gboolean force, burn, def;
2429
2430 subsettings = ghb_array_get(subtitleList, ii);
2431 track = ghb_dict_get_int(subsettings, "Track");
2432 subsource = ghb_array_get(sourceSubtitleList, track);
2433 desc = ghb_subtitle_short_description(subsource, subsettings);
2434 force = ghb_dict_get_bool(subsettings, "Forced");
2435 burn = ghb_dict_get_bool(subsettings, "Burn");
2436 def = ghb_dict_get_bool(subsettings, "Default");
2437
2438 g_string_append_printf(str, "\n%s", desc);
2439 free(desc);
2440 if (force)
2441 {
2442 g_string_append_printf(str, ", Forced Only");
2443 }
2444 if (burn)
2445 {
2446 g_string_append_printf(str, ", Burned");
2447 }
2448 else if (def)
2449 {
2450 g_string_append_printf(str, ", Default");
2451 }
2452 }
2453 if (show < count)
2454 {
2455 g_string_append_printf(str, "\n+ %d more subtitle track%s",
2456 count - show,
2457 count - show > 1 ? "s" : "");
2458 }
2459
2460 if (ghb_dict_get_bool(ud->settings, "ChapterMarkers"))
2461 {
2462 g_string_append_printf(str, "\nChapter Markers");
2463 }
2464
2465 text = g_string_free(str, FALSE);
2466 widget = GHB_WIDGET(ud->builder, "tracks_summary");
2467 gtk_label_set_text(GTK_LABEL(widget), text);
2468 g_free(text);
2469
2470 // Filters
2471 gboolean detel, comb_detect, deint, decomb, deblock, nlmeans, denoise;
2472 gboolean unsharp, lapsharp, hflip, rot, gray, colorspace, chroma_smooth;
2473 const char * sval;
2474
2475 sval = ghb_dict_get_string(ud->settings, "PictureDetelecine");
2476 detel = sval != NULL && !!strcasecmp(sval, "off");
2477 sval = ghb_dict_get_string(ud->settings, "PictureCombDetectPreset");
2478 comb_detect = sval != NULL && !!strcasecmp(sval, "off");
2479 sval = ghb_dict_get_string(ud->settings, "PictureDeinterlaceFilter");
2480 deint = sval != NULL && !strcasecmp(sval, "deinterlace");
2481 decomb = sval != NULL && !strcasecmp(sval, "decomb");
2482 sval = ghb_dict_get_string(ud->settings, "PictureDeblockPreset");
2483 deblock = sval != NULL && !!strcasecmp(sval, "off");
2484 sval = ghb_dict_get_string(ud->settings, "PictureDenoiseFilter");
2485 nlmeans = sval != NULL && !strcasecmp(sval, "nlmeans");
2486 denoise = sval != NULL && !strcasecmp(sval, "hqdn3d");
2487 sval = ghb_dict_get_string(ud->settings, "PictureSharpenFilter");
2488 unsharp = sval != NULL && !strcasecmp(sval, "unsharp");
2489 lapsharp = sval != NULL && !strcasecmp(sval, "lapsharp");
2490 hflip = ghb_dict_get_bool(ud->settings, "hflip");
2491 sval = ghb_dict_get_string(ud->settings, "rotate");
2492 rot = sval != NULL && !!strcasecmp(sval, "0");
2493 gray = ghb_dict_get_bool(ud->settings, "VideoGrayScale");
2494 sval = ghb_dict_get_string(ud->settings, "PictureColorspacePreset");
2495 colorspace = sval != NULL && !!strcasecmp(sval, "off");
2496 sval = ghb_dict_get_string(ud->settings, "PictureChromaSmoothPreset");
2497 chroma_smooth = sval != NULL && !!strcasecmp(sval, "off");
2498
2499 str = g_string_new("");
2500 sval = "";
2501 if (detel)
2502 {
2503 hb_filter_object_t * filter = hb_filter_get(HB_FILTER_DETELECINE);
2504 g_string_append_printf(str, "%s%s", sval, filter->name);
2505 sval = ", ";
2506 }
2507 if (comb_detect)
2508 {
2509 hb_filter_object_t * filter = hb_filter_get(HB_FILTER_COMB_DETECT);
2510 g_string_append_printf(str, "%s%s", sval, filter->name);
2511 sval = ", ";
2512 }
2513 if (deint)
2514 {
2515 hb_filter_object_t * filter = hb_filter_get(HB_FILTER_DEINTERLACE);
2516 g_string_append_printf(str, "%s%s", sval, filter->name);
2517 sval = ", ";
2518 }
2519 if (decomb)
2520 {
2521 hb_filter_object_t * filter = hb_filter_get(HB_FILTER_DECOMB);
2522 g_string_append_printf(str, "%s%s", sval, filter->name);
2523 sval = ", ";
2524 }
2525 if (deblock)
2526 {
2527 hb_filter_object_t * filter = hb_filter_get(HB_FILTER_DEBLOCK);
2528 g_string_append_printf(str, "%s%s", sval, filter->name);
2529 sval = ", ";
2530 }
2531 if (nlmeans)
2532 {
2533 hb_filter_object_t * filter = hb_filter_get(HB_FILTER_NLMEANS);
2534 g_string_append_printf(str, "%s%s", sval, filter->name);
2535 sval = ", ";
2536 }
2537 if (denoise)
2538 {
2539 hb_filter_object_t * filter = hb_filter_get(HB_FILTER_DENOISE);
2540 g_string_append_printf(str, "%s%s", sval, filter->name);
2541 sval = ", ";
2542 }
2543 if (chroma_smooth)
2544 {
2545 hb_filter_object_t * filter = hb_filter_get(HB_FILTER_CHROMA_SMOOTH);
2546 g_string_append_printf(str, "%s%s", sval, filter->name);
2547 sval = ", ";
2548 }
2549 if (unsharp)
2550 {
2551 hb_filter_object_t * filter = hb_filter_get(HB_FILTER_UNSHARP);
2552 g_string_append_printf(str, "%s%s", sval, filter->name);
2553 sval = ", ";
2554 }
2555 if (rot || hflip)
2556 {
2557 hb_filter_object_t * filter = hb_filter_get(HB_FILTER_ROTATE);
2558 g_string_append_printf(str, "%s%s", sval, filter->name);
2559 sval = ", ";
2560 }
2561 if (lapsharp)
2562 {
2563 hb_filter_object_t * filter = hb_filter_get(HB_FILTER_LAPSHARP);
2564 g_string_append_printf(str, "%s%s", sval, filter->name);
2565 sval = ", ";
2566 }
2567 if (gray)
2568 {
2569 hb_filter_object_t * filter = hb_filter_get(HB_FILTER_GRAYSCALE);
2570 g_string_append_printf(str, "%s%s", sval, filter->name);
2571 sval = ", ";
2572 }
2573 if (colorspace)
2574 {
2575 hb_filter_object_t * filter = hb_filter_get(HB_FILTER_COLORSPACE);
2576 g_string_append_printf(str, "%s%s", sval, filter->name);
2577 sval = ", ";
2578 }
2579
2580 text = g_string_free(str, FALSE);
2581 widget = GHB_WIDGET(ud->builder, "filters_summary");
2582 gtk_label_set_text(GTK_LABEL(widget), text);
2583 g_free(text);
2584
2585 double display_width;
2586 int width, height, display_height, par_width, par_height;
2587 char * display_aspect;
2588
2589 width = ghb_dict_get_int(ud->settings, "scale_width");
2590 height = ghb_dict_get_int(ud->settings, "scale_height");
2591 display_width = ghb_dict_get_int(ud->settings, "PictureDARWidth");
2592 display_height = ghb_dict_get_int(ud->settings, "DisplayHeight");
2593 par_width = ghb_dict_get_int(ud->settings, "PicturePARWidth");
2594 par_height = ghb_dict_get_int(ud->settings, "PicturePARHeight");
2595
2596 display_width = (double)width * par_width / par_height;
2597 display_aspect = ghb_get_display_aspect_string(display_width,
2598 display_height);
2599
2600 display_width = ghb_dict_get_int(ud->settings, "PictureDARWidth");
2601 text = g_strdup_printf("%dx%d storage, %dx%d display\n"
2602 "%d:%d Pixel Aspect Ratio\n"
2603 "%s Display Aspect Ratio",
2604 width, height, (int)display_width, display_height,
2605 par_width, par_height, display_aspect);
2606 widget = GHB_WIDGET(ud->builder, "dimensions_summary");
2607 gtk_label_set_text(GTK_LABEL(widget), text);
2608
2609 g_free(text);
2610 g_free(display_aspect);
2611
2612 ghb_value_free(&titleDict);
2613 }
2614
2615 void
ghb_set_title_settings(signal_user_data_t * ud,GhbValue * settings)2616 ghb_set_title_settings(signal_user_data_t *ud, GhbValue *settings)
2617 {
2618 int title_id, titleindex;
2619 const hb_title_t * title;
2620
2621 title_id = ghb_dict_get_int(settings, "title");
2622 title = ghb_lookup_title(title_id, &titleindex);
2623
2624 ghb_subtitle_set_pref_lang(settings);
2625 if (title != NULL)
2626 {
2627 GhbValue * job_dict, * title_dict;
2628 char * label;
2629
2630 job_dict = hb_preset_job_init(ghb_scan_handle(), title_id, settings);
2631 ghb_dict_set(settings, "Job", job_dict);
2632 title_dict = hb_title_to_dict(ghb_scan_handle(), title_id);
2633 ghb_dict_set(settings, "Title", title_dict);
2634
2635 gint num_chapters = hb_list_count(title->list_chapter);
2636
2637 ghb_dict_set_int(settings, "angle", 1);
2638 ghb_dict_set_string(settings, "PtoPType", "chapter");
2639 ghb_dict_set_int(settings, "start_point", 1);
2640 ghb_dict_set_int(settings, "end_point", num_chapters);
2641 ghb_dict_set_int(settings, "source_width", title->geometry.width);
2642 ghb_dict_set_int(settings, "source_height", title->geometry.height);
2643 ghb_dict_set_string(settings, "source", title->path);
2644 label = ghb_create_source_label(title);
2645 ghb_dict_set_string(settings, "source_label", label);
2646 g_free(label);
2647 label = ghb_create_volume_label(title);
2648 ghb_dict_set_string(settings, "volume", label);
2649 g_free(label);
2650
2651
2652 int angle, hflip;
2653 hb_geometry_crop_t srcGeo;
2654
2655 srcGeo.geometry = title->geometry;
2656 memcpy(srcGeo.crop, &title->crop, 4 * sizeof(int));
2657 angle = ghb_dict_get_int(settings, "rotate");
2658 hflip = ghb_dict_get_int(settings, "hflip");
2659 hb_rotate_geometry(&srcGeo, &srcGeo, angle, hflip);
2660 ghb_apply_crop(settings, &srcGeo);
2661
2662 int crop[4];
2663
2664 crop[0] = ghb_dict_get_int(settings, "PictureTopCrop");
2665 crop[1] = ghb_dict_get_int(settings, "PictureBottomCrop");
2666 crop[2] = ghb_dict_get_int(settings, "PictureLeftCrop");
2667 crop[3] = ghb_dict_get_int(settings, "PictureRightCrop");
2668 ghb_dict_set_int(settings, "scale_width",
2669 srcGeo.geometry.width - crop[2] - crop[3]);
2670
2671 // If anamorphic or keep_aspect, the height will
2672 // be automatically calculated
2673 gboolean keep_aspect;
2674 gint pic_par;
2675 keep_aspect = ghb_dict_get_bool(settings, "PictureKeepRatio");
2676 pic_par = ghb_settings_combo_int(settings, "PicturePAR");
2677 if (!keep_aspect ||
2678 pic_par == HB_ANAMORPHIC_NONE ||
2679 pic_par == HB_ANAMORPHIC_AUTO ||
2680 pic_par == HB_ANAMORPHIC_CUSTOM)
2681 {
2682 ghb_dict_set_int(settings, "scale_height",
2683 srcGeo.geometry.height - crop[0] - crop[1]);
2684 }
2685
2686 ghb_set_scale_settings(ud, settings, GHB_PIC_USE_MAX);
2687 ghb_dict_set_int(settings, "angle_count", title->angle_count);
2688
2689
2690 // Clear UI settings for new title
2691 ghb_dict_set_string(settings, "MetaName", "");
2692 ghb_dict_set_string(settings, "MetaArtist", "");
2693 ghb_dict_set_string(settings, "MetaReleaseDate", "");
2694 ghb_dict_set_string(settings, "MetaComment", "");
2695 ghb_dict_set_string(settings, "MetaAlbumArtist", "");
2696 ghb_dict_set_string(settings, "MetaGenre", "");
2697 ghb_dict_set_string(settings, "MetaDescription", "");
2698 ghb_dict_set_string(settings, "MetaLongDescription", "");
2699
2700 if (ghb_dict_get_bool(settings, "MetadataPassthrough"))
2701 {
2702 ghb_dict_set_string(settings, "MetaName", title->name);
2703 update_meta(settings, "Name", title->name);
2704 if (title->metadata && title->metadata->dict)
2705 {
2706
2707 hb_dict_iter_t iter = hb_dict_iter_init(title->metadata->dict);
2708
2709 while (iter != HB_DICT_ITER_DONE)
2710 {
2711 const char * key;
2712 hb_value_t * val;
2713
2714 hb_dict_iter_next_ex(title->metadata->dict, &iter, &key, &val);
2715 if (key != NULL && val != NULL)
2716 {
2717 const char * str = hb_value_get_string(val);
2718
2719 update_meta(settings, key, str);
2720
2721 if (!strcmp(key, "Album"))
2722 {
2723 key = "Name";
2724 }
2725 char * ui_key = g_strdup_printf("Meta%s", key);
2726 ghb_dict_set_string(settings, ui_key, str);
2727 free(ui_key);
2728 }
2729 }
2730 }
2731 }
2732 else
2733 {
2734 GhbValue * meta = ghb_get_job_metadata_settings(settings);
2735 hb_dict_clear(meta);
2736 }
2737 ghb_sanitize_audio_track_settings(settings);
2738 }
2739 else
2740 {
2741 ghb_dict_set_string(settings, "source_label", _("No Title Found"));
2742 ghb_dict_set_string(settings, "volume", _("New Video"));
2743 ghb_set_scale_settings(ud, settings, GHB_PIC_USE_MAX);
2744 }
2745
2746 set_destination_settings(ud, settings);
2747
2748 const char *dest_file, *dest_dir;
2749 char *dest;
2750 dest_file = ghb_dict_get_string(settings, "dest_file");
2751 dest_dir = ghb_dict_get_string(settings, "dest_dir");
2752 dest = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dest_dir, dest_file);
2753 ghb_dict_set_string(settings, "destination", dest);
2754 GhbValue *dest_dict = ghb_get_job_dest_settings(ud->settings);
2755 ghb_dict_set_string(dest_dict, "File", dest);
2756 g_free(dest);
2757
2758 ghb_dict_set_int(settings, "preview_frame", 2);
2759 }
2760
2761 void
ghb_set_current_title_settings(signal_user_data_t * ud)2762 ghb_set_current_title_settings(signal_user_data_t *ud)
2763 {
2764 ghb_set_title_settings(ud, ud->settings);
2765 ghb_update_summary_info(ud);
2766 }
2767
2768 static void
load_all_titles(signal_user_data_t * ud,int titleindex)2769 load_all_titles(signal_user_data_t *ud, int titleindex)
2770 {
2771 gint ii, count;
2772 GhbValue *preset;
2773 GhbValue *settings_array;
2774 const hb_title_t *title;
2775
2776 hb_list_t *list = ghb_get_title_list();
2777 count = hb_list_count(list);
2778
2779 if (count == 0)
2780 count = 1;
2781
2782 settings_array = ghb_array_new();
2783
2784 // Start with a clean job
2785 ghb_dict_remove(ud->settings, "Job");
2786
2787 preset = ghb_get_current_preset(ud);
2788 if (preset != NULL)
2789 {
2790 ghb_preset_to_settings(ud->settings, preset);
2791 ghb_value_free(&preset);
2792 }
2793 for (ii = 0; ii < count; ii++)
2794 {
2795 GhbValue *settings = ghb_value_dup(ud->settings);
2796
2797 title = hb_list_item(list, ii);
2798 ghb_dict_set_int(settings, "title", title ? title->index : -1);
2799 ghb_set_title_settings(ud, settings);
2800 ghb_array_append(settings_array, settings);
2801 }
2802 if (titleindex < 0 || titleindex >= count)
2803 {
2804 titleindex = 0;
2805 }
2806 ghb_value_free(&ud->settings_array);
2807 ud->settings_array = settings_array;
2808 ud->settings = ghb_array_get(ud->settings_array, titleindex);
2809 ghb_update_summary_info(ud);
2810 }
2811
2812 static gboolean update_preview = FALSE;
2813
2814 G_MODULE_EXPORT void
title_changed_cb(GtkWidget * widget,signal_user_data_t * ud)2815 title_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
2816 {
2817 gint title_id, titleindex, count;
2818 const hb_title_t * title;
2819 GtkLabel * title_label;
2820 char * label;
2821
2822 title_id = ghb_widget_int(widget);
2823 title = ghb_lookup_title(title_id, &titleindex);
2824
2825 label = ghb_create_title_label(title);
2826 title_label = GTK_LABEL(GHB_WIDGET(ud->builder, "title_label"));
2827 gtk_label_set_markup(title_label, label);
2828 g_free(label);
2829
2830 count = ghb_array_len(ud->settings_array);
2831 int idx = (titleindex >= 0 && titleindex < count) ? titleindex : 0;
2832 if (ghb_dict_get_bool(ud->prefs, "SyncTitleSettings"))
2833 {
2834 GhbValue * preset = ghb_settings_to_preset(ud->settings);
2835 GhbValue * settings = ghb_array_get(ud->settings_array, idx);
2836 if (preset != NULL)
2837 {
2838 ghb_preset_to_settings(settings, preset);
2839 ghb_set_title_settings(ud, settings);
2840 }
2841 ghb_value_free(&preset);
2842 }
2843 ud->settings = ghb_array_get(ud->settings_array, idx);
2844 ghb_load_settings(ud);
2845
2846 ghb_audio_title_change(ud, title != NULL);
2847 ghb_subtitle_title_change(ud, title != NULL);
2848 ghb_grey_combo_options(ud);
2849
2850 if (title != NULL)
2851 {
2852 gint preview_count;
2853 preview_count = title->preview_count;
2854 if (preview_count < 1)
2855 {
2856 preview_count = 1;
2857 }
2858 widget = GHB_WIDGET(ud->builder, "preview_frame");
2859 gtk_range_set_range(GTK_RANGE(widget), 1, preview_count);
2860
2861 ghb_reset_preview_image(ud);
2862 }
2863 ghb_update_summary_info(ud);
2864 }
2865
2866 G_MODULE_EXPORT void
ptop_widget_changed_cb(GtkWidget * widget,signal_user_data_t * ud)2867 ptop_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
2868 {
2869 gint title_id, titleindex;
2870 const hb_title_t * title;
2871 gboolean numeric = TRUE;
2872 GtkSpinButton *spin;
2873 GhbValue *range;
2874
2875 ghb_widget_to_setting(ud->settings, widget);
2876 ghb_check_dependency(ud, widget, NULL);
2877 ghb_live_reset(ud);
2878
2879 // Update type in Job
2880 range = ghb_get_job_range_settings(ud->settings);
2881 ghb_dict_set_string(range, "Type",
2882 ghb_dict_get_string(ud->settings, "PtoPType"));
2883
2884 title_id = ghb_dict_get_int(ud->settings, "title");
2885 title = ghb_lookup_title(title_id, &titleindex);
2886 if (title == NULL)
2887 return;
2888
2889 if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1)
2890 numeric = FALSE;
2891
2892 spin = GTK_SPIN_BUTTON(GHB_WIDGET(ud->builder, "start_point"));
2893 gtk_spin_button_set_numeric(spin, numeric);
2894 spin = GTK_SPIN_BUTTON(GHB_WIDGET(ud->builder, "end_point"));
2895 gtk_spin_button_set_numeric(spin, numeric);
2896
2897 gint duration = title->duration / 90000;
2898 if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0)
2899 {
2900 gint num_chapters = hb_list_count(title->list_chapter);
2901 spin_configure(ud, "start_point", 1, 1, num_chapters);
2902 spin_configure(ud, "end_point", num_chapters, 1, num_chapters);
2903 }
2904 else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1)
2905 {
2906 spin_configure(ud, "start_point", 0, 0, duration * 2 - 1);
2907 spin_configure(ud, "end_point", duration, 0, duration * 2);
2908 }
2909 else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 2)
2910 {
2911 gdouble max_frames = (gdouble)duration *
2912 title->vrate.num / title->vrate.den;
2913 spin_configure(ud, "start_point", 1, 1, max_frames * 2);
2914 spin_configure(ud, "end_point", max_frames, 1, max_frames * 2);
2915 }
2916 }
2917
2918 G_MODULE_EXPORT void
setting_widget_changed_cb(GtkWidget * widget,signal_user_data_t * ud)2919 setting_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
2920 {
2921 ghb_widget_to_setting(ud->settings, widget);
2922 ghb_check_dependency(ud, widget, NULL);
2923 ghb_update_summary_info(ud);
2924 ghb_clear_presets_selection(ud);
2925 ghb_live_reset(ud);
2926 }
2927
2928 G_MODULE_EXPORT void
meta_pass_changed_cb(GtkWidget * widget,signal_user_data_t * ud)2929 meta_pass_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
2930 {
2931 setting_widget_changed_cb(widget, ud);
2932
2933 int title_id, titleindex;
2934 const hb_title_t * title;
2935
2936 title_id = ghb_dict_get_int(ud->settings, "title");
2937 title = ghb_lookup_title(title_id, &titleindex);
2938
2939 if (title != NULL &&
2940 ghb_dict_get_bool(ud->settings, "MetadataPassthrough"))
2941 {
2942 // Reload metadata from title
2943 ghb_dict_set_string(ud->settings, "MetaName", title->name);
2944 update_meta(ud->settings, "Name", title->name);
2945 if (title->metadata && title->metadata->dict)
2946 {
2947
2948 hb_dict_iter_t iter = hb_dict_iter_init(title->metadata->dict);
2949
2950 while (iter != HB_DICT_ITER_DONE)
2951 {
2952 const char * key;
2953 hb_value_t * val;
2954
2955 hb_dict_iter_next_ex(title->metadata->dict, &iter, &key, &val);
2956 if (key != NULL && val != NULL)
2957 {
2958 const char * str = hb_value_get_string(val);
2959
2960 update_meta(ud->settings, key, str);
2961
2962 if (!strcmp(key, "Album"))
2963 {
2964 key = "Name";
2965 }
2966 char * ui_key = g_strdup_printf("Meta%s", key);
2967 ghb_dict_set_string(ud->settings, ui_key, str);
2968 free(ui_key);
2969 }
2970 }
2971 }
2972 }
2973 else
2974 {
2975 // Clear metadata
2976 ghb_dict_set_string(ud->settings, "MetaName", "");
2977 ghb_dict_set_string(ud->settings, "MetaArtist", "");
2978 ghb_dict_set_string(ud->settings, "MetaReleaseDate", "");
2979 ghb_dict_set_string(ud->settings, "MetaComment", "");
2980 ghb_dict_set_string(ud->settings, "MetaAlbumArtist", "");
2981 ghb_dict_set_string(ud->settings, "MetaGenre", "");
2982 ghb_dict_set_string(ud->settings, "MetaDescription", "");
2983 ghb_dict_set_string(ud->settings, "MetaLongDescription", "");
2984
2985 GhbValue * meta = ghb_get_job_metadata_settings(ud->settings);
2986 hb_dict_clear(meta);
2987 }
2988
2989 // Update UI
2990 ghb_ui_update_from_settings(ud, "MetaName", ud->settings);
2991 ghb_ui_update_from_settings(ud, "MetaArtist", ud->settings);
2992 ghb_ui_update_from_settings(ud, "MetaReleaseDate", ud->settings);
2993 ghb_ui_update_from_settings(ud, "MetaComment", ud->settings);
2994 ghb_ui_update_from_settings(ud, "MetaAlbumArtist", ud->settings);
2995 ghb_ui_update_from_settings(ud, "MetaGenre", ud->settings);
2996 ghb_ui_update_from_settings(ud, "MetaDescription", ud->settings);
2997 ghb_ui_update_from_settings(ud, "MetaLongDescription", ud->settings);
2998 }
2999
3000 G_MODULE_EXPORT void
filter_widget_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3001 filter_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3002 {
3003 setting_widget_changed_cb(widget, ud);
3004 update_preview = TRUE;
3005 }
3006
3007 G_MODULE_EXPORT void
nonsetting_widget_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3008 nonsetting_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3009 {
3010 ghb_widget_to_setting(ud->settings, widget);
3011 ghb_check_dependency(ud, widget, NULL);
3012 ghb_update_summary_info(ud);
3013 ghb_live_reset(ud);
3014 }
3015
3016 G_MODULE_EXPORT void
comb_detect_widget_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3017 comb_detect_widget_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3018 {
3019 ghb_widget_to_setting(ud->settings, widget);
3020 ghb_check_dependency(ud, widget, NULL);
3021 ghb_clear_presets_selection(ud);
3022 ghb_live_reset(ud);
3023
3024 const char * comb_detect;
3025 comb_detect = ghb_dict_get_string(ud->settings, "PictureCombDetectPreset");
3026 if (strcasecmp(comb_detect, "off"))
3027 {
3028 const char * deint;
3029 deint = ghb_dict_get_string(ud->settings, "PictureDeinterlaceFilter");
3030 if (!strcasecmp(deint, "off"))
3031 {
3032 ghb_ui_update(ud, "PictureDeinterlaceFilter",
3033 ghb_string_value("decomb"));
3034 }
3035 }
3036 ghb_update_summary_info(ud);
3037 }
3038
3039 G_MODULE_EXPORT void
deint_filter_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3040 deint_filter_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3041 {
3042 ghb_widget_to_setting(ud->settings, widget);
3043 ghb_check_dependency(ud, widget, NULL);
3044 ghb_clear_presets_selection(ud);
3045 ghb_live_reset(ud);
3046 ghb_update_ui_combo_box(ud, "PictureDeinterlacePreset", NULL, FALSE);
3047 ghb_ui_update(ud, "PictureDeinterlacePreset",
3048 ghb_dict_get(ud->settings, "PictureDeinterlacePreset"));
3049
3050 const char * deint;
3051 deint = ghb_dict_get_string(ud->settings, "PictureDeinterlaceFilter");
3052 if (!strcasecmp(deint, "off"))
3053 {
3054 ghb_ui_update(ud, "PictureCombDetectPreset",
3055 ghb_string_value("off"));
3056 }
3057 ghb_update_summary_info(ud);
3058 }
3059
3060 G_MODULE_EXPORT void
denoise_filter_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3061 denoise_filter_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3062 {
3063 ghb_widget_to_setting(ud->settings, widget);
3064 ghb_check_dependency(ud, widget, NULL);
3065 ghb_clear_presets_selection(ud);
3066 ghb_live_reset(ud);
3067 ghb_update_ui_combo_box(ud, "PictureDenoisePreset", NULL, FALSE);
3068 ghb_ui_update(ud, "PictureDenoisePreset",
3069 ghb_dict_get(ud->settings, "PictureDenoisePreset"));
3070 ghb_update_summary_info(ud);
3071 }
3072
3073 G_MODULE_EXPORT void
sharpen_filter_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3074 sharpen_filter_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3075 {
3076 ghb_widget_to_setting(ud->settings, widget);
3077 ghb_check_dependency(ud, widget, NULL);
3078 ghb_clear_presets_selection(ud);
3079 ghb_live_reset(ud);
3080 ghb_update_ui_combo_box(ud, "PictureSharpenPreset", NULL, FALSE);
3081 ghb_update_ui_combo_box(ud, "PictureSharpenTune", NULL, FALSE);
3082 ghb_ui_update(ud, "PictureSharpenPreset",
3083 ghb_dict_get(ud->settings, "PictureSharpenPreset"));
3084 ghb_ui_update(ud, "PictureSharpenTune", ghb_string_value("none"));
3085 ghb_update_summary_info(ud);
3086 }
3087
3088 G_MODULE_EXPORT void
title_angle_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3089 title_angle_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3090 {
3091 ghb_widget_to_setting(ud->settings, widget);
3092 ghb_check_dependency(ud, widget, NULL);
3093 ghb_live_reset(ud);
3094
3095 GhbValue *source = ghb_get_job_source_settings(ud->settings);
3096 ghb_dict_set_int(source, "Angle", ghb_dict_get_int(ud->settings, "angle"));
3097 }
3098
3099 G_MODULE_EXPORT void
meta_name_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3100 meta_name_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3101 {
3102 const char *val;
3103
3104 ghb_widget_to_setting(ud->settings, widget);
3105 val = ghb_dict_get_string(ud->settings, "MetaName");
3106 update_meta(ud->settings, "Name", val);
3107 }
3108
3109 G_MODULE_EXPORT void
meta_artist_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3110 meta_artist_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3111 {
3112 const char *val;
3113
3114 ghb_widget_to_setting(ud->settings, widget);
3115 val = ghb_dict_get_string(ud->settings, "MetaArtist");
3116 update_meta(ud->settings, "Artist", val);
3117 }
3118
3119 G_MODULE_EXPORT void
meta_album_artist_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3120 meta_album_artist_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3121 {
3122 const char *val;
3123
3124 ghb_widget_to_setting(ud->settings, widget);
3125 val = ghb_dict_get_string(ud->settings, "MetaAlbumArtist");
3126 update_meta(ud->settings, "AlbumArtist", val);
3127 }
3128
3129 G_MODULE_EXPORT void
meta_release_date_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3130 meta_release_date_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3131 {
3132 const char *val;
3133
3134 ghb_widget_to_setting(ud->settings, widget);
3135 val = ghb_dict_get_string(ud->settings, "MetaReleaseDate");
3136 update_meta(ud->settings, "ReleaseDate", val);
3137 }
3138
3139 G_MODULE_EXPORT void
meta_comment_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3140 meta_comment_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3141 {
3142 const char *val;
3143
3144 ghb_widget_to_setting(ud->settings, widget);
3145 val = ghb_dict_get_string(ud->settings, "MetaComment");
3146 update_meta(ud->settings, "Comment", val);
3147 }
3148
3149 G_MODULE_EXPORT void
meta_genre_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3150 meta_genre_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3151 {
3152 const char *val;
3153
3154 ghb_widget_to_setting(ud->settings, widget);
3155 val = ghb_dict_get_string(ud->settings, "MetaGenre");
3156 update_meta(ud->settings, "Genre", val);
3157 }
3158
3159 G_MODULE_EXPORT void
meta_description_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3160 meta_description_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3161 {
3162 const char *val;
3163
3164 ghb_widget_to_setting(ud->settings, widget);
3165 val = ghb_dict_get_string(ud->settings, "MetaDescription");
3166 update_meta(ud->settings, "Description", val);
3167 }
3168
3169 G_MODULE_EXPORT void
plot_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3170 plot_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3171 {
3172 GtkWidget * textview;
3173 const char * val;
3174
3175 textview = GTK_WIDGET(GHB_WIDGET(ud->builder, "MetaLongDescription"));
3176 ghb_widget_to_setting(ud->settings, textview);
3177 val = ghb_dict_get_string(ud->settings, "MetaLongDescription");
3178 update_meta(ud->settings, "LongDescription", val);
3179 }
3180
3181 G_MODULE_EXPORT void
chapter_markers_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3182 chapter_markers_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3183 {
3184 ghb_widget_to_setting(ud->settings, widget);
3185 ghb_check_dependency(ud, widget, NULL);
3186 ghb_clear_presets_selection(ud);
3187 ghb_live_reset(ud);
3188
3189 GhbValue *dest;
3190 int start, end;
3191 gboolean markers;
3192 dest = ghb_get_job_dest_settings(ud->settings);
3193 markers = ghb_dict_get_bool(ud->settings, "ChapterMarkers");
3194 start = ghb_dict_get_int(ud->settings, "start_point");
3195 end = ghb_dict_get_int(ud->settings, "end_point");
3196 markers &= (end > start);
3197 ghb_dict_set_bool(dest, "ChapterMarkers", markers);
3198 ghb_update_summary_info(ud);
3199 }
3200
3201 G_MODULE_EXPORT void
vquality_type_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3202 vquality_type_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3203 {
3204 ghb_widget_to_setting(ud->settings, widget);
3205 ghb_check_dependency(ud, widget, NULL);
3206 ghb_clear_presets_selection(ud);
3207 ghb_live_reset(ud);
3208 if (check_name_template(ud, "{quality}") ||
3209 check_name_template(ud, "{bitrate}"))
3210 set_destination(ud);
3211 }
3212
3213 G_MODULE_EXPORT void
vbitrate_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3214 vbitrate_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3215 {
3216 ghb_widget_to_setting(ud->settings, widget);
3217 ghb_check_dependency(ud, widget, NULL);
3218 ghb_clear_presets_selection(ud);
3219 ghb_live_reset(ud);
3220 if (check_name_template(ud, "{bitrate}"))
3221 set_destination(ud);
3222 }
3223
3224 G_MODULE_EXPORT void
vquality_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3225 vquality_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3226 {
3227 ghb_widget_to_setting(ud->settings, widget);
3228 ghb_check_dependency(ud, widget, NULL);
3229 ghb_clear_presets_selection(ud);
3230 ghb_live_reset(ud);
3231
3232 gint vcodec;
3233 double vquality;
3234
3235 vcodec = ghb_settings_video_encoder_codec(ud->settings, "VideoEncoder");
3236 vquality = ghb_dict_get_double(ud->settings, "VideoQualitySlider");
3237 if (vcodec == HB_VCODEC_X264_8BIT && vquality < 1.0)
3238 {
3239 // Set Profile to auto for lossless x264
3240 ghb_ui_update(ud, "VideoProfile", ghb_string_value("auto"));
3241 }
3242
3243 gdouble step;
3244 float min, max, min_step;
3245 int direction;
3246
3247 hb_video_quality_get_limits(vcodec, &min, &max, &min_step, &direction);
3248 step = ghb_settings_combo_double(ud->prefs, "VideoQualityGranularity");
3249 if (step < min_step)
3250 {
3251 step = min_step;
3252 }
3253 gdouble val = gtk_range_get_value(GTK_RANGE(widget));
3254 if (val < 0)
3255 {
3256 val = ((int)((val - step / 2) / step)) * step;
3257 }
3258 else
3259 {
3260 val = ((int)((val + step / 2) / step)) * step;
3261 }
3262 if (val < min)
3263 {
3264 val = min;
3265 }
3266 if (val > max)
3267 {
3268 val = max;
3269 }
3270 gtk_range_set_value(GTK_RANGE(widget), val);
3271 if (check_name_template(ud, "{quality}"))
3272 set_destination(ud);
3273 }
3274
3275 G_MODULE_EXPORT gboolean
ptop_input_cb(GtkWidget * widget,gdouble * val,signal_user_data_t * ud)3276 ptop_input_cb(GtkWidget *widget, gdouble *val, signal_user_data_t *ud)
3277 {
3278 if (ghb_settings_combo_int(ud->settings, "PtoPType") != 1)
3279 return FALSE;
3280
3281 const gchar *text;
3282 int result;
3283 double ss = 0;
3284 int hh = 0, mm = 0;
3285
3286 text = ghb_editable_get_text(widget);
3287 result = sscanf(text, "%2d:%2d:%lf", &hh, &mm, &ss);
3288 if (result <= 0)
3289 return FALSE;
3290 if (result == 1)
3291 {
3292 result = sscanf(text, "%lf", val);
3293 return TRUE;
3294 }
3295 *val = hh * 3600 + mm * 60 + ss;
3296 return TRUE;
3297 }
3298
3299 G_MODULE_EXPORT gboolean
ptop_output_cb(GtkWidget * widget,signal_user_data_t * ud)3300 ptop_output_cb(GtkWidget *widget, signal_user_data_t *ud)
3301 {
3302 if (ghb_settings_combo_int(ud->settings, "PtoPType") != 1)
3303 return FALSE;
3304
3305 GtkAdjustment *adjustment;
3306 gchar *text;
3307 double value, ss;
3308 int hh, mm;
3309
3310 adjustment = gtk_spin_button_get_adjustment(GTK_SPIN_BUTTON(widget));
3311 value = gtk_adjustment_get_value(adjustment);
3312 hh = value / 3600;
3313 value = value - hh * 3600;
3314 mm = value / 60;
3315 value = value - mm * 60;
3316 ss = value;
3317 text = g_strdup_printf ("%02d:%02d:%05.2f", hh, mm, ss);
3318 ghb_editable_set_text(widget, text);
3319 g_free (text);
3320
3321 return TRUE;
3322 }
3323
sanitize_ptop(signal_user_data_t * ud,int end_changed)3324 static void sanitize_ptop(signal_user_data_t * ud, int end_changed)
3325 {
3326 int64_t start, end;
3327
3328 if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0)
3329 {
3330 start = ghb_dict_get_int(ud->settings, "start_point");
3331 end = ghb_dict_get_int(ud->settings, "end_point");
3332 if (start > end)
3333 {
3334 if (end_changed == 1)
3335 ghb_ui_update(ud, "start_point", ghb_int_value(end));
3336 else
3337 ghb_ui_update(ud, "end_point", ghb_int_value(start));
3338 }
3339 }
3340 else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1)
3341 {
3342 start = ghb_dict_get_double(ud->settings, "start_point") * 90000;
3343 end = ghb_dict_get_double(ud->settings, "end_point") * 90000;
3344 if (start >= end)
3345 {
3346 if (end_changed == 1)
3347 ghb_ui_update(ud, "start_point", ghb_int_value(end-1));
3348 else
3349 ghb_ui_update(ud, "end_point", ghb_int_value(start+1));
3350 }
3351 }
3352 else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 2)
3353 {
3354 start = ghb_dict_get_int(ud->settings, "start_point");
3355 end = ghb_dict_get_int(ud->settings, "end_point");
3356 if (start > end)
3357 {
3358 if (end_changed == 1)
3359 ghb_ui_update(ud, "start_point", ghb_int_value(end));
3360 else
3361 ghb_ui_update(ud, "end_point", ghb_int_value(start));
3362 }
3363 }
3364 }
3365
update_ptop(signal_user_data_t * ud)3366 static void update_ptop(signal_user_data_t * ud)
3367 {
3368 int64_t start, end;
3369 GhbValue *dest = ghb_get_job_dest_settings(ud->settings);
3370 GhbValue *range = ghb_get_job_range_settings(ud->settings);
3371
3372 if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0)
3373 {
3374 start = ghb_dict_get_int(ud->settings, "start_point");
3375 end = ghb_dict_get_int(ud->settings, "end_point");
3376 if (check_name_template(ud, "{chapters}"))
3377 set_destination(ud);
3378 GtkWidget *widget = GHB_WIDGET (ud->builder, "ChapterMarkers");
3379 gtk_widget_set_sensitive(widget, end > start);
3380 update_title_duration(ud);
3381
3382 gboolean markers;
3383 markers = ghb_dict_get_int(ud->settings, "ChapterMarkers");
3384 markers &= (end > start);
3385 ghb_dict_set_bool(dest, "ChapterMarkers", markers);
3386 ghb_dict_set_int(range, "Start", start);
3387 ghb_dict_set_int(range, "End", end);
3388 }
3389 else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1)
3390 {
3391 start = ghb_dict_get_double(ud->settings, "start_point") * 90000;
3392 end = ghb_dict_get_double(ud->settings, "end_point") * 90000;
3393 update_title_duration(ud);
3394
3395 ghb_dict_set_int(range, "Start", start);
3396 ghb_dict_set_int(range, "End", end);
3397 }
3398 else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 2)
3399 {
3400 start = ghb_dict_get_int(ud->settings, "start_point");
3401 end = ghb_dict_get_int(ud->settings, "end_point");
3402 update_title_duration(ud);
3403
3404 ghb_dict_set_int(range, "Start", start);
3405 ghb_dict_set_int(range, "End", end);
3406 }
3407 }
3408
3409 G_MODULE_EXPORT void
ptop_edited_cb(GtkWidget * widget,signal_user_data_t * ud)3410 ptop_edited_cb(GtkWidget *widget, signal_user_data_t *ud)
3411 {
3412 if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1)
3413 return;
3414
3415 const gchar *text, *key;
3416 int val = 0;
3417
3418 text = ghb_editable_get_text(widget);
3419 if (text == NULL || text[0] == 0)
3420 return;
3421 val = g_strtod(text, NULL);
3422 key = ghb_get_setting_key(widget);
3423 ghb_dict_set_int(ud->settings, key, val);
3424 update_ptop(ud);
3425 }
3426
3427 G_MODULE_EXPORT void
start_point_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3428 start_point_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3429 {
3430 ghb_widget_to_setting(ud->settings, widget);
3431 sanitize_ptop(ud, 0);
3432 ghb_check_dependency(ud, widget, NULL);
3433 update_ptop(ud);
3434 }
3435
3436 G_MODULE_EXPORT void
end_point_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3437 end_point_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3438 {
3439 ghb_widget_to_setting(ud->settings, widget);
3440 sanitize_ptop(ud, 1);
3441 ghb_check_dependency(ud, widget, NULL);
3442 update_ptop(ud);
3443 }
3444
3445 G_MODULE_EXPORT void
scale_width_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3446 scale_width_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3447 {
3448 g_debug("scale_width_changed_cb ()");
3449 ghb_widget_to_setting(ud->settings, widget);
3450 ghb_check_dependency(ud, widget, NULL);
3451 ghb_clear_presets_selection(ud);
3452 if (gtk_widget_is_sensitive(widget))
3453 ghb_set_scale(ud, GHB_PIC_KEEP_WIDTH);
3454 update_preview = TRUE;
3455 ghb_live_reset(ud);
3456 }
3457
3458 G_MODULE_EXPORT void
scale_height_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3459 scale_height_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3460 {
3461 g_debug("scale_height_changed_cb ()");
3462 ghb_widget_to_setting(ud->settings, widget);
3463 ghb_check_dependency(ud, widget, NULL);
3464 ghb_clear_presets_selection(ud);
3465 if (gtk_widget_is_sensitive(widget))
3466 ghb_set_scale(ud, GHB_PIC_KEEP_HEIGHT);
3467
3468 update_preview = TRUE;
3469 ghb_live_reset(ud);
3470 }
3471
3472 G_MODULE_EXPORT void
crop_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3473 crop_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3474 {
3475 g_debug("crop_changed_cb ()");
3476 ghb_widget_to_setting(ud->settings, widget);
3477 ghb_check_dependency(ud, widget, NULL);
3478 ghb_clear_presets_selection(ud);
3479 if (gtk_widget_is_sensitive(widget))
3480 ghb_set_scale(ud, 0);
3481 update_preview = TRUE;
3482 ghb_live_reset(ud);
3483 }
3484
3485 G_MODULE_EXPORT void
pad_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3486 pad_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3487 {
3488 g_debug("pad_changed_cb ()");
3489 ghb_widget_to_setting(ud->settings, widget);
3490 ghb_check_dependency(ud, widget, NULL);
3491 ghb_clear_presets_selection(ud);
3492 ghb_live_reset(ud);
3493 if (gtk_widget_is_sensitive(widget))
3494 ghb_set_scale(ud, 0);
3495
3496 update_preview = TRUE;
3497 }
3498
3499 G_MODULE_EXPORT void
display_width_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3500 display_width_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3501 {
3502 g_debug("display_width_changed_cb ()");
3503 ghb_widget_to_setting(ud->settings, widget);
3504 ghb_check_dependency(ud, widget, NULL);
3505 ghb_clear_presets_selection(ud);
3506 ghb_live_reset(ud);
3507 if (gtk_widget_is_sensitive(widget))
3508 ghb_set_scale(ud, GHB_PIC_KEEP_DISPLAY_WIDTH);
3509
3510 update_preview = TRUE;
3511 }
3512
3513 G_MODULE_EXPORT void
display_height_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3514 display_height_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3515 {
3516 g_debug("display_height_changed_cb ()");
3517 ghb_widget_to_setting(ud->settings, widget);
3518 ghb_check_dependency(ud, widget, NULL);
3519 ghb_clear_presets_selection(ud);
3520 ghb_live_reset(ud);
3521 if (gtk_widget_is_sensitive(widget))
3522 ghb_set_scale(ud, GHB_PIC_KEEP_DISPLAY_HEIGHT);
3523
3524 update_preview = TRUE;
3525 }
3526
3527 G_MODULE_EXPORT void
par_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3528 par_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3529 {
3530 g_debug("par_changed_cb ()");
3531 ghb_widget_to_setting(ud->settings, widget);
3532 ghb_check_dependency(ud, widget, NULL);
3533 ghb_clear_presets_selection(ud);
3534 ghb_live_reset(ud);
3535 if (gtk_widget_is_sensitive(widget))
3536 ghb_set_scale(ud, GHB_PIC_KEEP_HEIGHT);
3537
3538 update_preview = TRUE;
3539 }
3540
3541 G_MODULE_EXPORT void
scale_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3542 scale_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3543 {
3544 g_debug("scale_changed_cb ()");
3545 ghb_widget_to_setting(ud->settings, widget);
3546 ghb_check_dependency(ud, widget, NULL);
3547 ghb_clear_presets_selection(ud);
3548 ghb_live_reset(ud);
3549 if (gtk_widget_is_sensitive(widget))
3550 ghb_set_scale(ud, 0);
3551 update_preview = TRUE;
3552 }
3553
3554 G_MODULE_EXPORT void
rotate_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3555 rotate_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3556 {
3557 int angle, hflip, prev_angle, prev_hflip;
3558
3559 g_debug("rotate_changed_cb ()");
3560 prev_angle = ghb_dict_get_int(ud->settings, "rotate");
3561 prev_hflip = ghb_dict_get_int(ud->settings, "hflip");
3562
3563 ghb_widget_to_setting(ud->settings, widget);
3564 ghb_check_dependency(ud, widget, NULL);
3565 ghb_clear_presets_selection(ud);
3566 ghb_live_reset(ud);
3567
3568 hb_geometry_crop_t geo;
3569
3570 // First normalize current settings to 0 angle 0 hflip
3571 geo.geometry.width = ghb_dict_get_int(ud->settings, "scale_width");
3572 geo.geometry.height = ghb_dict_get_int(ud->settings, "scale_height");
3573 geo.geometry.par.num =
3574 ghb_dict_get_int(ud->settings, "PicturePARWidth");
3575 geo.geometry.par.den =
3576 ghb_dict_get_int(ud->settings, "PicturePARHeight");
3577 geo.crop[0] = ghb_dict_get_int(ud->settings, "PictureTopCrop");
3578 geo.crop[1] = ghb_dict_get_int(ud->settings, "PictureBottomCrop");
3579 geo.crop[2] = ghb_dict_get_int(ud->settings, "PictureLeftCrop");
3580 geo.crop[3] = ghb_dict_get_int(ud->settings, "PictureRightCrop");
3581
3582 geo.pad[0] = ghb_dict_get_int(ud->settings, "PicturePadTop");
3583 geo.pad[1] = ghb_dict_get_int(ud->settings, "PicturePadBottom");
3584 geo.pad[2] = ghb_dict_get_int(ud->settings, "PicturePadLeft");
3585 geo.pad[3] = ghb_dict_get_int(ud->settings, "PicturePadRight");
3586 // Normally hflip is applied, then rotation.
3587 // To revert, must apply rotation then hflip.
3588 hb_rotate_geometry(&geo, &geo, 360 - prev_angle, 0);
3589 hb_rotate_geometry(&geo, &geo, 0, prev_hflip);
3590
3591 const hb_title_t * title;
3592 int title_id, titleindex;
3593
3594 title_id = ghb_dict_get_int(ud->settings, "title");
3595 title = ghb_lookup_title(title_id, &titleindex);
3596 if (title != NULL)
3597 {
3598 // If there is a title, reset to title width/height so that
3599 // dimension limits get properly re-applied
3600 geo.geometry.width = title->geometry.width;
3601 geo.geometry.height = title->geometry.height;
3602 }
3603
3604 // rotate dimensions to new angle and hflip
3605 angle = ghb_dict_get_int(ud->settings, "rotate");
3606 hflip = ghb_dict_get_int(ud->settings, "hflip");
3607 hb_rotate_geometry(&geo, &geo, angle, hflip);
3608
3609 ghb_dict_set_int(ud->settings, "scale_width",
3610 geo.geometry.width - geo.crop[2] - geo.crop[3]);
3611 ghb_dict_set_int(ud->settings, "scale_height",
3612 geo.geometry.height - geo.crop[0] - geo.crop[1]);
3613 ghb_dict_set_int(ud->settings, "PicturePARWidth", geo.geometry.par.num);
3614 ghb_dict_set_int(ud->settings, "PicturePARHeight", geo.geometry.par.den);
3615 ghb_dict_set_int(ud->settings, "PictureTopCrop", geo.crop[0]);
3616 ghb_dict_set_int(ud->settings, "PictureBottomCrop", geo.crop[1]);
3617 ghb_dict_set_int(ud->settings, "PictureLeftCrop", geo.crop[2]);
3618 ghb_dict_set_int(ud->settings, "PictureRightCrop", geo.crop[3]);
3619
3620 ghb_dict_set_int(ud->settings, "PicturePadTop", geo.pad[0]);
3621 ghb_dict_set_int(ud->settings, "PicturePadBottom", geo.pad[1]);
3622 ghb_dict_set_int(ud->settings, "PicturePadLeft", geo.pad[2]);
3623 ghb_dict_set_int(ud->settings, "PicturePadRight", geo.pad[3]);
3624
3625 ghb_set_scale(ud, 0);
3626 update_preview = TRUE;
3627 }
3628
3629 G_MODULE_EXPORT void
resolution_limit_changed_cb(GtkWidget * widget,signal_user_data_t * ud)3630 resolution_limit_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
3631 {
3632 g_debug("resolution_limit_changed_cb ()");
3633 ghb_widget_to_setting(ud->settings, widget);
3634 ghb_check_dependency(ud, widget, NULL);
3635 ghb_clear_presets_selection(ud);
3636 ghb_live_reset(ud);
3637
3638 const gchar * resolution_limit;
3639 int width, height;
3640
3641 resolution_limit = ghb_dict_get_string(ud->settings, "resolution_limit");
3642 ghb_lookup_resolution_limit_dimensions(resolution_limit, &width, &height);
3643 if (width >= 0 && height >= 0)
3644 {
3645 ghb_dict_set_int(ud->settings, "PictureWidth", width);
3646 ghb_dict_set_int(ud->settings, "PictureHeight", height);
3647 }
3648
3649 ghb_set_scale(ud, GHB_PIC_KEEP_HEIGHT);
3650
3651 update_preview = TRUE;
3652 }
3653
3654 G_MODULE_EXPORT void
generic_entry_changed_cb(GtkEntry * entry,signal_user_data_t * ud)3655 generic_entry_changed_cb(GtkEntry *entry, signal_user_data_t *ud)
3656 {
3657 // Normally (due to user input) I only want to process the entry
3658 // when editing is done and the focus-out signal is sent.
3659 // But... there's always a but.
3660 // If the entry is changed by software, the focus-out signal is not sent.
3661 // The changed signal is sent ... so here we are.
3662 // I don't want to process upon every keystroke, so I prevent processing
3663 // while the widget has focus.
3664 g_debug("generic_entry_changed_cb ()");
3665 if (!gtk_widget_has_focus((GtkWidget*)entry))
3666 {
3667 ghb_widget_to_setting(ud->settings, (GtkWidget*)entry);
3668 }
3669 }
3670
3671 G_MODULE_EXPORT void
preferences_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)3672 preferences_action_cb(GSimpleAction *action, GVariant *param,
3673 signal_user_data_t *ud)
3674 {
3675 GtkWidget *dialog;
3676
3677 dialog = GHB_WIDGET(ud->builder, "prefs_dialog");
3678 gtk_dialog_run(GTK_DIALOG(dialog));
3679 gtk_widget_hide(dialog);
3680 ghb_prefs_store();
3681 }
3682
3683 typedef struct
3684 {
3685 GtkMessageDialog *dlg;
3686 const gchar *msg;
3687 const gchar *action;
3688 gint timeout;
3689 signal_user_data_t *ud;
3690 } countdown_t;
3691
3692 static gboolean
quit_cb(countdown_t * cd)3693 quit_cb(countdown_t *cd)
3694 {
3695 gchar *str;
3696
3697 cd->timeout--;
3698 if (cd->timeout == 0)
3699 {
3700 ghb_hb_cleanup(FALSE);
3701 prune_logs(cd->ud);
3702
3703 gtk_widget_destroy (GTK_WIDGET(cd->dlg));
3704 g_application_quit(G_APPLICATION(cd->ud->app));
3705 return FALSE;
3706 }
3707 str = g_strdup_printf(_("%s\n\n%s in %d seconds ..."),
3708 cd->msg, cd->action, cd->timeout);
3709 gtk_message_dialog_set_markup(cd->dlg, str);
3710 g_free(str);
3711 return TRUE;
3712 }
3713
3714 static gboolean
shutdown_cb(countdown_t * cd)3715 shutdown_cb(countdown_t *cd)
3716 {
3717 gchar *str;
3718
3719 cd->timeout--;
3720 if (cd->timeout == 0)
3721 {
3722 ghb_hb_cleanup(FALSE);
3723 prune_logs(cd->ud);
3724
3725 ghb_shutdown_gsm();
3726 g_application_quit(G_APPLICATION(cd->ud->app));
3727 return FALSE;
3728 }
3729 str = g_strdup_printf(_("%s\n\n%s in %d seconds ..."),
3730 cd->msg, cd->action, cd->timeout);
3731 gtk_message_dialog_set_markup(cd->dlg, str);
3732 g_free(str);
3733 return TRUE;
3734 }
3735
3736 static gboolean
suspend_cb(countdown_t * cd)3737 suspend_cb(countdown_t *cd)
3738 {
3739 gchar *str;
3740
3741 cd->timeout--;
3742 if (cd->timeout == 0)
3743 {
3744 gtk_widget_destroy (GTK_WIDGET(cd->dlg));
3745 ghb_suspend_gpm();
3746 return FALSE;
3747 }
3748 str = g_strdup_printf(_("%s\n\n%s in %d seconds ..."),
3749 cd->msg, cd->action, cd->timeout);
3750 gtk_message_dialog_set_markup(cd->dlg, str);
3751 g_free(str);
3752 return TRUE;
3753 }
3754
3755 void
ghb_countdown_dialog(GtkMessageType type,const gchar * message,const gchar * action,const gchar * cancel,GSourceFunc action_func,signal_user_data_t * ud,gint timeout)3756 ghb_countdown_dialog(
3757 GtkMessageType type,
3758 const gchar *message,
3759 const gchar *action,
3760 const gchar *cancel,
3761 GSourceFunc action_func,
3762 signal_user_data_t *ud,
3763 gint timeout)
3764 {
3765 GtkWindow *hb_window;
3766 GtkWidget *dialog;
3767 GtkResponseType response;
3768 guint timeout_id;
3769 countdown_t cd;
3770
3771 cd.msg = message;
3772 cd.action = action;
3773 cd.timeout = timeout;
3774 cd.ud = ud;
3775
3776 // Toss up a warning dialog
3777 hb_window = GTK_WINDOW(GHB_WIDGET(ud->builder, "hb_window"));
3778 dialog = gtk_message_dialog_new(hb_window, GTK_DIALOG_MODAL,
3779 type, GTK_BUTTONS_NONE,
3780 _("%s\n\n%s in %d seconds ..."),
3781 message, action, timeout);
3782 gtk_dialog_add_buttons( GTK_DIALOG(dialog),
3783 cancel, GTK_RESPONSE_CANCEL,
3784 NULL);
3785
3786 cd.dlg = GTK_MESSAGE_DIALOG(dialog);
3787 timeout_id = g_timeout_add(1000, action_func, &cd);
3788 response = gtk_dialog_run(GTK_DIALOG(dialog));
3789 gtk_widget_destroy (dialog);
3790 if (response == GTK_RESPONSE_CANCEL)
3791 {
3792 GMainContext *mc;
3793 GSource *source;
3794
3795 mc = g_main_context_default();
3796 source = g_main_context_find_source_by_id(mc, timeout_id);
3797 if (source != NULL)
3798 g_source_destroy(source);
3799 }
3800 }
3801
3802 gboolean
ghb_message_dialog(GtkWindow * parent,GtkMessageType type,const gchar * message,const gchar * no,const gchar * yes)3803 ghb_message_dialog(GtkWindow *parent, GtkMessageType type, const gchar *message, const gchar *no, const gchar *yes)
3804 {
3805 GtkWidget *dialog;
3806 GtkResponseType response;
3807
3808 // Toss up a warning dialog
3809 dialog = gtk_message_dialog_new(parent, GTK_DIALOG_MODAL,
3810 type, GTK_BUTTONS_NONE,
3811 "%s", message);
3812 gtk_dialog_add_buttons( GTK_DIALOG(dialog),
3813 no, GTK_RESPONSE_NO,
3814 yes, GTK_RESPONSE_YES, NULL);
3815 response = gtk_dialog_run(GTK_DIALOG(dialog));
3816 gtk_widget_destroy (dialog);
3817 if (response == GTK_RESPONSE_NO)
3818 {
3819 return FALSE;
3820 }
3821 return TRUE;
3822 }
3823
3824 void
ghb_error_dialog(GtkWindow * parent,GtkMessageType type,const gchar * message,const gchar * cancel)3825 ghb_error_dialog(GtkWindow *parent, GtkMessageType type, const gchar *message, const gchar *cancel)
3826 {
3827 GtkWidget *dialog;
3828
3829 // Toss up a warning dialog
3830 dialog = gtk_message_dialog_new(parent, GTK_DIALOG_MODAL,
3831 type, GTK_BUTTONS_NONE,
3832 "%s", message);
3833 gtk_dialog_add_buttons( GTK_DIALOG(dialog),
3834 cancel, GTK_RESPONSE_CANCEL, NULL);
3835 gtk_dialog_run(GTK_DIALOG(dialog));
3836 gtk_widget_destroy (dialog);
3837 }
3838
3839 void
ghb_cancel_encode(signal_user_data_t * ud,const gchar * extra_msg)3840 ghb_cancel_encode(signal_user_data_t *ud, const gchar *extra_msg)
3841 {
3842 GtkWindow *hb_window;
3843 GtkWidget *dialog;
3844 GtkResponseType response;
3845
3846 if (extra_msg == NULL) extra_msg = "";
3847 // Toss up a warning dialog
3848 hb_window = GTK_WINDOW(GHB_WIDGET(ud->builder, "hb_window"));
3849 dialog = gtk_message_dialog_new(hb_window, GTK_DIALOG_MODAL,
3850 GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE,
3851 _("%sYour movie will be lost if you don't continue encoding."),
3852 extra_msg);
3853 gtk_dialog_add_buttons( GTK_DIALOG(dialog),
3854 _("Cancel Current and Stop"), 1,
3855 _("Cancel Current, Start Next"), 2,
3856 _("Finish Current, then Stop"), 3,
3857 _("Continue Encoding"), 4,
3858 NULL);
3859 response = gtk_dialog_run(GTK_DIALOG(dialog));
3860 gtk_widget_destroy (dialog);
3861 switch ((int)response)
3862 {
3863 case 1:
3864 ghb_stop_queue();
3865 ud->cancel_encode = GHB_CANCEL_ALL;
3866 break;
3867 case 2:
3868 ghb_stop_queue();
3869 ud->cancel_encode = GHB_CANCEL_CURRENT;
3870 break;
3871 case 3:
3872 ud->cancel_encode = GHB_CANCEL_FINISH;
3873 break;
3874 case 4:
3875 default:
3876 ud->cancel_encode = GHB_CANCEL_NONE;
3877 break;
3878 }
3879 }
3880
3881 gboolean
ghb_cancel_encode2(signal_user_data_t * ud,const gchar * extra_msg)3882 ghb_cancel_encode2(signal_user_data_t *ud, const gchar *extra_msg)
3883 {
3884 GtkWindow *hb_window;
3885 GtkWidget *dialog;
3886 GtkResponseType response;
3887
3888 if (extra_msg == NULL) extra_msg = "";
3889 // Toss up a warning dialog
3890 hb_window = GTK_WINDOW(GHB_WIDGET(ud->builder, "hb_window"));
3891 dialog = gtk_message_dialog_new(hb_window, GTK_DIALOG_MODAL,
3892 GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE,
3893 _("%sYour movie will be lost if you don't continue encoding."),
3894 extra_msg);
3895 gtk_dialog_add_buttons( GTK_DIALOG(dialog),
3896 _("Cancel Current and Stop"), 1,
3897 _("Continue Encoding"), 4,
3898 NULL);
3899 response = gtk_dialog_run(GTK_DIALOG(dialog));
3900 gtk_widget_destroy (dialog);
3901 switch ((int)response)
3902 {
3903 case 1:
3904 ghb_stop_queue();
3905 ud->cancel_encode = GHB_CANCEL_ALL;
3906 return TRUE;
3907 case 4:
3908 default:
3909 break;
3910 }
3911 return FALSE;
3912 }
3913
3914 static void
start_new_log(signal_user_data_t * ud,GhbValue * uiDict)3915 start_new_log(signal_user_data_t *ud, GhbValue *uiDict)
3916 {
3917 time_t _now;
3918 struct tm *now;
3919 gchar *log_path, *pos, *basename, *dest_dir;
3920 const gchar *destname;
3921
3922 // queue_activity_buffer is about to be reused, make sure
3923 // queue is showing the correct buffer
3924 ghb_queue_select_log(ud);
3925 // Erase current contents of queue activity
3926 gtk_text_buffer_set_text(ud->queue_activity_buffer, "", 0);
3927
3928 _now = time(NULL);
3929 now = localtime(&_now);
3930 destname = ghb_dict_get_string(uiDict, "destination");
3931 basename = g_path_get_basename(destname);
3932 if (ghb_dict_get_bool(ud->prefs, "EncodeLogLocation"))
3933 {
3934 dest_dir = g_path_get_dirname (destname);
3935 }
3936 else
3937 {
3938 dest_dir = ghb_get_user_config_dir("EncodeLogs");
3939 }
3940 pos = g_strrstr( basename, "." );
3941 if (pos != NULL)
3942 {
3943 *pos = 0;
3944 }
3945 log_path = g_strdup_printf("%s/%s %d-%02d-%02d %02d-%02d-%02d.log",
3946 dest_dir,
3947 basename,
3948 now->tm_year + 1900, now->tm_mon + 1, now->tm_mday,
3949 now->tm_hour, now->tm_min, now->tm_sec);
3950 g_free(basename);
3951 g_free(dest_dir);
3952 if (ud->job_activity_log)
3953 g_io_channel_unref(ud->job_activity_log);
3954 ud->job_activity_log = g_io_channel_new_file (log_path, "w", NULL);
3955 if (ud->job_activity_log)
3956 {
3957 gchar *ver_str;
3958
3959 ver_str = g_strdup_printf("Handbrake Version: %s (%d)\n",
3960 hb_get_version(NULL), hb_get_build(NULL));
3961 g_io_channel_write_chars (ud->job_activity_log, ver_str,
3962 -1, NULL, NULL);
3963 g_free(ver_str);
3964 ghb_dict_set_string(uiDict, "ActivityFilename", log_path);
3965 }
3966 g_free(log_path);
3967 }
3968
3969 static void
submit_job(signal_user_data_t * ud,GhbValue * queueDict)3970 submit_job(signal_user_data_t *ud, GhbValue *queueDict)
3971 {
3972 gchar *type, *modified;
3973 const char *name;
3974 GhbValue *uiDict;
3975 gboolean preset_modified;
3976
3977 g_debug("submit_job");
3978 if (queueDict == NULL) return;
3979 uiDict = ghb_dict_get(queueDict, "uiSettings");
3980 preset_modified = ghb_dict_get_bool(uiDict, "preset_modified");
3981 name = ghb_dict_get_string(uiDict, "PresetFullName");
3982 type = ghb_dict_get_int(uiDict, "Type") == 1 ? "Custom " : "";
3983 modified = preset_modified ? "Modified " : "";
3984 ghb_log("%s%sPreset: %s", modified, type, name);
3985
3986 ghb_dict_set_int(uiDict, "job_status", GHB_QUEUE_RUNNING);
3987 start_new_log(ud, uiDict);
3988 GhbValue *job_dict = ghb_dict_get(queueDict, "Job");
3989 int unique_id = ghb_add_job(ghb_queue_handle(), job_dict);
3990 ghb_dict_set_int(uiDict, "job_unique_id", unique_id);
3991 time_t now = time(NULL);
3992 ghb_dict_set_int(uiDict, "job_start_time", now);
3993 ghb_start_queue();
3994
3995 // Show queue progress bar
3996 int index = ghb_find_queue_job(ud->queue, unique_id, NULL);
3997 ghb_queue_progress_set_visible(ud, index, 1);
3998 }
3999
4000 static void
prune_logs(signal_user_data_t * ud)4001 prune_logs(signal_user_data_t *ud)
4002 {
4003 gchar *dest_dir;
4004 gint days;
4005
4006 // Only prune logs stored in the default config dir location
4007 days = ghb_settings_combo_int(ud->prefs, "LogLongevity");
4008 if (days > 365)
4009 return;
4010
4011 dest_dir = ghb_get_user_config_dir("EncodeLogs");
4012 if (g_file_test(dest_dir, G_FILE_TEST_IS_DIR))
4013 {
4014 const gchar *file;
4015 gint duration = days * 24 * 60 * 60;
4016
4017 GDir *gdir = g_dir_open(dest_dir, 0, NULL);
4018 time_t now;
4019
4020 now = time(NULL);
4021 file = g_dir_read_name(gdir);
4022 while (file)
4023 {
4024 gchar *path;
4025 struct stat stbuf;
4026
4027 path = g_strdup_printf("%s/%s", dest_dir, file);
4028 g_stat(path, &stbuf);
4029 if (now - stbuf.st_mtime > duration)
4030 {
4031 g_unlink(path);
4032 }
4033 g_free(path);
4034 file = g_dir_read_name(gdir);
4035 }
4036 g_dir_close(gdir);
4037 }
4038 g_free(dest_dir);
4039 ghb_preview_cleanup(ud);
4040 }
4041
4042 static gint
queue_pending_count(GhbValue * queue)4043 queue_pending_count(GhbValue *queue)
4044 {
4045 gint nn, ii, count;
4046 GhbValue *queueDict, *uiDict;
4047 gint status;
4048
4049 nn = 0;
4050 count = ghb_array_len(queue);
4051 for (ii = 0; ii < count; ii++)
4052 {
4053
4054 queueDict = ghb_array_get(queue, ii);
4055 uiDict = ghb_dict_get(queueDict, "uiSettings");
4056 status = ghb_dict_get_int(uiDict, "job_status");
4057 if (status == GHB_QUEUE_PENDING)
4058 {
4059 nn++;
4060 }
4061 }
4062 return nn;
4063 }
4064
4065 void
ghb_update_pending(signal_user_data_t * ud)4066 ghb_update_pending(signal_user_data_t *ud)
4067 {
4068 GtkLabel *label;
4069 gint pending;
4070 gchar *str;
4071
4072 pending = queue_pending_count(ud->queue);
4073 if (pending == 1)
4074 {
4075 str = g_strdup_printf(_("%d encode pending"), pending);
4076 }
4077 else
4078 {
4079 str = g_strdup_printf(_("%d encodes pending"), pending);
4080 }
4081 label = GTK_LABEL(GHB_WIDGET(ud->builder, "pending_status"));
4082 gtk_label_set_text(label, str);
4083 label = GTK_LABEL(GHB_WIDGET(ud->builder, "queue_status_label"));
4084 gtk_label_set_text(label, str);
4085 g_free(str);
4086
4087 update_queue_labels(ud);
4088 }
4089
4090 void
ghb_start_next_job(signal_user_data_t * ud)4091 ghb_start_next_job(signal_user_data_t *ud)
4092 {
4093 gint count, ii;
4094 GhbValue *queueDict, *uiDict;
4095 gint status;
4096 GtkWidget *progress;
4097
4098 g_debug("start_next_job");
4099 progress = GHB_WIDGET(ud->builder, "progressbar");
4100 gtk_widget_show(progress);
4101
4102 count = ghb_array_len(ud->queue);
4103 for (ii = 0; ii < count; ii++)
4104 {
4105
4106 queueDict = ghb_array_get(ud->queue, ii);
4107 uiDict = ghb_dict_get(queueDict, "uiSettings");
4108 status = ghb_dict_get_int(uiDict, "job_status");
4109 if (status == GHB_QUEUE_PENDING)
4110 {
4111 ghb_inhibit_suspend(ud);
4112 submit_job(ud, queueDict);
4113 ghb_update_pending(ud);
4114 return;
4115 }
4116 }
4117 // Nothing pending
4118 ghb_uninhibit_suspend(ud);
4119 ghb_notify_done(ud);
4120 ghb_update_pending(ud);
4121 gtk_widget_hide(progress);
4122 ghb_dict_set_bool(ud->globals, "SkipDiskFreeCheck", FALSE);
4123 }
4124
4125 static gchar*
working_status_string(signal_user_data_t * ud,ghb_instance_status_t * status)4126 working_status_string(signal_user_data_t *ud, ghb_instance_status_t *status)
4127 {
4128 gchar *task_str, *job_str, *status_str;
4129 gint qcount;
4130 gint index;
4131
4132 qcount = ghb_array_len(ud->queue);
4133 index = ghb_find_queue_job(ud->queue, status->unique_id, NULL);
4134 if (qcount > 1)
4135 {
4136 job_str = g_strdup_printf(_("job %d of %d, "), index+1, qcount);
4137 }
4138 else
4139 {
4140 job_str = g_strdup("");
4141 }
4142 if (status->pass_count > 1)
4143 {
4144 if (status->pass_id == HB_PASS_SUBTITLE)
4145 {
4146 task_str = g_strdup_printf(_("pass %d (subtitle scan) of %d, "),
4147 status->pass, status->pass_count);
4148 }
4149 else
4150 {
4151 task_str = g_strdup_printf(_("pass %d of %d, "),
4152 status->pass, status->pass_count);
4153 }
4154 }
4155 else
4156 {
4157 task_str = g_strdup("");
4158 }
4159 if(status->seconds > -1)
4160 {
4161 if (status->rate_cur > 0.0)
4162 {
4163 status_str= g_strdup_printf(
4164 _("Encoding: %s%s%.2f %%"
4165 " (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)"),
4166 job_str, task_str,
4167 100.0 * status->progress,
4168 status->rate_cur, status->rate_avg, status->hours,
4169 status->minutes, status->seconds );
4170 }
4171 else
4172 {
4173 status_str= g_strdup_printf(
4174 _("Encoding: %s%s%.2f %%"
4175 " (ETA %02dh%02dm%02ds)"),
4176 job_str, task_str,
4177 100.0 * status->progress,
4178 status->hours, status->minutes, status->seconds );
4179 }
4180 }
4181 else
4182 {
4183 status_str= g_strdup_printf(
4184 _("Encoding: %s%s%.2f %%"),
4185 job_str, task_str,
4186 100.0 * status->progress );
4187 }
4188 g_free(task_str);
4189 g_free(job_str);
4190 return status_str;
4191 }
4192
4193 static gchar*
searching_status_string(signal_user_data_t * ud,ghb_instance_status_t * status)4194 searching_status_string(signal_user_data_t *ud, ghb_instance_status_t *status)
4195 {
4196 gchar *task_str, *job_str, *status_str;
4197 gint qcount;
4198 gint index;
4199
4200 qcount = ghb_array_len(ud->queue);
4201 index = ghb_find_queue_job(ud->queue, status->unique_id, NULL);
4202 if (qcount > 1)
4203 {
4204 job_str = g_strdup_printf(_("job %d of %d, "), index+1, qcount);
4205 }
4206 else
4207 {
4208 job_str = g_strdup("");
4209 }
4210 task_str = g_strdup_printf(_("Searching for start time, "));
4211 if(status->seconds > -1)
4212 {
4213 status_str= g_strdup_printf(
4214 _("Encoding: %s%s%.2f %%"
4215 " (ETA %02dh%02dm%02ds)"),
4216 job_str, task_str,
4217 100.0 * status->progress,
4218 status->hours, status->minutes, status->seconds );
4219 }
4220 else
4221 {
4222 status_str= g_strdup_printf(
4223 _("Encoding: %s%s%.2f %%"),
4224 job_str, task_str,
4225 100.0 * status->progress );
4226 }
4227 g_free(task_str);
4228 g_free(job_str);
4229 return status_str;
4230 }
4231
4232 static void
ghb_backend_events(signal_user_data_t * ud)4233 ghb_backend_events(signal_user_data_t *ud)
4234 {
4235 ghb_status_t status;
4236 gchar * status_str;
4237 GtkProgressBar * progress;
4238 GtkLabel * work_status;
4239 GhbValue * queueDict = NULL;
4240 gint index = -1;
4241 static gint prev_scan_state = -1;
4242 static gint prev_queue_state = -1;
4243 static gint event_sequence = 0;
4244
4245 event_sequence++;
4246 ghb_track_status();
4247 ghb_get_status(&status);
4248 if (prev_scan_state != status.scan.state ||
4249 prev_queue_state != status.queue.state)
4250 {
4251 ghb_queue_buttons_grey(ud);
4252 prev_scan_state = status.scan.state;
4253 prev_queue_state = status.queue.state;
4254 }
4255 progress = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "progressbar"));
4256 work_status = GTK_LABEL(GHB_WIDGET (ud->builder, "work_status"));
4257 if (status.scan.state == GHB_STATE_IDLE &&
4258 status.queue.state == GHB_STATE_IDLE)
4259 {
4260 static gboolean prev_dvdnav;
4261 gboolean dvdnav = ghb_dict_get_bool(ud->prefs, "use_dvdnav");
4262 if (dvdnav != prev_dvdnav)
4263 {
4264 hb_dvd_set_dvdnav(dvdnav);
4265 prev_dvdnav = dvdnav;
4266 }
4267 }
4268 // First handle the status of title scans
4269 // Then handle the status of the queue
4270 if (status.scan.state & GHB_STATE_SCANNING)
4271 {
4272 GtkProgressBar *scan_prog;
4273 GtkLabel *label;
4274
4275 scan_prog = GTK_PROGRESS_BAR(GHB_WIDGET (ud->builder, "scan_prog"));
4276 label = GTK_LABEL(GHB_WIDGET(ud->builder, "source_scan_label"));
4277
4278 if (status.scan.title_cur == 0)
4279 {
4280 status_str = g_strdup (_("Scanning..."));
4281 }
4282 else
4283 {
4284 if (status.scan.preview_cur == 0)
4285 status_str = g_strdup_printf(_("Scanning title %d of %d..."),
4286 status.scan.title_cur, status.scan.title_count );
4287 else
4288 status_str = g_strdup_printf(
4289 _("Scanning title %d of %d preview %d..."),
4290 status.scan.title_cur, status.scan.title_count,
4291 status.scan.preview_cur);
4292
4293 }
4294 gtk_label_set_text(label, status_str);
4295 g_free(status_str);
4296 if (status.scan.title_count > 0)
4297 {
4298 gtk_progress_bar_set_fraction(scan_prog, status.scan.progress);
4299 }
4300 }
4301 else if (status.scan.state & GHB_STATE_SCANDONE)
4302 {
4303 GtkWidget *widget;
4304
4305 widget = GHB_WIDGET(ud->builder, "sourcetoolbutton");
4306 gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(widget), "hb-source");
4307 gtk_tool_button_set_label(GTK_TOOL_BUTTON(widget), _("Open Source"));
4308 gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(widget), _("Choose Video Source"));
4309
4310 widget = GHB_WIDGET(ud->builder, "source_open");
4311 gtk_widget_set_sensitive(widget, TRUE);
4312 widget = GHB_WIDGET(ud->builder, "source_title_open");
4313 gtk_widget_set_sensitive(widget, TRUE);
4314
4315 hide_scan_progress(ud);
4316
4317 int title_id, titleindex;
4318 const hb_title_t *title;
4319 title_id = ghb_longest_title();
4320 title = ghb_lookup_title(title_id, &titleindex);
4321 ghb_update_ui_combo_box(ud, "title", NULL, FALSE);
4322 load_all_titles(ud, titleindex);
4323
4324 ghb_clear_scan_state(GHB_STATE_SCANDONE);
4325 // Are there really any titles.
4326 if (title == NULL)
4327 {
4328 ghb_ui_update(ud, "title", ghb_string_value("none"));
4329 }
4330 else
4331 {
4332 ghb_ui_update(ud, "title", ghb_int_value(title->index));
4333 }
4334
4335 if (ghb_queue_edit_settings != NULL)
4336 {
4337 // Switch to the correct title in the list
4338 ghb_ui_update(ud, "title",
4339 ghb_dict_get_value(ghb_queue_edit_settings, "title"));
4340
4341 // The above should cause the current title index to update
4342 title_id = ghb_dict_get_int(ud->settings, "title");
4343 title = ghb_lookup_title(title_id, &titleindex);
4344 ghb_array_replace(ud->settings_array, titleindex,
4345 ghb_queue_edit_settings);
4346 ud->settings = ghb_queue_edit_settings;
4347 ghb_load_settings(ud);
4348 ghb_queue_edit_settings = NULL;
4349 }
4350 }
4351
4352 if (status.queue.unique_id != 0)
4353 {
4354 index = ghb_find_queue_job(ud->queue, status.queue.unique_id,
4355 &queueDict);
4356 if ((status.queue.state & GHB_STATE_WORKING) &&
4357 (event_sequence % 50 == 0)) // check every 10 seconds
4358 {
4359 ghb_low_disk_check(ud);
4360 }
4361 }
4362
4363 if (status.queue.state & GHB_STATE_SCANNING)
4364 {
4365 // This needs to be in scanning and working since scanning
4366 // happens fast enough that it can be missed
4367 gtk_label_set_text(work_status, _("Scanning ..."));
4368 gtk_progress_bar_set_fraction(progress, status.queue.progress);
4369 ghb_queue_update_live_stats(ud, index, &status.queue);
4370 ghb_queue_progress_set_fraction(ud, index, status.queue.progress);
4371 }
4372 else if (status.queue.state & GHB_STATE_SCANDONE)
4373 {
4374 ghb_clear_queue_state(GHB_STATE_SCANDONE);
4375 }
4376 else if (status.queue.state & GHB_STATE_PAUSED)
4377 {
4378 gtk_label_set_text (work_status, _("Paused"));
4379 ghb_queue_update_live_stats(ud, index, &status.queue);
4380 }
4381 else if (status.queue.state & GHB_STATE_SEARCHING)
4382 {
4383 gchar *status_str;
4384
4385 status_str = searching_status_string(ud, &status.queue);
4386 gtk_label_set_text (work_status, status_str);
4387 gtk_progress_bar_set_fraction(progress, status.queue.progress);
4388 ghb_queue_progress_set_fraction(ud, index, status.queue.progress);
4389 g_free(status_str);
4390 }
4391 else if (status.queue.state & GHB_STATE_WORKING)
4392 {
4393 gchar *status_str;
4394
4395 status_str = working_status_string(ud, &status.queue);
4396 gtk_label_set_text (work_status, status_str);
4397 gtk_progress_bar_set_fraction (progress, status.queue.progress);
4398 ghb_queue_update_live_stats(ud, index, &status.queue);
4399 ghb_queue_progress_set_fraction(ud, index, status.queue.progress);
4400 g_free(status_str);
4401 }
4402 else if (status.queue.state & GHB_STATE_WORKDONE)
4403 {
4404 gint qstatus;
4405
4406 switch (status.queue.error)
4407 {
4408 case GHB_ERROR_NONE:
4409 gtk_label_set_text(work_status, _("Encode Done!"));
4410 qstatus = GHB_QUEUE_DONE;
4411 break;
4412 case GHB_ERROR_CANCELED:
4413 gtk_label_set_text(work_status, _("Encode Canceled."));
4414 qstatus = GHB_QUEUE_CANCELED;
4415 break;
4416 case GHB_ERROR_FAIL:
4417 default:
4418 gtk_label_set_text(work_status, _("Encode Failed."));
4419 qstatus = GHB_QUEUE_FAIL;
4420 }
4421 if (queueDict != NULL)
4422 {
4423 GhbValue *uiDict = ghb_dict_get(queueDict, "uiSettings");
4424 ghb_dict_set_int(uiDict, "job_status", qstatus);
4425 time_t now = time(NULL);
4426 ghb_dict_set_int(uiDict, "job_finish_time", now);
4427 ghb_dict_set_int(uiDict, "job_pause_time_ms", status.queue.paused);
4428 }
4429 ghb_queue_update_status_icon(ud, index);
4430 ghb_queue_update_live_stats(ud, index, &status.queue);
4431 gtk_progress_bar_set_fraction(progress, 1.0);
4432 ghb_queue_progress_set_visible(ud, index, FALSE);
4433 ghb_queue_progress_set_fraction(ud, index, 1.0);
4434
4435 ghb_clear_queue_state(GHB_STATE_WORKDONE);
4436 if (ud->job_activity_log)
4437 g_io_channel_unref(ud->job_activity_log);
4438 ud->job_activity_log = NULL;
4439 if (ghb_dict_get_bool(ud->prefs, "RemoveFinishedJobs") &&
4440 status.queue.error == GHB_ERROR_NONE)
4441 {
4442 ghb_queue_remove_row(ud, index);
4443 }
4444 if (ud->cancel_encode != GHB_CANCEL_ALL &&
4445 ud->cancel_encode != GHB_CANCEL_FINISH)
4446 {
4447 ghb_start_next_job(ud);
4448 }
4449 else
4450 {
4451 ghb_uninhibit_suspend(ud);
4452 gtk_widget_hide(GTK_WIDGET(progress));
4453 ghb_dict_set_bool(ud->globals, "SkipDiskFreeCheck", FALSE);
4454 }
4455 ghb_save_queue(ud->queue);
4456 ud->cancel_encode = GHB_CANCEL_NONE;
4457 }
4458 else if (status.queue.state & GHB_STATE_MUXING)
4459 {
4460 gtk_label_set_text (work_status, _("Muxing: This may take a while..."));
4461 }
4462
4463 if (status.live.state & GHB_STATE_WORKING)
4464 {
4465 GtkProgressBar *live_progress;
4466 live_progress = GTK_PROGRESS_BAR(
4467 GHB_WIDGET(ud->builder, "live_encode_progress"));
4468 status_str = working_status_string(ud, &status.live);
4469 gtk_progress_bar_set_text (live_progress, status_str);
4470 gtk_progress_bar_set_fraction (live_progress, status.live.progress);
4471 g_free(status_str);
4472 }
4473 if (status.live.state & GHB_STATE_WORKDONE)
4474 {
4475 switch( status.live.error )
4476 {
4477 case GHB_ERROR_NONE:
4478 {
4479 ghb_live_encode_done(ud, TRUE);
4480 } break;
4481 default:
4482 {
4483 ghb_live_encode_done(ud, FALSE);
4484 } break;
4485 }
4486 ghb_clear_live_state(GHB_STATE_WORKDONE);
4487 }
4488 }
4489
4490 G_MODULE_EXPORT gboolean
ghb_timer_cb(gpointer data)4491 ghb_timer_cb(gpointer data)
4492 {
4493 signal_user_data_t *ud = (signal_user_data_t*)data;
4494
4495 ghb_live_preview_progress(ud);
4496 ghb_backend_events(ud);
4497 if (update_preview)
4498 {
4499 g_debug("Updating preview\n");
4500 ghb_set_preview_image(ud);
4501 update_preview = FALSE;
4502 }
4503
4504 #if !defined(_NO_UPDATE_CHECK)
4505 if (!appcast_busy)
4506 {
4507 const gchar *updates;
4508 updates = ghb_dict_get_string(ud->prefs, "check_updates");
4509 gint64 duration = 0;
4510 if (strcmp(updates, "daily") == 0)
4511 duration = 60 * 60 * 24;
4512 else if (strcmp(updates, "weekly") == 0)
4513 duration = 60 * 60 * 24 * 7;
4514 else if (strcmp(updates, "monthly") == 0)
4515 duration = 60 * 60 * 24 * 7;
4516
4517 if (duration != 0)
4518 {
4519 gint64 last;
4520 time_t tt;
4521
4522 last = ghb_dict_get_int(ud->prefs, "last_update_check");
4523 time(&tt);
4524 if (last + duration < tt)
4525 {
4526 ghb_dict_set_int(ud->prefs,
4527 "last_update_check", tt);
4528 ghb_pref_save(ud->prefs, "last_update_check");
4529 GHB_THREAD_NEW("Update Check", (GThreadFunc)ghb_check_update, ud);
4530 }
4531 }
4532 }
4533 #endif
4534 return TRUE;
4535 }
4536
4537 static void
append_activity(GtkTextBuffer * tb,const char * text,gsize length)4538 append_activity(GtkTextBuffer * tb, const char * text, gsize length)
4539 {
4540 if (text == NULL || length <= 0)
4541 {
4542 return;
4543 }
4544 GtkTextIter iter;
4545
4546 gtk_text_buffer_get_end_iter(tb, &iter);
4547 gtk_text_buffer_insert(tb, &iter, text, -1);
4548 }
4549
4550 G_MODULE_EXPORT gboolean
ghb_log_cb(GIOChannel * source,GIOCondition cond,gpointer data)4551 ghb_log_cb(GIOChannel *source, GIOCondition cond, gpointer data)
4552 {
4553 gchar *text = NULL;
4554 gsize length, outlength;
4555 GError *gerror = NULL;
4556 GIOStatus status;
4557
4558 signal_user_data_t *ud = (signal_user_data_t*)data;
4559
4560 status = g_io_channel_read_line (source, &text, &length, NULL, &gerror);
4561 // Trim nils from end of text, they cause g_io_channel_write_chars to
4562 // fail with an assertion that aborts
4563 while (length > 0 && text[length-1] == 0)
4564 length--;
4565 if (text != NULL && length > 0)
4566 {
4567 gchar *utf8_text;
4568
4569 // Assume logging is in current locale
4570 utf8_text = g_locale_to_utf8(text, -1, NULL, &length, NULL);
4571 if (utf8_text != NULL)
4572 {
4573 // Write to activity windows
4574 append_activity(ud->activity_buffer, utf8_text, length);
4575 append_activity(ud->queue_activity_buffer, utf8_text, length);
4576
4577 // Write log files
4578 #if defined(_WIN32)
4579 gsize one = 1;
4580 utf8_text[length-1] = '\r';
4581 #endif
4582 if (ud->activity_log != NULL)
4583 {
4584 g_io_channel_write_chars (ud->activity_log, utf8_text,
4585 length, &outlength, NULL);
4586 #if defined(_WIN32)
4587 g_io_channel_write_chars (ud->activity_log, "\n",
4588 one, &one, NULL);
4589 #endif
4590 g_io_channel_flush(ud->activity_log, NULL);
4591 }
4592 if (ud->job_activity_log)
4593 {
4594 g_io_channel_write_chars (ud->job_activity_log, utf8_text,
4595 length, &outlength, NULL);
4596 #if defined(_WIN32)
4597 g_io_channel_write_chars (ud->activity_log, "\n",
4598 one, &outlength, NULL);
4599 #endif
4600 g_io_channel_flush(ud->job_activity_log, NULL);
4601 }
4602 g_free(utf8_text);
4603 }
4604 }
4605 if (text != NULL)
4606 g_free(text);
4607
4608 if (status != G_IO_STATUS_NORMAL)
4609 {
4610 // This should never happen, but if it does I would get into an
4611 // infinite loop. Returning false removes this callback.
4612 g_warning("Error while reading activity from pipe");
4613 if (gerror != NULL)
4614 {
4615 g_warning("%s", gerror->message);
4616 g_error_free (gerror);
4617 }
4618 return FALSE;
4619 }
4620 if (gerror != NULL)
4621 g_error_free (gerror);
4622 return TRUE;
4623 }
4624
4625 static void
update_activity_labels(signal_user_data_t * ud,gboolean active)4626 update_activity_labels(signal_user_data_t *ud, gboolean active)
4627 {
4628 GtkToolButton *button;
4629
4630 button = GTK_TOOL_BUTTON(GHB_WIDGET(ud->builder, "show_activity"));
4631 gtk_tool_button_set_label(button, "Activity");
4632 }
4633
4634 G_MODULE_EXPORT void
show_activity_action_cb(GSimpleAction * action,GVariant * value,signal_user_data_t * ud)4635 show_activity_action_cb(GSimpleAction *action, GVariant *value,
4636 signal_user_data_t *ud)
4637 {
4638 GtkWidget * activity_window;
4639 gboolean state = g_variant_get_boolean(value);
4640
4641 g_simple_action_set_state(action, value);
4642 activity_window = GHB_WIDGET(ud->builder, "activity_window");
4643 gtk_widget_set_visible(activity_window, state);
4644 update_activity_labels(ud, state);
4645 }
4646
4647 G_MODULE_EXPORT gboolean
activity_window_delete_cb(GtkWidget * xwidget,GdkEvent * event,signal_user_data_t * ud)4648 activity_window_delete_cb(
4649 GtkWidget *xwidget,
4650 #if !GTK_CHECK_VERSION(3, 90, 0)
4651 GdkEvent *event,
4652 #endif
4653 signal_user_data_t *ud)
4654 {
4655 gtk_widget_set_visible(xwidget, FALSE);
4656 GtkWidget *widget = GHB_WIDGET (ud->builder, "show_activity");
4657 gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), FALSE);
4658 return TRUE;
4659 }
4660
4661 void
ghb_log(gchar * log,...)4662 ghb_log(gchar *log, ...)
4663 {
4664 va_list args;
4665 time_t _now;
4666 struct tm *now;
4667 gchar fmt[362];
4668
4669 _now = time(NULL);
4670 now = localtime( &_now );
4671 snprintf(fmt, 362, "[%02d:%02d:%02d] gtkgui: %s\n",
4672 now->tm_hour, now->tm_min, now->tm_sec, log);
4673 va_start(args, log);
4674 vfprintf(stderr, fmt, args);
4675 va_end(args);
4676 }
4677
4678 void
ghb_browse_uri(signal_user_data_t * ud,const gchar * uri)4679 ghb_browse_uri(signal_user_data_t *ud, const gchar *uri)
4680 {
4681 #if defined(_WIN32)
4682 ShellExecute(NULL, "open", uri, NULL, NULL, SW_SHOWNORMAL);
4683 #else
4684 GtkWindow * parent;
4685 gboolean result;
4686
4687 parent = GTK_WINDOW(GHB_WIDGET(ud->builder, "hb_window"));
4688 result = gtk_show_uri_on_window(parent, uri, GDK_CURRENT_TIME, NULL);
4689 if (result) return;
4690 char *argv[] =
4691 {"xdg-open",NULL,NULL,NULL};
4692 argv[1] = (gchar*)uri;
4693 result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
4694 NULL, NULL, NULL);
4695 if (result) return;
4696
4697 argv[0] = "gnome-open";
4698 result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
4699 NULL, NULL, NULL);
4700 if (result) return;
4701
4702 argv[0] = "kfmclient";
4703 argv[1] = "exec";
4704 argv[2] = (gchar*)uri;
4705 result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
4706 NULL, NULL, NULL);
4707 if (result) return;
4708
4709 argv[0] = "firefox";
4710 argv[1] = (gchar*)uri;
4711 argv[2] = NULL;
4712 result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL,
4713 NULL, NULL, NULL);
4714 #endif
4715 }
4716
4717 G_MODULE_EXPORT void
about_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)4718 about_action_cb(GSimpleAction *action, GVariant *param, signal_user_data_t *ud)
4719 {
4720 GtkWidget *widget = GHB_WIDGET (ud->builder, "hb_about");
4721 gchar *ver;
4722
4723 ver = g_strdup_printf("%s (%s)", HB_PROJECT_VERSION, HB_PROJECT_HOST_ARCH);
4724 gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(widget), ver);
4725 g_free(ver);
4726 gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(widget),
4727 HB_PROJECT_URL_WEBSITE);
4728 gtk_about_dialog_set_website_label(GTK_ABOUT_DIALOG(widget),
4729 HB_PROJECT_URL_WEBSITE);
4730 gtk_dialog_run(GTK_DIALOG(widget));
4731 gtk_widget_hide(widget);
4732 }
4733
4734 #define HB_DOCS "https://handbrake.fr/docs/"
4735
4736 G_MODULE_EXPORT void
guide_action_cb(GSimpleAction * action,GVariant * param,signal_user_data_t * ud)4737 guide_action_cb(GSimpleAction *action, GVariant *param, signal_user_data_t *ud)
4738 {
4739 ghb_browse_uri(ud, HB_DOCS);
4740 }
4741
4742 static void
update_queue_labels(signal_user_data_t * ud)4743 update_queue_labels(signal_user_data_t *ud)
4744 {
4745 GtkToolButton *button;
4746 gint pending;
4747 const gchar *show_hide;
4748 gchar *str;
4749
4750 button = GTK_TOOL_BUTTON(GHB_WIDGET(ud->builder, "show_queue"));
4751 pending = queue_pending_count(ud->queue);
4752
4753 show_hide = _("Queue");
4754 if (pending > 0)
4755 {
4756 str = g_strdup_printf("%s (%d)", show_hide, pending);
4757 }
4758 else
4759 {
4760 str = g_strdup_printf("%s", show_hide);
4761 }
4762 gtk_tool_button_set_label(button, str);
4763 g_free(str);
4764 }
4765
4766 static void
presets_window_set_visible(signal_user_data_t * ud,gboolean visible)4767 presets_window_set_visible(signal_user_data_t *ud, gboolean visible)
4768 {
4769 GtkWidget * presets_window;
4770 GtkWidget * hb_window;
4771
4772 hb_window = GHB_WIDGET(ud->builder, "hb_window");
4773 if (!gtk_widget_is_visible(hb_window))
4774 {
4775 return;
4776 }
4777
4778 int w, h;
4779 w = ghb_dict_get_int(ud->prefs, "presets_window_width");
4780 h = ghb_dict_get_int(ud->prefs, "presets_window_height");
4781
4782 presets_window = GHB_WIDGET(ud->builder, "presets_window");
4783 if (w > 200 && h > 200)
4784 {
4785 gtk_window_resize(GTK_WINDOW(presets_window), w, h);
4786 }
4787 gtk_widget_set_visible(presets_window, visible);
4788
4789 #if !GTK_CHECK_VERSION(3, 90, 0)
4790 // TODO: Is this possible in GTK4?
4791 int x, y;
4792
4793 if (visible)
4794 {
4795 gtk_window_get_position(GTK_WINDOW(hb_window), &x, &y);
4796 x -= w + 10;
4797 if (x < 0)
4798 {
4799 gtk_window_move(GTK_WINDOW(hb_window), w + 10, y);
4800 x = 0;
4801 }
4802 gtk_window_move(GTK_WINDOW(presets_window), x, y);
4803 }
4804 #endif
4805 }
4806
4807 G_MODULE_EXPORT void
show_presets_action_cb(GSimpleAction * action,GVariant * value,signal_user_data_t * ud)4808 show_presets_action_cb(GSimpleAction *action, GVariant *value,
4809 signal_user_data_t *ud)
4810 {
4811 gboolean state = g_variant_get_boolean(value);
4812
4813 g_simple_action_set_state(action, value);
4814 presets_window_set_visible(ud, state);
4815 }
4816
4817 void
debug_log_handler(const gchar * domain,GLogLevelFlags flags,const gchar * msg,gpointer data)4818 debug_log_handler(const gchar *domain, GLogLevelFlags flags, const gchar *msg, gpointer data)
4819 {
4820 signal_user_data_t *ud = (signal_user_data_t*)data;
4821
4822 if (ud->debug)
4823 {
4824 printf("%s: %s\n", domain, msg);
4825 }
4826 }
4827
4828 void
warn_log_handler(const gchar * domain,GLogLevelFlags flags,const gchar * msg,gpointer data)4829 warn_log_handler(const gchar *domain, GLogLevelFlags flags, const gchar *msg, gpointer data)
4830 {
4831 printf("%s: %s\n", domain, msg);
4832 }
4833
4834 void
ghb_hbfd(signal_user_data_t * ud,gboolean hbfd)4835 ghb_hbfd(signal_user_data_t *ud, gboolean hbfd)
4836 {
4837 GtkWidget *widget;
4838 g_debug("ghb_hbfd");
4839 widget = GHB_WIDGET(ud->builder, "queue_pause");
4840 gtk_widget_set_visible(widget, !hbfd);
4841 widget = GHB_WIDGET(ud->builder, "queue_add");
4842 gtk_widget_set_visible(widget, !hbfd);
4843 widget = GHB_WIDGET(ud->builder, "show_queue");
4844 gtk_widget_set_visible(widget, !hbfd);
4845 widget = GHB_WIDGET(ud->builder, "show_activity");
4846 gtk_widget_set_visible(widget, !hbfd);
4847
4848 widget = GHB_WIDGET(ud->builder, "container_box");
4849 gtk_widget_set_visible(widget, !hbfd);
4850 widget = GHB_WIDGET(ud->builder, "SettingsStackSwitcher");
4851 gtk_widget_set_visible(widget, !hbfd);
4852 widget = GHB_WIDGET(ud->builder, "SettingsStack");
4853 gtk_widget_set_visible(widget, !hbfd);
4854 widget = GHB_WIDGET(ud->builder, "presets_save");
4855 gtk_widget_set_visible(widget, !hbfd);
4856 widget = GHB_WIDGET(ud->builder, "presets_remove");
4857 gtk_widget_set_visible(widget, !hbfd);
4858 widget = GHB_WIDGET (ud->builder, "hb_window");
4859 gtk_window_resize(GTK_WINDOW(widget), 16, 16);
4860
4861 }
4862
4863 G_MODULE_EXPORT void
hbfd_action_cb(GSimpleAction * action,GVariant * value,signal_user_data_t * ud)4864 hbfd_action_cb(GSimpleAction *action, GVariant *value, signal_user_data_t *ud)
4865 {
4866 gboolean state = g_variant_get_boolean(value);
4867
4868 g_simple_action_set_state(action, value);
4869 ghb_dict_set(ud->prefs, "hbfd", ghb_boolean_value(state));
4870 ghb_hbfd(ud, state);
4871 ghb_pref_save(ud->prefs, "hbfd");
4872 }
4873
4874 G_MODULE_EXPORT void
activity_font_changed_cb(GtkWidget * widget,signal_user_data_t * ud)4875 activity_font_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
4876 {
4877
4878 ghb_widget_to_setting(ud->prefs, widget);
4879 const gchar *name = ghb_get_setting_key(widget);
4880 ghb_pref_set(ud->prefs, name);
4881
4882 int size = ghb_dict_get_int(ud->prefs, "ActivityFontSize");
4883
4884 #if GTK_CHECK_VERSION(3, 16, 0)
4885 const gchar *css_template =
4886 " \n\
4887 #activity_view \n\
4888 { \n\
4889 font-family: monospace; \n\
4890 font-size: %dpt; \n\
4891 font-weight: lighter; \n\
4892 } \n\
4893 ";
4894 char * css = g_strdup_printf(css_template, size);
4895 GtkCssProvider * provider = gtk_css_provider_new();
4896
4897 ghb_css_provider_load_from_data(provider, css, -1);
4898 GtkWidget * win = GHB_WIDGET(ud->builder, "hb_window");
4899 #if GTK_CHECK_VERSION(3, 90, 0)
4900 GdkDisplay *dd = gtk_widget_get_display(win);
4901 gtk_style_context_add_provider_for_display(dd,
4902 GTK_STYLE_PROVIDER(provider),
4903 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
4904 #else
4905 GdkScreen *ss = gtk_window_get_screen(GTK_WINDOW(win));
4906 gtk_style_context_add_provider_for_screen(ss,
4907 GTK_STYLE_PROVIDER(provider),
4908 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
4909 #endif
4910 g_object_unref(provider);
4911 g_free(css);
4912 #else
4913 const gchar * font_template = "monospace %d";
4914 char * font = g_strdup_printf(font_template, size);
4915 PangoFontDescription * font_desc;
4916 GtkTextView * textview;
4917
4918 font_desc = pango_font_description_from_string(font);
4919 textview = GTK_TEXT_VIEW(GHB_WIDGET(ud->builder, "activity_view"));
4920 gtk_widget_override_font(GTK_WIDGET(textview), font_desc);
4921 pango_font_description_free(font_desc);
4922 g_free(font);
4923 #endif
4924 }
4925
4926 G_MODULE_EXPORT void
when_complete_changed_cb(GtkWidget * widget,signal_user_data_t * ud)4927 when_complete_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
4928 {
4929 GhbValue * value = ghb_widget_value(widget);
4930 ghb_ui_update(ud, "QueueWhenComplete", value);
4931 ghb_value_free(&value);
4932
4933 ghb_widget_to_setting (ud->prefs, widget);
4934
4935 ghb_check_dependency(ud, widget, NULL);
4936 const gchar *name = ghb_get_setting_key(widget);
4937 ghb_pref_set(ud->prefs, name);
4938 ghb_prefs_store();
4939 }
4940
4941 G_MODULE_EXPORT void
queue_when_complete_changed_cb(GtkWidget * widget,signal_user_data_t * ud)4942 queue_when_complete_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
4943 {
4944 GhbValue * value = ghb_widget_value(widget);
4945 ghb_ui_update(ud, "WhenComplete", value);
4946 ghb_value_free(&value);
4947 }
4948
4949 G_MODULE_EXPORT void
pref_changed_cb(GtkWidget * widget,signal_user_data_t * ud)4950 pref_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
4951 {
4952 ghb_widget_to_setting (ud->prefs, widget);
4953
4954 ghb_check_dependency(ud, widget, NULL);
4955 const gchar *name = ghb_get_setting_key(widget);
4956 ghb_pref_set(ud->prefs, name);
4957 }
4958
4959 G_MODULE_EXPORT void
log_level_changed_cb(GtkWidget * widget,signal_user_data_t * ud)4960 log_level_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
4961 {
4962 pref_changed_cb(widget, ud);
4963 int level = ghb_dict_get_int(ud->prefs, "LoggingLevel");
4964 ghb_log_level_set(level);
4965 }
4966
4967 G_MODULE_EXPORT void
use_m4v_changed_cb(GtkWidget * widget,signal_user_data_t * ud)4968 use_m4v_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
4969 {
4970 g_debug("use_m4v_changed_cb");
4971 ghb_widget_to_setting (ud->prefs, widget);
4972 ghb_check_dependency(ud, widget, NULL);
4973 const gchar *name = ghb_get_setting_key(widget);
4974 ghb_pref_set(ud->prefs, name);
4975 ghb_update_destination_extension(ud);
4976 }
4977
4978 G_MODULE_EXPORT void
vqual_granularity_changed_cb(GtkWidget * widget,signal_user_data_t * ud)4979 vqual_granularity_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
4980 {
4981 g_debug("vqual_granularity_changed_cb");
4982 ghb_widget_to_setting (ud->prefs, widget);
4983 ghb_check_dependency(ud, widget, NULL);
4984
4985 const gchar *name = ghb_get_setting_key(widget);
4986 ghb_pref_set(ud->prefs, name);
4987
4988 float val, vqmin, vqmax, step, page;
4989 int inverted, digits;
4990
4991 ghb_vquality_range(ud, &vqmin, &vqmax, &step, &page, &digits, &inverted);
4992 val = ghb_dict_get_double(ud->settings, "VideoQualitySlider");
4993 ghb_scale_configure(ud, "VideoQualitySlider", val, vqmin, vqmax,
4994 step, page, digits, inverted);
4995 }
4996
4997 G_MODULE_EXPORT void
tweaks_changed_cb(GtkWidget * widget,signal_user_data_t * ud)4998 tweaks_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
4999 {
5000 g_debug("tweaks_changed_cb");
5001 ghb_widget_to_setting (ud->prefs, widget);
5002 const gchar *name = ghb_get_setting_key(widget);
5003 ghb_pref_set(ud->prefs, name);
5004 }
5005
5006 G_MODULE_EXPORT void
hbfd_feature_changed_cb(GtkWidget * widget,signal_user_data_t * ud)5007 hbfd_feature_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
5008 {
5009 ghb_widget_to_setting (ud->prefs, widget);
5010 const gchar *name = ghb_get_setting_key(widget);
5011 ghb_pref_set(ud->prefs, name);
5012
5013 gboolean hbfd = ghb_dict_get_bool(ud->prefs, "hbfd_feature");
5014 if (hbfd)
5015 {
5016 const GhbValue *val;
5017 val = ghb_dict_get_value(ud->prefs, "hbfd");
5018 ghb_ui_settings_update(ud, ud->prefs, "hbfd", val);
5019 }
5020 widget = GHB_WIDGET(ud->builder, "hbfd");
5021 gtk_widget_set_visible(widget, hbfd);
5022 }
5023
5024 gboolean
ghb_file_menu_add_dvd(signal_user_data_t * ud)5025 ghb_file_menu_add_dvd(signal_user_data_t *ud)
5026 {
5027 GList *link, *drives;
5028 static GList *dvd_items = NULL;
5029
5030 g_debug("ghb_file_menu_add_dvd()");
5031 GtkMenu *menu = GTK_MENU(GHB_WIDGET(ud->builder, "file_submenu"));
5032
5033 // Clear previous dvd items from list
5034 link = dvd_items;
5035 while (link != NULL)
5036 {
5037 GtkWidget * widget = GTK_WIDGET(link->data);
5038 // widget_destroy automatically removes widget from container.
5039 gtk_widget_destroy(widget);
5040 link = link->next;
5041 }
5042 g_list_free(dvd_items);
5043 dvd_items = NULL;
5044
5045 int pos = 5;
5046 link = drives = dvd_device_list();
5047 if (drives != NULL)
5048 {
5049 GtkWidget *widget = gtk_separator_menu_item_new();
5050 dvd_items = g_list_append(dvd_items, (gpointer)widget);
5051
5052 gtk_menu_shell_insert(GTK_MENU_SHELL(menu), widget, pos++);
5053 gtk_widget_set_visible(widget, TRUE);
5054
5055 while (link != NULL)
5056 {
5057 GtkWidget *widget;
5058 gchar *drive = get_dvd_device_name(link->data);
5059 gchar *name = get_dvd_volume_name(link->data);
5060
5061 widget = gtk_menu_item_new_with_label(name);
5062 gtk_buildable_set_name(GTK_BUILDABLE(widget), drive);
5063 gtk_widget_set_tooltip_text(widget, _("Scan this DVD source"));
5064
5065 dvd_items = g_list_append(dvd_items, (gpointer)widget);
5066 gtk_menu_shell_insert(GTK_MENU_SHELL(menu), widget, pos++);
5067
5068 gtk_widget_set_visible(widget, TRUE);
5069
5070 // Connect signal to action (menu item)
5071 g_signal_connect(widget, "activate",
5072 (GCallback)dvd_source_activate_cb, ud);
5073 g_free(name);
5074 g_free(drive);
5075 free_drive(link->data);
5076 link = link->next;
5077 }
5078
5079 g_list_free(drives);
5080 }
5081
5082 return FALSE;
5083 }
5084
5085 gboolean ghb_is_cd(GDrive *gd);
5086
5087 static GList*
dvd_device_list()5088 dvd_device_list()
5089 {
5090 GList *dvd_devices = NULL;
5091
5092 #if defined(_WIN32)
5093 gint ii, drives;
5094 gchar drive[5];
5095
5096 strcpy(drive, "A:" G_DIR_SEPARATOR_S);
5097 drives = GetLogicalDrives();
5098 for (ii = 0; ii < 26; ii++)
5099 {
5100 if (drives & 0x01)
5101 {
5102 guint dtype;
5103
5104 drive[0] = 'A' + ii;
5105 dtype = GetDriveType(drive);
5106 if (dtype == DRIVE_CDROM)
5107 {
5108 dvd_devices = g_list_append(dvd_devices,
5109 (gpointer)g_strdup(drive));
5110 }
5111 }
5112 drives >>= 1;
5113 }
5114 #else
5115 GVolumeMonitor *gvm;
5116 GList *drives, *link;
5117
5118 gvm = g_volume_monitor_get ();
5119 drives = g_volume_monitor_get_connected_drives (gvm);
5120 link = drives;
5121 while (link != NULL)
5122 {
5123 GDrive *gd;
5124
5125 gd = (GDrive*)link->data;
5126 if (ghb_is_cd(gd))
5127 {
5128 dvd_devices = g_list_append(dvd_devices, gd);
5129 }
5130 else
5131 g_object_unref (gd);
5132 link = link->next;
5133 }
5134 g_list_free(drives);
5135 #endif
5136
5137 return dvd_devices;
5138 }
5139
5140 #if defined(__linux__) && defined(_HAVE_GUDEV)
5141 static GUdevClient *udev_ctx = NULL;
5142 #endif
5143
5144 gboolean
ghb_is_cd(GDrive * gd)5145 ghb_is_cd(GDrive *gd)
5146 {
5147 #if defined(__linux__) && defined(_HAVE_GUDEV)
5148 gchar *device;
5149 GUdevDevice *udd;
5150
5151 if (udev_ctx == NULL)
5152 return FALSE;
5153
5154 device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
5155 if (device == NULL)
5156 return FALSE;
5157
5158 udd = g_udev_client_query_by_device_file(udev_ctx, device);
5159 if (udd == NULL)
5160 {
5161 g_message("udev: Failed to lookup device %s", device);
5162 g_free(device);
5163 return FALSE;
5164 }
5165 g_free(device);
5166
5167 gint val;
5168 val = g_udev_device_get_property_as_int(udd, "ID_CDROM_DVD");
5169 g_object_unref(udd);
5170 if (val == 1)
5171 return TRUE;
5172
5173 return FALSE;
5174 #else
5175 return FALSE;
5176 #endif
5177 }
5178
5179 void
ghb_udev_init()5180 ghb_udev_init()
5181 {
5182 #if defined(__linux__) && defined(_HAVE_GUDEV)
5183 udev_ctx = g_udev_client_new(NULL);
5184 #endif
5185 }
5186
5187 #if defined(_WIN32)
5188 static void
handle_media_change(const gchar * device,gboolean insert,signal_user_data_t * ud)5189 handle_media_change(const gchar *device, gboolean insert, signal_user_data_t *ud)
5190 {
5191 guint dtype;
5192 static gint ins_count = 0;
5193 static gint rem_count = 0;
5194
5195 // The media change event in windows bounces around a bit
5196 // so I debounce it here
5197 // DVD insertion detected. Scan it.
5198 dtype = GetDriveType(device);
5199 if (dtype != DRIVE_CDROM)
5200 return;
5201 if (insert)
5202 {
5203 rem_count = 0;
5204 ins_count++;
5205 if (ins_count == 2)
5206 {
5207 GHB_THREAD_NEW("Cache Volume Names",
5208 (GThreadFunc)ghb_cache_volnames, ud);
5209 if (ghb_dict_get_bool(ud->prefs, "AutoScan") &&
5210 ud->current_dvd_device != NULL &&
5211 strcmp(device, ud->current_dvd_device) == 0)
5212 {
5213 show_scan_progress(ud);
5214 gint preview_count;
5215 preview_count = ghb_dict_get_int(ud->prefs, "preview_count");
5216 ghb_dict_set_string(ud->globals, "scan_source", device);
5217 start_scan(ud, device, 0, preview_count);
5218 }
5219 }
5220 }
5221 else
5222 {
5223 ins_count = 0;
5224 rem_count++;
5225 if (rem_count == 2)
5226 {
5227 GHB_THREAD_NEW("Cache Volume Names",
5228 (GThreadFunc)ghb_cache_volnames, ud);
5229 if (ud->current_dvd_device != NULL &&
5230 strcmp(device, ud->current_dvd_device) == 0)
5231 {
5232 ghb_hb_cleanup(TRUE);
5233 prune_logs(ud);
5234 ghb_dict_set_string(ud->globals, "scan_source", "/dev/null");
5235 start_scan(ud, "/dev/null", 0, 1);
5236 }
5237 }
5238 }
5239 }
5240
5241 static gchar
FindDriveFromMask(ULONG unitmask)5242 FindDriveFromMask(ULONG unitmask)
5243 {
5244 gchar cc;
5245 for (cc = 0; cc < 26; cc++)
5246 {
5247 if (unitmask & 0x01)
5248 return 'A' + cc;
5249 unitmask >>= 1;
5250 }
5251 return 0;
5252 }
5253
5254 void
wm_drive_changed(MSG * msg,signal_user_data_t * ud)5255 wm_drive_changed(MSG *msg, signal_user_data_t *ud)
5256 {
5257 PDEV_BROADCAST_HDR bch = (PDEV_BROADCAST_HDR)msg->lParam;
5258 gchar drive[4];
5259
5260 g_strlcpy(drive, "A:" G_DIR_SEPARATOR_S, 4);
5261 switch (msg->wParam)
5262 {
5263 case DBT_DEVICEARRIVAL:
5264 {
5265 if (bch->dbch_devicetype == DBT_DEVTYP_VOLUME)
5266 {
5267 PDEV_BROADCAST_VOLUME bcv = (PDEV_BROADCAST_VOLUME)bch;
5268
5269 if (bcv->dbcv_flags & DBTF_MEDIA)
5270 {
5271 drive[0] = FindDriveFromMask(bcv->dbcv_unitmask);
5272 handle_media_change(drive, TRUE, ud);
5273 }
5274 }
5275 } break;
5276
5277 case DBT_DEVICEREMOVECOMPLETE:
5278 {
5279 if (bch->dbch_devicetype == DBT_DEVTYP_VOLUME)
5280 {
5281 PDEV_BROADCAST_VOLUME bcv = (PDEV_BROADCAST_VOLUME)bch;
5282
5283 if (bcv->dbcv_flags & DBTF_MEDIA)
5284 {
5285 drive[0] = FindDriveFromMask(bcv->dbcv_unitmask);
5286 handle_media_change(drive, FALSE, ud);
5287 }
5288 }
5289 } break;
5290 default: ;
5291 }
5292 }
5293
5294 #else
5295
5296 G_MODULE_EXPORT void
drive_changed_cb(GVolumeMonitor * gvm,GDrive * gd,signal_user_data_t * ud)5297 drive_changed_cb(GVolumeMonitor *gvm, GDrive *gd, signal_user_data_t *ud)
5298 {
5299 gchar *device;
5300 gint state;
5301
5302 g_debug("drive_changed_cb()");
5303 GHB_THREAD_NEW("Cache Volume Names", (GThreadFunc)ghb_cache_volnames, ud);
5304
5305 state = ghb_get_scan_state();
5306 device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
5307 if (ud->current_dvd_device == NULL ||
5308 strcmp(device, ud->current_dvd_device) != 0 ||
5309 state != GHB_STATE_IDLE )
5310 {
5311 return;
5312 }
5313 if (g_drive_has_media(gd))
5314 {
5315 if (ghb_dict_get_bool(ud->prefs, "AutoScan"))
5316 {
5317 show_scan_progress(ud);
5318 gint preview_count;
5319 preview_count = ghb_dict_get_int(ud->prefs, "preview_count");
5320 ghb_dict_set_string(ud->globals, "scan_source", device);
5321 start_scan(ud, device, 0, preview_count);
5322 }
5323 }
5324 else
5325 {
5326 ghb_hb_cleanup(TRUE);
5327 prune_logs(ud);
5328 ghb_dict_set_string(ud->globals, "scan_source", "/dev/null");
5329 start_scan(ud, "/dev/null", 0, 1);
5330 }
5331 }
5332 #endif
5333
5334 typedef struct
5335 {
5336 gint count;
5337 signal_user_data_t *ud;
5338 } button_click_t;
5339
5340 static gboolean
easter_egg_timeout_cb(button_click_t * bc)5341 easter_egg_timeout_cb(button_click_t *bc)
5342 {
5343 if (bc->count == 1)
5344 {
5345 GtkWidget *widget;
5346 widget = GHB_WIDGET(bc->ud->builder, "allow_tweaks");
5347 gtk_widget_hide(widget);
5348 widget = GHB_WIDGET(bc->ud->builder, "hbfd_feature");
5349 gtk_widget_hide(widget);
5350 }
5351 bc->count = 0;
5352
5353 return FALSE;
5354 }
5355
5356 G_MODULE_EXPORT void
easter_egg_multi_cb(GtkGestureMultiPress * gest,gint n_press,gdouble x,gdouble y,signal_user_data_t * ud)5357 easter_egg_multi_cb(
5358 GtkGestureMultiPress * gest,
5359 gint n_press,
5360 gdouble x,
5361 gdouble y,
5362 signal_user_data_t * ud)
5363 {
5364 if (n_press == 3)
5365 {
5366 GtkWidget *widget;
5367 widget = GHB_WIDGET(ud->builder, "allow_tweaks");
5368 gtk_widget_show(widget);
5369 widget = GHB_WIDGET(ud->builder, "hbfd_feature");
5370 gtk_widget_show(widget);
5371 }
5372 }
5373
5374 G_MODULE_EXPORT gboolean
easter_egg_cb(GtkWidget * widget,GdkEvent * event,signal_user_data_t * ud)5375 easter_egg_cb(
5376 GtkWidget *widget,
5377 GdkEvent *event,
5378 signal_user_data_t *ud)
5379 {
5380 GdkEventType type = ghb_event_get_event_type(event);
5381 guint button;
5382 static button_click_t bc = { .count = 0 };
5383
5384 bc.ud = ud;
5385 ghb_event_get_button(event, &button);
5386 if (type == GDK_BUTTON_PRESS && button == 1)
5387 {
5388 bc.count++;
5389 switch (bc.count)
5390 {
5391 case 1:
5392 {
5393 g_timeout_add(500, (GSourceFunc)easter_egg_timeout_cb, &bc);
5394 } break;
5395
5396 case 3:
5397 {
5398 // It's a triple left mouse button click
5399 GtkWidget *widget;
5400 widget = GHB_WIDGET(ud->builder, "allow_tweaks");
5401 gtk_widget_show(widget);
5402 widget = GHB_WIDGET(ud->builder, "hbfd_feature");
5403 gtk_widget_show(widget);
5404 } break;
5405
5406 default:
5407 break;
5408 }
5409 }
5410 return FALSE;
5411 }
5412
5413 G_MODULE_EXPORT gchar*
format_deblock_cb(GtkScale * scale,gdouble val,signal_user_data_t * ud)5414 format_deblock_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud)
5415 {
5416 if (val < 5.0)
5417 {
5418 return g_strdup_printf(_("Off"));
5419 }
5420 else
5421 {
5422 return g_strdup_printf("%d", (gint)val);
5423 }
5424 }
5425
5426 static void
process_appcast(signal_user_data_t * ud)5427 process_appcast(signal_user_data_t *ud)
5428 {
5429 gchar *description = NULL, *build = NULL, *version = NULL, *msg;
5430 #if !defined(_WIN32) && !defined(_NO_UPDATE_CHECK)
5431 GtkWidget *window;
5432 static GtkWidget *html = NULL;
5433 #endif
5434 GtkWidget *dialog, *label;
5435 gint response, ibuild = 0, skip;
5436
5437 if (ud->appcast == NULL || ud->appcast_len < 15 ||
5438 strncmp(&(ud->appcast[9]), "200 OK", 6))
5439 {
5440 goto done;
5441 }
5442 ghb_appcast_parse(ud->appcast, &description, &build, &version);
5443 if (build)
5444 ibuild = g_strtod(build, NULL);
5445 skip = ghb_dict_get_int(ud->prefs, "update_skip_version");
5446 if (description == NULL || build == NULL || version == NULL
5447 || ibuild <= hb_get_build(NULL) || skip == ibuild)
5448 {
5449 goto done;
5450 }
5451 msg = g_strdup_printf(_("HandBrake %s/%s is now available (you have %s/%d)."),
5452 version, build, hb_get_version(NULL), hb_get_build(NULL));
5453 label = GHB_WIDGET(ud->builder, "update_message");
5454 gtk_label_set_text(GTK_LABEL(label), msg);
5455
5456 #if !defined(_WIN32) && !defined(_NO_UPDATE_CHECK)
5457 if (html == NULL)
5458 {
5459 html = webkit_web_view_new();
5460 window = GHB_WIDGET(ud->builder, "update_scroll");
5461 gtk_container_add(GTK_CONTAINER(window), html);
5462 // Show it
5463 gtk_widget_set_size_request(html, 420, 240);
5464 gtk_widget_show(html);
5465 }
5466 webkit_web_view_open(WEBKIT_WEB_VIEW(html), description);
5467 #endif
5468 dialog = GHB_WIDGET(ud->builder, "update_dialog");
5469 response = gtk_dialog_run(GTK_DIALOG(dialog));
5470 gtk_widget_hide(dialog);
5471 if (response == GTK_RESPONSE_OK)
5472 {
5473 // Skip
5474 ghb_dict_set_int(ud->prefs, "update_skip_version", ibuild);
5475 ghb_pref_save(ud->prefs, "update_skip_version");
5476 }
5477 g_free(msg);
5478
5479 done:
5480 if (description) g_free(description);
5481 if (build) g_free(build);
5482 if (version) g_free(version);
5483 g_free(ud->appcast);
5484 ud->appcast_len = 0;
5485 ud->appcast = NULL;
5486 appcast_busy = FALSE;
5487 }
5488
5489 void
ghb_net_close(GIOChannel * ioc)5490 ghb_net_close(GIOChannel *ioc)
5491 {
5492 gint fd;
5493
5494 g_debug("ghb_net_close");
5495 if (ioc == NULL) return;
5496 fd = g_io_channel_unix_get_fd(ioc);
5497 close(fd);
5498 g_io_channel_unref(ioc);
5499 }
5500
5501 G_MODULE_EXPORT gboolean
ghb_net_recv_cb(GIOChannel * ioc,GIOCondition cond,gpointer data)5502 ghb_net_recv_cb(GIOChannel *ioc, GIOCondition cond, gpointer data)
5503 {
5504 gchar buf[2048];
5505 gsize len;
5506 GError *gerror = NULL;
5507 GIOStatus status;
5508
5509 g_debug("ghb_net_recv_cb");
5510 signal_user_data_t *ud = (signal_user_data_t*)data;
5511
5512 status = g_io_channel_read_chars (ioc, buf, 2048, &len, &gerror);
5513 if ((status == G_IO_STATUS_NORMAL || status == G_IO_STATUS_EOF) &&
5514 len > 0)
5515 {
5516 gint new_len = ud->appcast_len + len;
5517 ud->appcast = g_realloc(ud->appcast, new_len + 1);
5518 memcpy(&(ud->appcast[ud->appcast_len]), buf, len);
5519 ud->appcast_len = new_len;
5520 }
5521 if (status == G_IO_STATUS_EOF)
5522 {
5523 if ( ud->appcast != NULL )
5524 {
5525 ud->appcast[ud->appcast_len] = 0;
5526 }
5527 ghb_net_close(ioc);
5528 process_appcast(ud);
5529 return FALSE;
5530 }
5531 return TRUE;
5532 }
5533
5534 GIOChannel*
ghb_net_open(signal_user_data_t * ud,gchar * address,gint port)5535 ghb_net_open(signal_user_data_t *ud, gchar *address, gint port)
5536 {
5537 GIOChannel *ioc;
5538 gint fd;
5539
5540 struct sockaddr_in sock;
5541 struct hostent * host;
5542
5543 g_debug("ghb_net_open");
5544 if( !( host = gethostbyname( address ) ) )
5545 {
5546 g_warning( "gethostbyname failed (%s)", address );
5547 appcast_busy = FALSE;
5548 return NULL;
5549 }
5550
5551 memset( &sock, 0, sizeof( struct sockaddr_in ) );
5552 sock.sin_family = host->h_addrtype;
5553 sock.sin_port = htons( port );
5554 memcpy( &sock.sin_addr, host->h_addr, host->h_length );
5555
5556 fd = socket(host->h_addrtype, SOCK_STREAM, 0);
5557 if( fd < 0 )
5558 {
5559 g_debug( "socket failed" );
5560 appcast_busy = FALSE;
5561 return NULL;
5562 }
5563
5564 if(connect(fd, (struct sockaddr*)&sock, sizeof(struct sockaddr_in )) < 0 )
5565 {
5566 g_debug( "connect failed" );
5567 appcast_busy = FALSE;
5568 return NULL;
5569 }
5570 ioc = g_io_channel_unix_new(fd);
5571 g_io_channel_set_encoding (ioc, NULL, NULL);
5572 g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL);
5573 g_io_add_watch (ioc, G_IO_IN, ghb_net_recv_cb, (gpointer)ud );
5574
5575 return ioc;
5576 }
5577
5578 gpointer
ghb_check_update(signal_user_data_t * ud)5579 ghb_check_update(signal_user_data_t *ud)
5580 {
5581 gchar *query;
5582 gsize len;
5583 GIOChannel *ioc;
5584 GError *gerror = NULL;
5585 GRegex *regex;
5586 GMatchInfo *mi;
5587 gchar *host, *appcast;
5588
5589 g_debug("ghb_check_update");
5590 appcast_busy = TRUE;
5591 regex = g_regex_new("^http://(.+)/(.+)$", 0, 0, NULL);
5592 if (!g_regex_match(regex, HB_PROJECT_URL_APPCAST, 0, &mi))
5593 {
5594 return NULL;
5595 }
5596
5597 host = g_match_info_fetch(mi, 1);
5598 appcast = g_match_info_fetch(mi, 2);
5599
5600 if (host == NULL || appcast == NULL)
5601 return NULL;
5602
5603 query = g_strdup_printf("GET /%s HTTP/1.0\r\nHost: %s\r\n\r\n",
5604 appcast, host);
5605
5606 ioc = ghb_net_open(ud, host, 80);
5607 if (ioc == NULL)
5608 goto free_resources;
5609
5610 g_io_channel_write_chars(ioc, query, strlen(query), &len, &gerror);
5611 g_io_channel_flush(ioc, &gerror);
5612
5613 free_resources:
5614 g_free(query);
5615 g_free(host);
5616 g_free(appcast);
5617 g_match_info_free(mi);
5618 g_regex_unref(regex);
5619 return NULL;
5620 }
5621
5622 void
ghb_notify_done(signal_user_data_t * ud)5623 ghb_notify_done(signal_user_data_t *ud)
5624 {
5625
5626 if (ghb_settings_combo_int(ud->prefs, "WhenComplete") == 0)
5627 return;
5628
5629 GNotification * notification;
5630 GIcon * icon;
5631
5632 notification = g_notification_new(_("Encode Complete"));
5633 g_notification_set_body(notification,
5634 _("Put down that cocktail, Your HandBrake queue is done!"));
5635 icon = g_themed_icon_new("fr.handbrake.ghb");
5636 g_notification_set_icon(notification, icon);
5637
5638 g_application_send_notification(G_APPLICATION(ud->app), "cocktail", notification);
5639 g_object_unref(G_OBJECT(notification));
5640 g_object_unref(G_OBJECT(icon));
5641
5642 if (ghb_settings_combo_int(ud->prefs, "WhenComplete") == 3)
5643 {
5644 if (ghb_can_shutdown_gsm())
5645 {
5646 ghb_countdown_dialog(GTK_MESSAGE_WARNING,
5647 _("Your encode is complete."),
5648 _("Shutting down the computer"),
5649 _("Cancel"), (GSourceFunc)shutdown_cb, ud, 60);
5650 }
5651 }
5652 if (ghb_settings_combo_int(ud->prefs, "WhenComplete") == 2)
5653 {
5654 if (ghb_can_suspend_gpm())
5655 {
5656 ghb_countdown_dialog(GTK_MESSAGE_WARNING,
5657 _("Your encode is complete."),
5658 _("Putting computer to sleep"),
5659 _("Cancel"), (GSourceFunc)suspend_cb, ud, 60);
5660 }
5661 }
5662 if (ghb_settings_combo_int(ud->prefs, "WhenComplete") == 4)
5663 {
5664 ghb_countdown_dialog(GTK_MESSAGE_WARNING,
5665 _("Your encode is complete."),
5666 _("Quitting Handbrake"),
5667 _("Cancel"), (GSourceFunc)quit_cb, ud, 60);
5668 }
5669 }
5670
5671 G_MODULE_EXPORT gboolean
window_map_cb(GtkWidget * widget,GdkEventAny * event,signal_user_data_t * ud)5672 window_map_cb(
5673 GtkWidget *widget,
5674 #if !GTK_CHECK_VERSION(3, 90, 0)
5675 GdkEventAny *event,
5676 #endif
5677 signal_user_data_t *ud)
5678 {
5679 return FALSE;
5680 }
5681
5682 G_MODULE_EXPORT void
hb_win_sz_alloc_cb(GtkWidget * widget,int width,int height,int baseline,signal_user_data_t * ud)5683 hb_win_sz_alloc_cb(
5684 GtkWidget *widget,
5685 #if GTK_CHECK_VERSION(3, 90, 0)
5686 int width,
5687 int height,
5688 int baseline,
5689 #else
5690 GdkRectangle *rect,
5691 #endif
5692 signal_user_data_t *ud)
5693 {
5694 if (gtk_widget_get_visible(widget))
5695 {
5696 gint w, h, ww, wh;
5697 w = ghb_dict_get_int(ud->prefs, "window_width");
5698 h = ghb_dict_get_int(ud->prefs, "window_height");
5699
5700 gtk_window_get_size(GTK_WINDOW(widget), &ww, &wh);
5701 if ( w != ww || h != wh )
5702 {
5703 ghb_dict_set_int(ud->prefs, "window_width", ww);
5704 ghb_dict_set_int(ud->prefs, "window_height", wh);
5705 ghb_pref_set(ud->prefs, "window_width");
5706 ghb_pref_set(ud->prefs, "window_height");
5707 ghb_prefs_store();
5708 }
5709 }
5710 }
5711
container_empty_cb(GtkWidget * widget,gpointer data)5712 static void container_empty_cb(GtkWidget *widget, gpointer data)
5713 {
5714 gtk_widget_destroy(widget);
5715 }
5716
ghb_container_empty(GtkContainer * c)5717 void ghb_container_empty(GtkContainer *c)
5718 {
5719 gtk_container_foreach(c, container_empty_cb, NULL);
5720 }
5721
5722 static void
lang_combo_search(GtkComboBox * combo,guint keyval,signal_user_data_t * ud)5723 lang_combo_search(
5724 GtkComboBox * combo,
5725 guint keyval,
5726 signal_user_data_t *ud)
5727 {
5728 if (combo == NULL)
5729 {
5730 return;
5731 }
5732 GtkTreeModel * model = GTK_TREE_MODEL(gtk_combo_box_get_model(combo));
5733 GtkTreeIter iter, prev_iter;
5734 gchar * lang;
5735 gunichar key_char;
5736 gunichar first_char;
5737 int pos = 0, count = 2048;
5738
5739 key_char = g_unichar_toupper(gdk_keyval_to_unicode(keyval));
5740 if (gtk_combo_box_get_active_iter(combo, &iter))
5741 {
5742 while (pos <= count)
5743 {
5744 pos++;
5745 prev_iter = iter;
5746 if (!gtk_tree_model_iter_next(model, &iter))
5747 {
5748 GtkTreePath * path = gtk_tree_model_get_path(model, &prev_iter);
5749 gint * ind = gtk_tree_path_get_indices(path);;
5750
5751 count = ind[0];
5752 gtk_tree_path_free(path);
5753 gtk_tree_model_get_iter_first(model, &iter);
5754 }
5755 gtk_tree_model_get(model, &iter, 0, &lang, -1);
5756 first_char = g_unichar_toupper(g_utf8_get_char(lang));
5757 g_free(lang);
5758 if (first_char == key_char)
5759 {
5760 gtk_combo_box_set_active_iter(combo, &iter);
5761 break;
5762 }
5763 }
5764 }
5765 }
5766
5767 #if GTK_CHECK_VERSION(3, 90, 0)
5768 G_MODULE_EXPORT gboolean
combo_search_key_press_cb(GtkEventControllerKey * keycon,guint keyval,guint keycode,GdkModifierType state,signal_user_data_t * ud)5769 combo_search_key_press_cb(
5770 GtkEventControllerKey * keycon,
5771 guint keyval,
5772 guint keycode,
5773 GdkModifierType state,
5774 signal_user_data_t * ud)
5775 {
5776 GtkComboBox * combo;
5777
5778 combo = GTK_COMBO_BOX(gtk_event_controller_get_widget(
5779 GTK_EVENT_CONTROLLER(keycon)));
5780 lang_combo_search(combo, keyval, ud);
5781 return FALSE;
5782 }
5783 #else
5784 G_MODULE_EXPORT gboolean
combo_search_key_press_cb(GtkWidget * widget,GdkEvent * event,signal_user_data_t * ud)5785 combo_search_key_press_cb(
5786 GtkWidget *widget,
5787 GdkEvent *event,
5788 signal_user_data_t *ud)
5789 {
5790 guint keyval;
5791
5792 ghb_event_get_keyval(event, &keyval);
5793 lang_combo_search(GTK_COMBO_BOX(widget), keyval, ud);
5794 return FALSE;
5795 }
5796 #endif
5797