From: John Stebbins Date: Tue, 27 Feb 2018 20:56:14 +0000 (-0700) Subject: LinGui: add support for GtkApplication suspend inhibit/uninhibit X-Git-Tag: 1.1.0~23 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=369b4f6b65b1fe376fa3ec3b62285c38181829ba;p=handbrake LinGui: add support for GtkApplication suspend inhibit/uninhibit This is a new API since GTK 3.4. Use it instead of DBUS when available. --- diff --git a/gtk/src/callbacks.c b/gtk/src/callbacks.c index 79b6f518a..0bdb9c08b 100644 --- a/gtk/src/callbacks.c +++ b/gtk/src/callbacks.c @@ -103,1772 +103,2314 @@ static gboolean ghb_can_suspend_gpm(); static void ghb_suspend_gpm(); static gboolean appcast_busy = FALSE; -// This is a dependency map used for greying widgets -// that are dependent on the state of another widget. -// The enable_value comes from the values that are -// obtained from ghb_widget_value(). For combo boxes -// you will have to look further to combo box options -// maps in hb-backend.c - -GhbValue *dep_map; -GhbValue *rev_map; - -void -ghb_init_dep_map() -{ - dep_map = ghb_resource_get("widget-deps"); - rev_map = ghb_resource_get("widget-reverse-deps"); -} +#if !defined(_WIN32) +#define GPM_DBUS_PM_SERVICE "org.freedesktop.PowerManagement" +#define GPM_DBUS_PM_PATH "/org/freedesktop/PowerManagement" +#define GPM_DBUS_PM_INTERFACE "org.freedesktop.PowerManagement" +#define GPM_DBUS_INHIBIT_PATH "/org/freedesktop/PowerManagement/Inhibit" +#define GPM_DBUS_INHIBIT_INTERFACE "org.freedesktop.PowerManagement.Inhibit" +#endif static gboolean -dep_check(signal_user_data_t *ud, const gchar *name, gboolean *out_hide) +ghb_can_suspend_gpm() { - GtkWidget *widget; - GObject *dep_object; - gint ii; - gint count; - gboolean result = TRUE; - GhbValue *array, *data; - const gchar *widget_name; + gboolean can_suspend = FALSE; +#if !defined(_WIN32) + DBusGConnection *conn; + DBusGProxy *proxy; + GError *error = NULL; + gboolean res; - g_debug("dep_check () %s", name); - if (rev_map == NULL) return TRUE; - array = ghb_dict_get(rev_map, name); - count = ghb_array_len(array); - *out_hide = FALSE; - for (ii = 0; ii < count; ii++) + g_debug("ghb_can_suspend_gpm()"); + conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); + if (error != NULL) { - data = ghb_array_get(array, ii); - widget_name = ghb_value_get_string(ghb_array_get(data, 0)); - widget = GHB_WIDGET(ud->builder, widget_name); - dep_object = gtk_builder_get_object(ud->builder, name); - if (widget != NULL && !gtk_widget_is_sensitive(widget)) - { - continue; - } - if (dep_object == NULL) + g_warning("DBUS cannot connect: %s", error->message); + g_error_free(error); + return FALSE; + } + proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE, + GPM_DBUS_PM_PATH, GPM_DBUS_PM_INTERFACE); + if (proxy == NULL) + { + g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE); + dbus_g_connection_unref(conn); + return FALSE; + } + res = dbus_g_proxy_call(proxy, "CanSuspend", &error, + G_TYPE_INVALID, + G_TYPE_BOOLEAN, &can_suspend, + G_TYPE_INVALID); + if (!res) + { + if (error != NULL) { - g_message("Failed to find widget"); + g_warning("CanSuspend failed: %s", error->message); + g_error_free(error); } else - { - gchar *value; - gint jj = 0; - gchar **values; - gboolean sensitive = FALSE; - gboolean die, hide; - - die = ghb_value_get_bool(ghb_array_get(data, 2)); - hide = ghb_value_get_bool(ghb_array_get(data, 3)); - const char *tmp = ghb_value_get_string(ghb_array_get(data, 1)); - values = g_strsplit(tmp, "|", 10); - - if (widget) - value = ghb_widget_string(widget); - else - value = ghb_dict_get_string_xform(ud->settings, widget_name); - while (values && values[jj]) - { - if (values[jj][0] == '>') - { - gdouble dbl = g_strtod (&values[jj][1], NULL); - gdouble dvalue = ghb_widget_double(widget); - if (dvalue > dbl) - { - sensitive = TRUE; - break; - } - } - else if (values[jj][0] == '<') - { - gdouble dbl = g_strtod (&values[jj][1], NULL); - gdouble dvalue = ghb_widget_double(widget); - if (dvalue < dbl) - { - sensitive = TRUE; - break; - } - } - if (strcmp(values[jj], value) == 0) - { - sensitive = TRUE; - break; - } - jj++; - } - sensitive = die ^ sensitive; - if (!sensitive) - { - result = FALSE; - *out_hide |= hide; - } - g_strfreev (values); - g_free(value); - } + g_warning("CanSuspend failed"); + // Try to shutdown anyway + can_suspend = TRUE; } - return result; + g_object_unref(G_OBJECT(proxy)); + dbus_g_connection_unref(conn); +#endif + return can_suspend; } -void -ghb_check_dependency( - signal_user_data_t *ud, - GtkWidget *widget, - const char *alt_name) +static void +ghb_suspend_gpm() { - GObject *dep_object; - const gchar *name; - GhbValue *array, *data; - gint count, ii; - const gchar *dep_name; - GType type; +#if !defined(_WIN32) + DBusGConnection *conn; + DBusGProxy *proxy; + GError *error = NULL; + gboolean res; - if (widget != NULL) + + g_debug("ghb_suspend_gpm()"); + conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); + if (error != NULL) { - type = G_OBJECT_TYPE(widget); - if (type == GTK_TYPE_COMBO_BOX) - if (gtk_combo_box_get_active(GTK_COMBO_BOX(widget)) < 0) return; - name = ghb_get_setting_key(widget); + g_warning("DBUS cannot connect: %s", error->message); + g_error_free(error); + return; } - else - name = alt_name; - - g_debug("ghb_check_dependency() %s", name); - - if (dep_map == NULL) return; - array = ghb_dict_get(dep_map, name); - count = ghb_array_len(array); - for (ii = 0; ii < count; ii++) + proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE, + GPM_DBUS_PM_PATH, GPM_DBUS_PM_INTERFACE); + if (proxy == NULL) { - gboolean sensitive; - gboolean hide; - - data = ghb_array_get(array, ii); - dep_name = ghb_value_get_string(data); - dep_object = gtk_builder_get_object(ud->builder, dep_name); - if (dep_object == NULL) - { - g_message("Failed to find dependent widget %s", dep_name); - continue; - } - sensitive = dep_check(ud, dep_name, &hide); - gtk_widget_set_sensitive(GTK_WIDGET(dep_object), sensitive); - gtk_widget_set_can_focus(GTK_WIDGET(dep_object), sensitive); - if (!sensitive && hide) + g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE); + dbus_g_connection_unref(conn); + return; + } + res = dbus_g_proxy_call(proxy, "Suspend", &error, + G_TYPE_INVALID, + G_TYPE_INVALID); + if (!res) + { + if (error != NULL) { - if (gtk_widget_get_visible(GTK_WIDGET(dep_object))) - { - gtk_widget_hide(GTK_WIDGET(dep_object)); - } + g_warning("Suspend failed: %s", error->message); + g_error_free(error); } else - { - if (!gtk_widget_get_visible(GTK_WIDGET(dep_object))) - { - gtk_widget_show_now(GTK_WIDGET(dep_object)); - } - } + g_warning("Suspend failed"); } + g_object_unref(G_OBJECT(proxy)); + dbus_g_connection_unref(conn); +#endif } -void -ghb_check_all_depencencies(signal_user_data_t *ud) +#if !defined(_WIN32) +static gboolean +ghb_can_shutdown_gpm() { - GhbDictIter iter; - const gchar *dep_name; - GhbValue *value; - GObject *dep_object; + gboolean can_shutdown = FALSE; + DBusGConnection *conn; + DBusGProxy *proxy; + GError *error = NULL; + gboolean res; - g_debug("ghb_check_all_depencencies ()"); - if (rev_map == NULL) return; - iter = ghb_dict_iter_init(rev_map); - while (ghb_dict_iter_next(rev_map, &iter, &dep_name, &value)) - { - gboolean sensitive; - gboolean hide; - dep_object = gtk_builder_get_object (ud->builder, dep_name); - if (dep_object == NULL) - { - g_message("Failed to find dependent widget %s", dep_name); - continue; - } - sensitive = dep_check(ud, dep_name, &hide); - gtk_widget_set_sensitive(GTK_WIDGET(dep_object), sensitive); - gtk_widget_set_can_focus(GTK_WIDGET(dep_object), sensitive); - if (!sensitive && hide) - { - gtk_widget_hide(GTK_WIDGET(dep_object)); - } - else + g_debug("ghb_can_shutdown_gpm()"); + conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); + if (error != NULL) + { + g_warning("DBUS cannot connect: %s", error->message); + g_error_free(error); + return FALSE; + } + proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE, + GPM_DBUS_PM_PATH, GPM_DBUS_PM_INTERFACE); + if (proxy == NULL) + { + g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE); + dbus_g_connection_unref(conn); + return FALSE; + } + res = dbus_g_proxy_call(proxy, "CanShutdown", &error, + G_TYPE_INVALID, + G_TYPE_BOOLEAN, &can_shutdown, + G_TYPE_INVALID); + if (!res) + { + if (error != NULL) { - gtk_widget_show_now(GTK_WIDGET(dep_object)); + g_warning("CanShutdown failed: %s", error->message); + g_error_free(error); } + else + g_warning("CanShutdown failed"); + // Try to shutdown anyway + can_shutdown = TRUE; } + g_object_unref(G_OBJECT(proxy)); + dbus_g_connection_unref(conn); + return can_shutdown; } +#endif -G_MODULE_EXPORT void -quit_action_cb(GSimpleAction *action, GVariant *param, signal_user_data_t *ud) +#if !defined(_WIN32) +static void +ghb_shutdown_gpm() { - gint state = ghb_get_queue_state(); - if (state & (GHB_STATE_WORKING|GHB_STATE_SEARCHING)) + DBusGConnection *conn; + DBusGProxy *proxy; + GError *error = NULL; + gboolean res; + + + g_debug("ghb_shutdown_gpm()"); + conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); + if (error != NULL) { - if (ghb_cancel_encode2(ud, _("Closing HandBrake will terminate encoding.\n"))) - { - ghb_hb_cleanup(FALSE); - prune_logs(ud); - g_application_quit(G_APPLICATION(ud->app)); - return; - } + g_warning("DBUS cannot connect: %s", error->message); + g_error_free(error); return; } - ghb_hb_cleanup(FALSE); - prune_logs(ud); - g_application_quit(G_APPLICATION(ud->app)); -} - -gboolean -uppers_and_unders(gchar *str) -{ - if (str == NULL) return FALSE; - str = g_strchomp(g_strchug(str)); - while (*str) + proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE, + GPM_DBUS_PM_PATH, GPM_DBUS_PM_INTERFACE); + if (proxy == NULL) { - if (*str == ' ') - { - return FALSE; - } - if (*str >= 'a' && *str <= 'z') + g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE); + dbus_g_connection_unref(conn); + return; + } + res = dbus_g_proxy_call(proxy, "Shutdown", &error, + G_TYPE_INVALID, + G_TYPE_INVALID); + if (!res) + { + if (error != NULL) { - return FALSE; + g_warning("Shutdown failed: %s", error->message); + g_error_free(error); } - str++; + else + g_warning("Shutdown failed"); } - return TRUE; + g_object_unref(G_OBJECT(proxy)); + dbus_g_connection_unref(conn); } +#endif -enum +guint +ghb_inhibit_gpm() { - CAMEL_FIRST_UPPER, - CAMEL_OTHER -}; + guint cookie = -1; -void -camel_convert(gchar *str) -{ - gint state = CAMEL_OTHER; +#if !defined(_WIN32) + DBusGConnection *conn; + DBusGProxy *proxy; + GError *error = NULL; + gboolean res; - if (str == NULL) return; - while (*str) + g_debug("ghb_inhibit_gpm()"); + conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); + if (error != NULL) { - if (*str == '_') *str = ' '; - switch (state) + g_warning("DBUS cannot connect: %s", error->message); + g_error_free(error); + return 0; + } + proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE, + GPM_DBUS_INHIBIT_PATH, GPM_DBUS_INHIBIT_INTERFACE); + if (proxy == NULL) + { + g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE); + dbus_g_connection_unref(conn); + return 0; + } + res = dbus_g_proxy_call(proxy, "Inhibit", &error, + G_TYPE_STRING, "ghb", + G_TYPE_STRING, "Encoding", + G_TYPE_INVALID, + G_TYPE_UINT, &cookie, + G_TYPE_INVALID); + if (!res) + { + if (error != NULL) { - case CAMEL_OTHER: - { - if (*str >= 'A' && *str <= 'Z') - state = CAMEL_FIRST_UPPER; - else - state = CAMEL_OTHER; - - } break; - case CAMEL_FIRST_UPPER: - { - if (*str >= 'A' && *str <= 'Z') - *str = *str - 'A' + 'a'; - else - state = CAMEL_OTHER; - } break; + g_error_free(error); } - str++; + cookie = -1; } -} - -#if defined(_WIN32) -static gchar* -get_dvd_device_name(gchar *device) -{ - return g_strdup(device); -} -#else -static gchar* -get_dvd_device_name(GDrive *gd) -{ - return g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE); -} -#endif - -static GHashTable *volname_hash = NULL; -#if GLIB_CHECK_VERSION(2, 32, 0) -static GMutex volname_mutex_static; + g_object_unref(G_OBJECT(proxy)); + dbus_g_connection_unref(conn); #endif -static GMutex *volname_mutex; -static void -free_volname_key(gpointer data) -{ - if (data != NULL) - g_free(data); + return cookie; } -static void -free_volname_value(gpointer data) +void +ghb_uninhibit_gpm(guint cookie) { - if (data != NULL) - g_free(data); -} +#if !defined(_WIN32) + DBusGConnection *conn; + DBusGProxy *proxy; + GError *error = NULL; + gboolean res; -#if defined(_WIN32) -static gchar* -get_direct_dvd_volume_name(const gchar *drive) -{ - gchar *result = NULL; - gchar vname[51], fsname[51]; + g_debug("ghb_uninhibit_gpm() cookie %u", cookie); - if (GetVolumeInformation(drive, vname, 50, NULL, NULL, NULL, fsname, 50)) + conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); + if (error != NULL) { - result = g_strdup_printf("%s", vname); + g_warning("DBUS cannot connect: %s", error->message); + g_error_free(error); + return; } - return result; + proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE, + GPM_DBUS_INHIBIT_PATH, GPM_DBUS_INHIBIT_INTERFACE); + if (proxy == NULL) + { + g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE); + dbus_g_connection_unref(conn); + return; + } + res = dbus_g_proxy_call(proxy, "UnInhibit", &error, + G_TYPE_UINT, cookie, + G_TYPE_INVALID, + G_TYPE_INVALID); + if (!res) + { + if (error != NULL) + { + g_error_free(error); + } + } + dbus_g_connection_unref(conn); + g_object_unref(G_OBJECT(proxy)); +#endif } -#else -static gchar* -get_direct_dvd_volume_name(const gchar *drive) -{ - gchar *result; - result = ghb_dvd_volname (drive); - return result; -} +#if !defined(_WIN32) +// For inhibit and shutdown +#define GPM_DBUS_SM_SERVICE "org.gnome.SessionManager" +#define GPM_DBUS_SM_PATH "/org/gnome/SessionManager" +#define GPM_DBUS_SM_INTERFACE "org.gnome.SessionManager" #endif -static gchar* -get_dvd_volume_name(gpointer gd) +static gboolean +ghb_can_shutdown_gsm() { - gchar *label = NULL; - gchar *result; - gchar *drive; + gboolean can_shutdown = FALSE; +#if !defined(_WIN32) + DBusGConnection *conn; + DBusGProxy *proxy; + GError *error = NULL; + gboolean res; - drive = get_dvd_device_name(gd); - g_mutex_lock(volname_mutex); - label = g_strdup(g_hash_table_lookup(volname_hash, drive)); - g_mutex_unlock(volname_mutex); - if (label != NULL) + conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); + if (error != NULL) { - if (uppers_and_unders(label)) - { - camel_convert(label); - } -#if defined(_WIN32) - result = g_strdup_printf("%s (%s)", label, drive); -#else - result = g_strdup_printf("%s - %s", drive, label); -#endif - g_free(label); + g_warning("DBUS cannot connect: %s", error->message); + g_error_free(error); + return FALSE; } - else + proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SM_SERVICE, + GPM_DBUS_SM_PATH, GPM_DBUS_SM_INTERFACE); + if (proxy == NULL) { - result = g_strdup_printf("%s", drive); + g_warning("Could not get DBUS proxy: %s", GPM_DBUS_SM_SERVICE); + dbus_g_connection_unref(conn); + return FALSE; + } + res = dbus_g_proxy_call(proxy, "CanShutdown", &error, + G_TYPE_INVALID, + G_TYPE_BOOLEAN, &can_shutdown, + G_TYPE_INVALID); + g_object_unref(G_OBJECT(proxy)); + dbus_g_connection_unref(conn); + if (!res) + { + if (error != NULL) + { + g_error_free(error); + } + // Try to shutdown anyway + can_shutdown = TRUE; + // Try the gpm version + return ghb_can_shutdown_gpm(); } - g_free(drive); - return result; -} - -void -ghb_volname_cache_init(void) -{ -#if GLIB_CHECK_VERSION(2, 32, 0) - g_mutex_init(&volname_mutex_static); - volname_mutex = &volname_mutex_static; -#else - volname_mutex = g_mutex_new(); #endif - volname_hash = g_hash_table_new_full(g_str_hash, g_str_equal, - free_volname_key, free_volname_value); + return can_shutdown; } static void -free_drive(gpointer drive) -{ -#if defined(_WIN32) - g_free(drive); -#else - g_object_unref(drive); -#endif -} - -gpointer -ghb_cache_volnames(signal_user_data_t *ud) +ghb_shutdown_gsm() { - GList *link, *drives; +#if !defined(_WIN32) + DBusGConnection *conn; + DBusGProxy *proxy; + GError *error = NULL; + gboolean res; - g_debug("ghb_cache_volnames()"); - link = drives = dvd_device_list(); - if (drives == NULL) - return NULL; - g_mutex_lock(volname_mutex); - g_hash_table_remove_all(volname_hash); - while (link != NULL) + g_debug("ghb_shutdown_gpm()"); + conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); + if (error != NULL) { - gchar *name, *drive; - -#if !defined(_WIN32) - if (!g_drive_has_media (link->data)) + g_warning("DBUS cannot connect: %s", error->message); + g_error_free(error); + return; + } + proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SM_SERVICE, + GPM_DBUS_SM_PATH, GPM_DBUS_SM_INTERFACE); + if (proxy == NULL) + { + g_warning("Could not get DBUS proxy: %s", GPM_DBUS_SM_SERVICE); + dbus_g_connection_unref(conn); + return; + } + res = dbus_g_proxy_call(proxy, "Shutdown", &error, + G_TYPE_INVALID, + G_TYPE_INVALID); + g_object_unref(G_OBJECT(proxy)); + dbus_g_connection_unref(conn); + if (!res) + { + if (error != NULL) { - g_object_unref(link->data); - link = link->next; - continue; + g_error_free(error); } + // Try the gpm version + ghb_shutdown_gpm(); + } #endif - drive = get_dvd_device_name(link->data); - name = get_direct_dvd_volume_name(drive); +} - if (drive != NULL && name != NULL) - { - g_hash_table_insert(volname_hash, drive, name); - } - else +guint +ghb_inhibit_gsm(signal_user_data_t *ud) +{ + guint cookie = -1; + +#if !defined(_WIN32) + DBusGConnection *conn; + DBusGProxy *proxy; + GError *error = NULL; + gboolean res; + guint xid; + GtkWidget *widget; + + g_debug("ghb_inhibit_gsm()"); + conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); + if (error != NULL) + { + g_warning("DBUS cannot connect: %s", error->message); + g_error_free(error); + return -1; + } + proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SM_SERVICE, + GPM_DBUS_SM_PATH, GPM_DBUS_SM_INTERFACE); + if (proxy == NULL) + { + g_warning("Could not get DBUS proxy: %s", GPM_DBUS_SM_SERVICE); + dbus_g_connection_unref(conn); + return -1; + } + widget = GHB_WIDGET(ud->builder, "hb_window"); + xid = GDK_WINDOW_XID(gtk_widget_get_window(widget)); + res = dbus_g_proxy_call(proxy, "Inhibit", &error, + G_TYPE_STRING, "ghb", + G_TYPE_UINT, xid, + G_TYPE_STRING, "Encoding", + G_TYPE_UINT, 1 | 4, + G_TYPE_INVALID, + G_TYPE_UINT, &cookie, + G_TYPE_INVALID); + g_object_unref(G_OBJECT(proxy)); + dbus_g_connection_unref(conn); + if (!res) + { + if (error != NULL) { - if (drive != NULL) - g_free(drive); - if (name != NULL) - g_free(name); + g_error_free(error); } - - free_drive(link->data); - link = link->next; + cookie = -1; } - g_mutex_unlock(volname_mutex); - - g_list_free(drives); - - g_idle_add((GSourceFunc)ghb_file_menu_add_dvd, ud); +#endif - return NULL; + return cookie; } -static const gchar* -get_extension(signal_user_data_t *ud, GhbValue *settings) +void +ghb_uninhibit_gsm(guint cookie) { - const char *mux_id; - const hb_container_t *mux; +#if !defined(_WIN32) + DBusGConnection *conn; + DBusGProxy *proxy; + GError *error = NULL; + gboolean res; - mux_id = ghb_dict_get_string(settings, "FileFormat"); - mux = ghb_lookup_container_by_name(mux_id); + g_debug("ghb_uninhibit_gsm() cookie %u", cookie); - if ((mux->format & HB_MUX_MASK_MP4) && - ghb_dict_get_bool(ud->prefs, "UseM4v")) + conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); + if (error != NULL) { - return "m4v"; + g_warning("DBUS cannot connect: %s", error->message); + g_error_free(error); + return; } - return mux->default_extension; + proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SM_SERVICE, + GPM_DBUS_SM_PATH, GPM_DBUS_SM_INTERFACE); + if (proxy == NULL) + { + g_warning("Could not get DBUS proxy: %s", GPM_DBUS_SM_SERVICE); + dbus_g_connection_unref(conn); + return; + } + res = dbus_g_proxy_call(proxy, "Uninhibit", &error, + G_TYPE_UINT, cookie, + G_TYPE_INVALID, + G_TYPE_INVALID); + if (!res) + { + if (error != NULL) + { + g_error_free(error); + } + } + dbus_g_connection_unref(conn); + g_object_unref(G_OBJECT(proxy)); +#endif } -static gboolean -check_name_template(signal_user_data_t *ud, const char *str) +enum { + GHB_SUSPEND_UNINHIBITED = 0, + GHB_SUSPEND_INHIBITED_GPM, + GHB_SUSPEND_INHIBITED_GSM, + GHB_SUSPEND_INHIBITED_GTK +}; +static int suspend_inhibited = GHB_SUSPEND_UNINHIBITED; +static guint suspend_cookie; + +void +ghb_inhibit_suspend(signal_user_data_t *ud) { - if (ghb_dict_get_bool(ud->prefs, "auto_name")) + if (suspend_inhibited != GHB_SUSPEND_UNINHIBITED) { - const gchar *template; - - template = ghb_dict_get_string(ud->prefs, "auto_name_template"); - if (strstr(template, str) != NULL) - return TRUE; + // Already inhibited + return; } - return FALSE; +#if GTK_CHECK_VERSION(3, 4, 0) + suspend_cookie = gtk_application_inhibit(ud->app, NULL, + GTK_APPLICATION_INHIBIT_SUSPEND, "Encoding"); + if (suspend_cookie != 0) + { + suspend_inhibited = GHB_SUSPEND_INHIBITED_GTK; + return; + } +#else + suspend_cookie = ghb_inhibit_gsm(ud); + if (suspend_cookie != -1) + { + suspend_inhibited = GHB_SUSPEND_INHIBITED_GSM; + return; + } + suspend_cookie = ghb_inhibit_gpm(ud); + if (suspend_cookie != -1) + { + suspend_inhibited = GHB_SUSPEND_INHIBITED_GPM; + return; + } +#endif } -static void -set_destination_settings(signal_user_data_t *ud, GhbValue *settings) +void +ghb_uninhibit_suspend(signal_user_data_t *ud) { - const gchar *extension, *dest_file; - gchar *filename; + switch (suspend_inhibited) + { +#if GTK_CHECK_VERSION(3, 4, 0) + case GHB_SUSPEND_INHIBITED_GTK: + gtk_application_uninhibit(ud->app, suspend_cookie); + break; +#endif - extension = get_extension(ud, settings); + case GHB_SUSPEND_INHIBITED_GPM: + ghb_uninhibit_gpm(suspend_cookie); + break; - g_debug("set_destination_settings"); - dest_file = ghb_dict_get_string(ud->settings, "dest_file"); - if (dest_file == NULL) - { - // Initialize destination filename if it has no value yet. - // If auto-naming is disabled, this will be the default filename. - GString *str = g_string_new(""); - const gchar *vol_name; - vol_name = ghb_dict_get_string(settings, "volume"); - g_string_append_printf(str, "%s", vol_name); - g_string_append_printf(str, ".%s", extension); - filename = g_string_free(str, FALSE); - ghb_dict_set_string(settings, "dest_file", filename); - g_free(filename); + case GHB_SUSPEND_INHIBITED_GSM: + ghb_uninhibit_gsm(suspend_cookie); + break; + + default: + break; } - if (ghb_dict_get_bool(ud->prefs, "auto_name")) - { - GString *str = g_string_new(""); - const gchar *p; + suspend_inhibited = GHB_SUSPEND_UNINHIBITED; +} - p = ghb_dict_get_string(ud->prefs, "auto_name_template"); - while (*p) +// This is a dependency map used for greying widgets +// that are dependent on the state of another widget. +// The enable_value comes from the values that are +// obtained from ghb_widget_value(). For combo boxes +// you will have to look further to combo box options +// maps in hb-backend.c + +GhbValue *dep_map; +GhbValue *rev_map; + +void +ghb_init_dep_map() +{ + dep_map = ghb_resource_get("widget-deps"); + rev_map = ghb_resource_get("widget-reverse-deps"); +} + +static gboolean +dep_check(signal_user_data_t *ud, const gchar *name, gboolean *out_hide) +{ + GtkWidget *widget; + GObject *dep_object; + gint ii; + gint count; + gboolean result = TRUE; + GhbValue *array, *data; + const gchar *widget_name; + + g_debug("dep_check () %s", name); + + if (rev_map == NULL) return TRUE; + array = ghb_dict_get(rev_map, name); + count = ghb_array_len(array); + *out_hide = FALSE; + for (ii = 0; ii < count; ii++) + { + data = ghb_array_get(array, ii); + widget_name = ghb_value_get_string(ghb_array_get(data, 0)); + widget = GHB_WIDGET(ud->builder, widget_name); + dep_object = gtk_builder_get_object(ud->builder, name); + if (widget != NULL && !gtk_widget_is_sensitive(widget)) { - if (!strncmp(p, "{source}", strlen("{source}"))) - { - const gchar *vol_name; - vol_name = ghb_dict_get_string(settings, "volume"); - g_string_append_printf(str, "%s", vol_name); - p += strlen("{source}"); - } - else if (!strncmp(p, "{title}", strlen("{title}"))) - { - gint title = ghb_dict_get_int(settings, "title"); - if (title >= 0) - g_string_append_printf(str, "%d", title); - p += strlen("{title}"); - } - else if (!strncmp(p, "{preset}", strlen("{preset}"))) - { - const gchar *preset_name; - preset_name = ghb_dict_get_string(settings, "PresetName"); - g_string_append_printf(str, "%s", preset_name); - p += strlen("{preset}"); - } - else if (!strncmp(p, "{chapters}", strlen("{chapters}"))) + continue; + } + if (dep_object == NULL) + { + g_message("Failed to find widget"); + } + else + { + gchar *value; + gint jj = 0; + gchar **values; + gboolean sensitive = FALSE; + gboolean die, hide; + + die = ghb_value_get_bool(ghb_array_get(data, 2)); + hide = ghb_value_get_bool(ghb_array_get(data, 3)); + const char *tmp = ghb_value_get_string(ghb_array_get(data, 1)); + values = g_strsplit(tmp, "|", 10); + + if (widget) + value = ghb_widget_string(widget); + else + value = ghb_dict_get_string_xform(ud->settings, widget_name); + while (values && values[jj]) { - if (ghb_settings_combo_int(settings, "PtoPType") == 0) + if (values[jj][0] == '>') { - gint start, end; - start = ghb_dict_get_int(settings, "start_point"); - end = ghb_dict_get_int(settings, "end_point"); - if (start == end) - g_string_append_printf(str, "%d", start); - else - g_string_append_printf(str, "%d-%d", start, end); + gdouble dbl = g_strtod (&values[jj][1], NULL); + gdouble dvalue = ghb_widget_double(widget); + if (dvalue > dbl) + { + sensitive = TRUE; + break; + } } - p += strlen("{chapters}"); - } - else if (!strncmp(p, "{time}", strlen("{time}"))) - { - char st[6]; - struct tm *lt; - time_t t = time(NULL); - lt = localtime(&t); - st[0] = 0; - strftime(st, 6, "%H:%M", lt); - g_string_append_printf(str, "%s", st); - p += strlen("{time}"); - } - else if (!strncmp(p, "{date}", strlen("{date}"))) - { - char dt[11]; - struct tm *lt; - time_t t = time(NULL); - lt = localtime(&t); - dt[0] = 0; - strftime(dt, 11, "%Y-%m-%d", lt); - g_string_append_printf(str, "%s", dt); - p += strlen("{date}"); - } - else if (!strncmp(p, "{quality}", strlen("{quality}"))) - { - if (ghb_dict_get_bool(settings, "vquality_type_constant")) + else if (values[jj][0] == '<') { - gint vcodec; - const char *vqname; - double vquality; - vcodec = ghb_settings_video_encoder_codec(settings, "VideoEncoder"); - vqname = hb_video_quality_get_name(vcodec); - vquality = ghb_dict_get_double(settings, "VideoQualitySlider"); - g_string_append_printf(str, "%s%.3g", vqname, vquality); + gdouble dbl = g_strtod (&values[jj][1], NULL); + gdouble dvalue = ghb_widget_double(widget); + if (dvalue < dbl) + { + sensitive = TRUE; + break; + } } - p += strlen("{quality}"); - } - else if (!strncmp(p, "{bitrate}", strlen("{bitrate}"))) - { - if (ghb_dict_get_bool(settings, "vquality_type_bitrate")) + if (strcmp(values[jj], value) == 0) { - int vbitrate; - vbitrate = ghb_dict_get_int(settings, "VideoAvgBitrate"); - g_string_append_printf(str, "%dkbps", vbitrate); + sensitive = TRUE; + break; } - p += strlen("{bitrate}"); + jj++; } - else + sensitive = die ^ sensitive; + if (!sensitive) { - g_string_append_printf(str, "%c", *p); - p++; + result = FALSE; + *out_hide |= hide; } + g_strfreev (values); + g_free(value); } - g_string_append_printf(str, ".%s", extension); - filename = g_string_free(str, FALSE); - ghb_dict_set_string(settings, "dest_file", filename); - g_free(filename); } + return result; } -static void -set_destination(signal_user_data_t *ud) -{ - set_destination_settings(ud, ud->settings); - ghb_ui_update(ud, "dest_file", - ghb_dict_get_value(ud->settings, "dest_file")); -} - -static gchar* -get_file_label(const gchar *filename) +void +ghb_check_dependency( + signal_user_data_t *ud, + GtkWidget *widget, + const char *alt_name) { - gchar *base, *pos, *end; + GObject *dep_object; + const gchar *name; + GhbValue *array, *data; + gint count, ii; + const gchar *dep_name; + GType type; - base = g_path_get_basename(filename); - pos = strrchr(base, '.'); - if (pos != NULL) + if (widget != NULL) { - // If the last '.' is within 4 chars of end of name, assume - // there is an extension we want to strip. - end = &base[strlen(base) - 1]; - if (end - pos <= 4) - *pos = 0; + type = G_OBJECT_TYPE(widget); + if (type == GTK_TYPE_COMBO_BOX) + if (gtk_combo_box_get_active(GTK_COMBO_BOX(widget)) < 0) return; + name = ghb_get_setting_key(widget); } - return base; -} + else + name = alt_name; -static gchar* -resolve_drive_name(gchar *filename) -{ -#if defined(_WIN32) - if (filename[1] == ':') + g_debug("ghb_check_dependency() %s", name); + + if (dep_map == NULL) return; + array = ghb_dict_get(dep_map, name); + count = ghb_array_len(array); + for (ii = 0; ii < count; ii++) { - gchar drive[4]; - gchar *name; - gint dtype; + gboolean sensitive; + gboolean hide; - g_strlcpy(drive, filename, 4); - dtype = GetDriveType(drive); - if (dtype == DRIVE_CDROM) + data = ghb_array_get(array, ii); + dep_name = ghb_value_get_string(data); + dep_object = gtk_builder_get_object(ud->builder, dep_name); + if (dep_object == NULL) { - gchar vname[51], fsname[51]; - GetVolumeInformation(drive, vname, 50, NULL, - NULL, NULL, fsname, 50); - name = g_strdup(vname); - return name; + g_message("Failed to find dependent widget %s", dep_name); + continue; + } + sensitive = dep_check(ud, dep_name, &hide); + gtk_widget_set_sensitive(GTK_WIDGET(dep_object), sensitive); + gtk_widget_set_can_focus(GTK_WIDGET(dep_object), sensitive); + if (!sensitive && hide) + { + if (gtk_widget_get_visible(GTK_WIDGET(dep_object))) + { + gtk_widget_hide(GTK_WIDGET(dep_object)); + } + } + else + { + if (!gtk_widget_get_visible(GTK_WIDGET(dep_object))) + { + gtk_widget_show_now(GTK_WIDGET(dep_object)); + } } } - return NULL; -#else - return NULL; -#endif } -static gboolean -update_source_label(signal_user_data_t *ud, const gchar *source) +void +ghb_check_all_depencencies(signal_user_data_t *ud) { - GStatBuf stat_buf; - gchar *label = NULL; - gint len; - gchar **path; - gchar *start; - gchar *filename = g_strdup(source); + GhbDictIter iter; + const gchar *dep_name; + GhbValue *value; + GObject *dep_object; - g_debug("update_source_label()"); - if (g_stat(filename, &stat_buf) == 0) + g_debug("ghb_check_all_depencencies ()"); + if (rev_map == NULL) return; + iter = ghb_dict_iter_init(rev_map); + while (ghb_dict_iter_next(rev_map, &iter, &dep_name, &value)) { - len = strlen(filename); - if (S_ISDIR(stat_buf.st_mode)) - { - // Skip dos drive letters -#if defined(_WIN32) - start = strchr(filename, ':'); -#else - start = filename; -#endif - label = resolve_drive_name(filename); - if (label != NULL) - { - if (uppers_and_unders(label)) - { - camel_convert(label); - } - } - else - { - if (filename[len-1] == G_DIR_SEPARATOR) filename[len-1] = 0; - if (start != NULL) - start++; - else - start = filename; + gboolean sensitive; + gboolean hide; - path = g_strsplit(start, G_DIR_SEPARATOR_S, -1); - len = g_strv_length (path); - if ((len > 1) && (strcmp("VIDEO_TS", path[len-1]) == 0)) - { - label = g_strdup(path[len-2]); - if (uppers_and_unders(label)) - { - camel_convert(label); - } - } - else if (len > 0) - { - if (path[len-1][0] != 0) - { - label = g_strdup(path[len-1]); - if (uppers_and_unders(label)) - { - camel_convert(label); - } - } - else - label = g_strdup("new_video"); - } - else - label = g_strdup("new_video"); - g_strfreev (path); - } + dep_object = gtk_builder_get_object (ud->builder, dep_name); + if (dep_object == NULL) + { + g_message("Failed to find dependent widget %s", dep_name); + continue; } - else if (S_ISBLK(stat_buf.st_mode)) + sensitive = dep_check(ud, dep_name, &hide); + gtk_widget_set_sensitive(GTK_WIDGET(dep_object), sensitive); + gtk_widget_set_can_focus(GTK_WIDGET(dep_object), sensitive); + if (!sensitive && hide) { - // Is regular file or block dev. - // Check to see if it is a dvd image - label = ghb_dvd_volname(filename); - if (label == NULL) - { - label = get_file_label(filename); - } - else - { - if (uppers_and_unders(label)) - { - camel_convert(label); - } - } + gtk_widget_hide(GTK_WIDGET(dep_object)); } else { - label = get_file_label(filename); + gtk_widget_show_now(GTK_WIDGET(dep_object)); } } - else - { - label = get_file_label(filename); - } - g_free(filename); - GtkWidget *widget = GHB_WIDGET (ud->builder, "source_label"); - if (label != NULL) - { - gtk_label_set_text (GTK_LABEL(widget), label); - ghb_dict_set_string(ud->globals, "volume", label); - g_free(label); - } - else - { - label = _("No Title Found"); - gtk_label_set_text (GTK_LABEL(widget), label); - ghb_dict_set_string(ud->globals, "volume", label); - return FALSE; - } - return TRUE; } G_MODULE_EXPORT void -chooser_file_selected_cb(GtkFileChooser *dialog, signal_user_data_t *ud) +quit_action_cb(GSimpleAction *action, GVariant *param, signal_user_data_t *ud) { - gchar *name = gtk_file_chooser_get_filename (dialog); - GtkTreeModel *store; - GtkTreeIter iter; - const gchar *device; - gboolean foundit = FALSE; - GtkComboBox *combo; - - g_debug("chooser_file_selected_cb ()"); - - if (name == NULL) return; - combo = GTK_COMBO_BOX(GHB_WIDGET(ud->builder, "source_device")); - store = gtk_combo_box_get_model(combo); - if (gtk_tree_model_get_iter_first(store, &iter)) + gint state = ghb_get_queue_state(); + if (state & (GHB_STATE_WORKING|GHB_STATE_SEARCHING)) { - do + if (ghb_cancel_encode2(ud, _("Closing HandBrake will terminate encoding.\n"))) { - gtk_tree_model_get(store, &iter, 0, &device, -1); - if (strcmp(name, device) == 0) - { - foundit = TRUE; - break; - } - } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter)); + ghb_hb_cleanup(FALSE); + prune_logs(ud); + g_application_quit(G_APPLICATION(ud->app)); + return; + } + return; } - if (foundit) - gtk_combo_box_set_active_iter (combo, &iter); - else - gtk_combo_box_set_active (combo, 0); - - g_free(name); + ghb_hb_cleanup(FALSE); + prune_logs(ud); + g_application_quit(G_APPLICATION(ud->app)); } -G_MODULE_EXPORT void -dvd_device_changed_cb(GtkComboBoxText *combo, signal_user_data_t *ud) +gboolean +uppers_and_unders(gchar *str) { - GtkWidget *dialog; - gint ii; - - g_debug("dvd_device_changed_cb ()"); - ii = gtk_combo_box_get_active (GTK_COMBO_BOX(combo)); - if (ii > 0) + if (str == NULL) return FALSE; + str = g_strchomp(g_strchug(str)); + while (*str) { - const gchar *device; - gchar *name; - - dialog = GHB_WIDGET(ud->builder, "source_dialog"); - device = gtk_combo_box_text_get_active_text(combo); - // Protext against unexpected NULL return value - if (device != NULL) + if (*str == ' ') { - name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog)); - if (name == NULL || strcmp(name, device) != 0) - gtk_file_chooser_select_filename (GTK_FILE_CHOOSER(dialog), device); - if (name != NULL) - g_free(name); + return FALSE; + } + if (*str >= 'a' && *str <= 'z') + { + return FALSE; } + str++; } + return TRUE; } -static void -source_dialog_extra_widgets( - signal_user_data_t *ud, - GtkWidget *dialog) +enum { - GtkComboBoxText *combo; - GList *drives, *link; + CAMEL_FIRST_UPPER, + CAMEL_OTHER +}; - g_debug("source_dialog_extra_widgets ()"); - combo = GTK_COMBO_BOX_TEXT(GHB_WIDGET(ud->builder, "source_device")); - gtk_list_store_clear(GTK_LIST_STORE( - gtk_combo_box_get_model(GTK_COMBO_BOX(combo)))); +void +camel_convert(gchar *str) +{ + gint state = CAMEL_OTHER; - link = drives = dvd_device_list(); - gtk_combo_box_text_append_text (combo, _("Not Selected")); - while (link != NULL) + if (str == NULL) return; + while (*str) { - gchar *name = get_dvd_device_name(link->data); - gtk_combo_box_text_append_text(combo, name); - g_free(name); - free_drive(link->data); - link = link->next; - } - g_list_free(drives); - gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0); + if (*str == '_') *str = ' '; + switch (state) + { + case CAMEL_OTHER: + { + if (*str >= 'A' && *str <= 'Z') + state = CAMEL_FIRST_UPPER; + else + state = CAMEL_OTHER; + + } break; + case CAMEL_FIRST_UPPER: + { + if (*str >= 'A' && *str <= 'Z') + *str = *str - 'A' + 'a'; + else + state = CAMEL_OTHER; + } break; + } + str++; + } } -static void break_duration(gint64 duration, gint *hh, gint *mm, gint *ss) +#if defined(_WIN32) +static gchar* +get_dvd_device_name(gchar *device) { - *hh = duration / (60*60); - *mm = (duration / 60) % 60; - *ss = duration % 60; + return g_strdup(device); +} +#else +static gchar* +get_dvd_device_name(GDrive *gd) +{ + return g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE); } +#endif + +static GHashTable *volname_hash = NULL; +#if GLIB_CHECK_VERSION(2, 32, 0) +static GMutex volname_mutex_static; +#endif +static GMutex *volname_mutex; static void -update_title_duration(signal_user_data_t *ud) +free_volname_key(gpointer data) { - gint hh, mm, ss, start, end; - gchar *text; - GtkWidget *widget; - int title_id, titleindex; - const hb_title_t *title; + if (data != NULL) + g_free(data); +} - title_id = ghb_dict_get_int(ud->settings, "title"); - title = ghb_lookup_title(title_id, &titleindex); - widget = GHB_WIDGET (ud->builder, "title_duration"); +static void +free_volname_value(gpointer data) +{ + if (data != NULL) + g_free(data); +} - if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0) +#if defined(_WIN32) +static gchar* +get_direct_dvd_volume_name(const gchar *drive) +{ + gchar *result = NULL; + gchar vname[51], fsname[51]; + + if (GetVolumeInformation(drive, vname, 50, NULL, NULL, NULL, fsname, 50)) { - start = ghb_dict_get_int(ud->settings, "start_point"); - end = ghb_dict_get_int(ud->settings, "end_point"); - ghb_part_duration(title, start, end, &hh, &mm, &ss); + result = g_strdup_printf("%s", vname); } - else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1) - { - gint duration; + return result; +} +#else +static gchar* +get_direct_dvd_volume_name(const gchar *drive) +{ + gchar *result; - start = ghb_dict_get_int(ud->settings, "start_point"); - end = ghb_dict_get_int(ud->settings, "end_point"); - duration = end - start; - break_duration(duration, &hh, &mm, &ss); - } - else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 2) - { - if (title != NULL) - { - gint64 frames; - gint duration; + result = ghb_dvd_volname (drive); + return result; +} +#endif - start = ghb_dict_get_int(ud->settings, "start_point"); - end = ghb_dict_get_int(ud->settings, "end_point"); - frames = end - start + 1; - duration = frames * title->vrate.den / title->vrate.num; - break_duration(duration, &hh, &mm, &ss); - } - else +static gchar* +get_dvd_volume_name(gpointer gd) +{ + gchar *label = NULL; + gchar *result; + gchar *drive; + + drive = get_dvd_device_name(gd); + g_mutex_lock(volname_mutex); + label = g_strdup(g_hash_table_lookup(volname_hash, drive)); + g_mutex_unlock(volname_mutex); + if (label != NULL) + { + if (uppers_and_unders(label)) { - hh = mm = ss = 0; + camel_convert(label); } +#if defined(_WIN32) + result = g_strdup_printf("%s (%s)", label, drive); +#else + result = g_strdup_printf("%s - %s", drive, label); +#endif + g_free(label); } - text = g_strdup_printf("%02d:%02d:%02d", hh, mm, ss); - gtk_label_set_text(GTK_LABEL(widget), text); - g_free(text); + else + { + result = g_strdup_printf("%s", drive); + } + g_free(drive); + return result; } -void ghb_show_container_options(signal_user_data_t *ud) +void +ghb_volname_cache_init(void) { - GtkWidget *w1, *w2, *w3; - w1 = GHB_WIDGET(ud->builder, "AlignAVStart"); - w2 = GHB_WIDGET(ud->builder, "Mp4HttpOptimize"); - w3 = GHB_WIDGET(ud->builder, "Mp4iPodCompatible"); - - const char *mux_id; - const hb_container_t *mux; - - mux_id = ghb_dict_get_string(ud->settings, "FileFormat"); - mux = ghb_lookup_container_by_name(mux_id); - - gint enc = ghb_settings_video_encoder_codec(ud->settings, "VideoEncoder"); - - gtk_widget_set_visible(w1, (mux->format & HB_MUX_MASK_MP4)); - gtk_widget_set_visible(w2, (mux->format & HB_MUX_MASK_MP4)); - gtk_widget_set_visible(w3, (mux->format & HB_MUX_MASK_MP4) && - (enc == HB_VCODEC_X264_8BIT)); +#if GLIB_CHECK_VERSION(2, 32, 0) + g_mutex_init(&volname_mutex_static); + volname_mutex = &volname_mutex_static; +#else + volname_mutex = g_mutex_new(); +#endif + volname_hash = g_hash_table_new_full(g_str_hash, g_str_equal, + free_volname_key, free_volname_value); } static void -adjustment_configure( - GtkAdjustment *adj, - double val, - double min, double max, - double step, double page, double page_sz) +free_drive(gpointer drive) { - gtk_adjustment_configure(adj, val, min, max, step, page, page_sz); +#if defined(_WIN32) + g_free(drive); +#else + g_object_unref(drive); +#endif } -static void -spin_configure(signal_user_data_t *ud, char *name, double val, double min, double max) +gpointer +ghb_cache_volnames(signal_user_data_t *ud) { - GtkSpinButton *spin; - GtkAdjustment *adj; - double step, page, page_sz; + GList *link, *drives; - spin = GTK_SPIN_BUTTON(GHB_WIDGET(ud->builder, name)); + g_debug("ghb_cache_volnames()"); + link = drives = dvd_device_list(); + if (drives == NULL) + return NULL; - adj = gtk_spin_button_get_adjustment(spin); - step = gtk_adjustment_get_step_increment(adj); - page = gtk_adjustment_get_page_increment(adj); - page_sz = gtk_adjustment_get_page_size(adj); + g_mutex_lock(volname_mutex); + g_hash_table_remove_all(volname_hash); + while (link != NULL) + { + gchar *name, *drive; - adjustment_configure(adj, val, min, max, step, page, page_sz); -} +#if !defined(_WIN32) + if (!g_drive_has_media (link->data)) + { + g_object_unref(link->data); + link = link->next; + continue; + } +#endif + drive = get_dvd_device_name(link->data); + name = get_direct_dvd_volume_name(drive); -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) -{ - GtkScale *scale; - GtkAdjustment *adj; - double page_sz; + if (drive != NULL && name != NULL) + { + g_hash_table_insert(volname_hash, drive, name); + } + else + { + if (drive != NULL) + g_free(drive); + if (name != NULL) + g_free(name); + } - scale = GTK_SCALE(GHB_WIDGET(ud->builder, name)); + free_drive(link->data); + link = link->next; + } + g_mutex_unlock(volname_mutex); - adj = gtk_range_get_adjustment(GTK_RANGE(scale)); - page_sz = gtk_adjustment_get_page_size(adj); + g_list_free(drives); - adjustment_configure(adj, val, min, max, step, page, page_sz); + g_idle_add((GSourceFunc)ghb_file_menu_add_dvd, ud); - gtk_scale_set_digits(scale, digits); - gtk_range_set_inverted(GTK_RANGE(scale), inverted); + return NULL; } -void -ghb_set_widget_ranges(signal_user_data_t *ud, GhbValue *settings) +static const gchar* +get_extension(signal_user_data_t *ud, GhbValue *settings) { - int title_id, titleindex; - const hb_title_t * title; - double val; + const char *mux_id; + const hb_container_t *mux; - title_id = ghb_dict_get_int(settings, "title"); - title = ghb_lookup_title(title_id, &titleindex); + mux_id = ghb_dict_get_string(settings, "FileFormat"); + mux = ghb_lookup_container_by_name(mux_id); - // Reconfigure the UI combo boxes - ghb_update_ui_combo_box(ud, "AudioTrack", title, FALSE); - ghb_update_ui_combo_box(ud, "SubtitleTrack", title, FALSE); + if ((mux->format & HB_MUX_MASK_MP4) && + ghb_dict_get_bool(ud->prefs, "UseM4v")) + { + return "m4v"; + } + return mux->default_extension; +} - if (title != NULL) +static gboolean +check_name_template(signal_user_data_t *ud, const char *str) +{ + if (ghb_dict_get_bool(ud->prefs, "auto_name")) { + const gchar *template; - // Set the limits of cropping. hb_set_anamorphic_size crashes if - // you pass it a cropped width or height == 0. - gint vbound, hbound; - vbound = title->geometry.height; - hbound = title->geometry.width; + template = ghb_dict_get_string(ud->prefs, "auto_name_template"); + if (strstr(template, str) != NULL) + return TRUE; + } + return FALSE; +} - val = ghb_dict_get_int(ud->settings, "PictureTopCrop"); - spin_configure(ud, "PictureTopCrop", val, 0, vbound); - val = ghb_dict_get_int(ud->settings, "PictureBottomCrop"); - spin_configure(ud, "PictureBottomCrop", val, 0, vbound); - val = ghb_dict_get_int(ud->settings, "PictureLeftCrop"); - spin_configure(ud, "PictureLeftCrop", val, 0, hbound); - val = ghb_dict_get_int(ud->settings, "PictureRightCrop"); - spin_configure(ud, "PictureRightCrop", val, 0, hbound); +static void +set_destination_settings(signal_user_data_t *ud, GhbValue *settings) +{ + const gchar *extension, *dest_file; + gchar *filename; - gint duration = title->duration / 90000; + extension = get_extension(ud, settings); - if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0) - { - gint num_chapters = hb_list_count(title->list_chapter); + g_debug("set_destination_settings"); + dest_file = ghb_dict_get_string(ud->settings, "dest_file"); + if (dest_file == NULL) + { + // Initialize destination filename if it has no value yet. + // If auto-naming is disabled, this will be the default filename. + GString *str = g_string_new(""); + const gchar *vol_name; + vol_name = ghb_dict_get_string(settings, "volume"); + g_string_append_printf(str, "%s", vol_name); + g_string_append_printf(str, ".%s", extension); + filename = g_string_free(str, FALSE); + ghb_dict_set_string(settings, "dest_file", filename); + g_free(filename); + } + if (ghb_dict_get_bool(ud->prefs, "auto_name")) + { + GString *str = g_string_new(""); + const gchar *p; - val = ghb_dict_get_int(ud->settings, "start_point"); - spin_configure(ud, "start_point", val, 1, num_chapters); - val = ghb_dict_get_int(ud->settings, "end_point"); - spin_configure(ud, "end_point", val, 1, num_chapters); - } - else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1) - { - val = ghb_dict_get_int(ud->settings, "start_point"); - spin_configure(ud, "start_point", val, 0, duration * 2 - 1); - val = ghb_dict_get_int(ud->settings, "end_point"); - spin_configure(ud, "end_point", val, 0, duration * 2); - } - else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 2) + p = ghb_dict_get_string(ud->prefs, "auto_name_template"); + while (*p) { - gdouble max_frames; - max_frames = (gdouble)duration * - title->vrate.num / title->vrate.den; - - val = ghb_dict_get_int(ud->settings, "start_point"); - spin_configure(ud, "start_point", val, 1, max_frames * 2); - val = ghb_dict_get_int(ud->settings, "end_point"); - spin_configure(ud, "end_point", val, 1, max_frames * 2); + if (!strncmp(p, "{source}", strlen("{source}"))) + { + const gchar *vol_name; + vol_name = ghb_dict_get_string(settings, "volume"); + g_string_append_printf(str, "%s", vol_name); + p += strlen("{source}"); + } + else if (!strncmp(p, "{title}", strlen("{title}"))) + { + gint title = ghb_dict_get_int(settings, "title"); + if (title >= 0) + g_string_append_printf(str, "%d", title); + p += strlen("{title}"); + } + else if (!strncmp(p, "{preset}", strlen("{preset}"))) + { + const gchar *preset_name; + preset_name = ghb_dict_get_string(settings, "PresetName"); + g_string_append_printf(str, "%s", preset_name); + p += strlen("{preset}"); + } + else if (!strncmp(p, "{chapters}", strlen("{chapters}"))) + { + if (ghb_settings_combo_int(settings, "PtoPType") == 0) + { + gint start, end; + start = ghb_dict_get_int(settings, "start_point"); + end = ghb_dict_get_int(settings, "end_point"); + if (start == end) + g_string_append_printf(str, "%d", start); + else + g_string_append_printf(str, "%d-%d", start, end); + } + p += strlen("{chapters}"); + } + else if (!strncmp(p, "{time}", strlen("{time}"))) + { + char st[6]; + struct tm *lt; + time_t t = time(NULL); + lt = localtime(&t); + st[0] = 0; + strftime(st, 6, "%H:%M", lt); + g_string_append_printf(str, "%s", st); + p += strlen("{time}"); + } + else if (!strncmp(p, "{date}", strlen("{date}"))) + { + char dt[11]; + struct tm *lt; + time_t t = time(NULL); + lt = localtime(&t); + dt[0] = 0; + strftime(dt, 11, "%Y-%m-%d", lt); + g_string_append_printf(str, "%s", dt); + p += strlen("{date}"); + } + else if (!strncmp(p, "{quality}", strlen("{quality}"))) + { + if (ghb_dict_get_bool(settings, "vquality_type_constant")) + { + gint vcodec; + const char *vqname; + double vquality; + vcodec = ghb_settings_video_encoder_codec(settings, "VideoEncoder"); + vqname = hb_video_quality_get_name(vcodec); + vquality = ghb_dict_get_double(settings, "VideoQualitySlider"); + g_string_append_printf(str, "%s%.3g", vqname, vquality); + } + p += strlen("{quality}"); + } + else if (!strncmp(p, "{bitrate}", strlen("{bitrate}"))) + { + if (ghb_dict_get_bool(settings, "vquality_type_bitrate")) + { + int vbitrate; + vbitrate = ghb_dict_get_int(settings, "VideoAvgBitrate"); + g_string_append_printf(str, "%dkbps", vbitrate); + } + p += strlen("{bitrate}"); + } + else + { + g_string_append_printf(str, "%c", *p); + p++; + } } - - val = ghb_dict_get_int(ud->settings, "angle"); - spin_configure(ud, "angle", val, 1, title->angle_count); + g_string_append_printf(str, ".%s", extension); + filename = g_string_free(str, FALSE); + ghb_dict_set_string(settings, "dest_file", filename); + g_free(filename); } - - float vqmin, vqmax, step, page; - int inverted, digits; - - ghb_vquality_range(ud, &vqmin, &vqmax, &step, &page, &digits, &inverted); - val = ghb_dict_get_double(ud->settings, "VideoQualitySlider"); - ghb_scale_configure(ud, "VideoQualitySlider", val, vqmin, vqmax, - step, page, digits, inverted); } static void -check_chapter_markers(signal_user_data_t *ud) +set_destination(signal_user_data_t *ud) { - GtkWidget *widget; - gint start, end; - - if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0) - { - start = ghb_dict_get_int(ud->settings, "start_point"); - end = ghb_dict_get_int(ud->settings, "end_point"); - widget = GHB_WIDGET (ud->builder, "ChapterMarkers"); - gtk_widget_set_sensitive(widget, end > start); - } + set_destination_settings(ud, ud->settings); + ghb_ui_update(ud, "dest_file", + ghb_dict_get_value(ud->settings, "dest_file")); } -void -ghb_load_settings(signal_user_data_t * ud) +static gchar* +get_file_label(const gchar *filename) { - const char * fullname; - int type; - gboolean preset_modified; - static gboolean busy = FALSE; - - if (busy) - return; - busy = TRUE; + gchar *base, *pos, *end; - fullname = ghb_dict_get_string(ud->settings, "PresetFullName"); - type = ghb_dict_get_int(ud->settings, "Type"); - preset_modified = ghb_dict_get_bool(ud->settings, "preset_modified"); - if (preset_modified) - { - ghb_clear_presets_selection(ud); - ghb_preset_menu_button_refresh(ud, fullname, type); - } - else + base = g_path_get_basename(filename); + pos = strrchr(base, '.'); + if (pos != NULL) { - ghb_dict_set_bool(ud->settings, "preset_reload", TRUE); - ghb_select_preset(ud, fullname, type); - ghb_dict_set_bool(ud->settings, "preset_reload", FALSE); + // If the last '.' is within 4 chars of end of name, assume + // there is an extension we want to strip. + end = &base[strlen(base) - 1]; + if (end - pos <= 4) + *pos = 0; } - - busy = FALSE; - - ghb_load_post_settings(ud); + return base; } -void -ghb_load_post_settings(signal_user_data_t * ud) +static gchar* +resolve_drive_name(gchar *filename) { - static gboolean busy = FALSE; - if (busy) - return; - busy = TRUE; - - ud->dont_clear_presets = TRUE; - ud->scale_busy = TRUE; +#if defined(_WIN32) + if (filename[1] == ':') + { + gchar drive[4]; + gchar *name; + gint dtype; - ghb_set_widget_ranges(ud, ud->settings); - ghb_check_all_depencencies(ud); - ghb_show_container_options(ud); - check_chapter_markers(ud); + g_strlcpy(drive, filename, 4); + dtype = GetDriveType(drive); + if (dtype == DRIVE_CDROM) + { + gchar vname[51], fsname[51]; + GetVolumeInformation(drive, vname, 50, NULL, + NULL, NULL, fsname, 50); + name = g_strdup(vname); + return name; + } + } + return NULL; +#else + return NULL; +#endif +} - ghb_clear_audio_selection(ud->builder); - ghb_clear_subtitle_selection(ud->builder); - ghb_settings_to_ui(ud, ud->settings); - ghb_audio_defaults_to_ui(ud); - ghb_subtitle_defaults_to_ui(ud); - ghb_audio_list_refresh_all(ud); - ghb_subtitle_list_refresh_all(ud); - ghb_chapter_list_refresh_all(ud); - update_title_duration(ud); - ghb_update_title_info(ud); +static gboolean +update_source_label(signal_user_data_t *ud, const gchar *source) +{ + GStatBuf stat_buf; + gchar *label = NULL; + gint len; + gchar **path; + gchar *start; + gchar *filename = g_strdup(source); - ud->dont_clear_presets = FALSE; - ud->scale_busy = FALSE; - busy = FALSE; + g_debug("update_source_label()"); + if (g_stat(filename, &stat_buf) == 0) + { + len = strlen(filename); + if (S_ISDIR(stat_buf.st_mode)) + { + // Skip dos drive letters +#if defined(_WIN32) + start = strchr(filename, ':'); +#else + start = filename; +#endif + label = resolve_drive_name(filename); + if (label != NULL) + { + if (uppers_and_unders(label)) + { + camel_convert(label); + } + } + else + { + if (filename[len-1] == G_DIR_SEPARATOR) filename[len-1] = 0; + if (start != NULL) + start++; + else + start = filename; - ghb_picture_settings_deps(ud); + path = g_strsplit(start, G_DIR_SEPARATOR_S, -1); + len = g_strv_length (path); + if ((len > 1) && (strcmp("VIDEO_TS", path[len-1]) == 0)) + { + label = g_strdup(path[len-2]); + if (uppers_and_unders(label)) + { + camel_convert(label); + } + } + else if (len > 0) + { + if (path[len-1][0] != 0) + { + label = g_strdup(path[len-1]); + if (uppers_and_unders(label)) + { + camel_convert(label); + } + } + else + label = g_strdup("new_video"); + } + else + label = g_strdup("new_video"); + g_strfreev (path); + } + } + else if (S_ISBLK(stat_buf.st_mode)) + { + // Is regular file or block dev. + // Check to see if it is a dvd image + label = ghb_dvd_volname(filename); + if (label == NULL) + { + label = get_file_label(filename); + } + else + { + if (uppers_and_unders(label)) + { + camel_convert(label); + } + } + } + else + { + label = get_file_label(filename); + } + } + else + { + label = get_file_label(filename); + } + g_free(filename); + GtkWidget *widget = GHB_WIDGET (ud->builder, "source_label"); + if (label != NULL) + { + gtk_label_set_text (GTK_LABEL(widget), label); + ghb_dict_set_string(ud->globals, "volume", label); + g_free(label); + } + else + { + label = _("No Title Found"); + gtk_label_set_text (GTK_LABEL(widget), label); + ghb_dict_set_string(ud->globals, "volume", label); + return FALSE; + } + return TRUE; } -static void -show_scan_progress(signal_user_data_t *ud) +G_MODULE_EXPORT void +chooser_file_selected_cb(GtkFileChooser *dialog, signal_user_data_t *ud) { - GtkWidget * widget; - GtkProgressBar * progress; - GtkLabel * label; + gchar *name = gtk_file_chooser_get_filename (dialog); + GtkTreeModel *store; + GtkTreeIter iter; + const gchar *device; + gboolean foundit = FALSE; + GtkComboBox *combo; - widget = GHB_WIDGET(ud->builder, "SourceInfoBox"); - gtk_widget_hide(widget); + g_debug("chooser_file_selected_cb ()"); - widget = GHB_WIDGET(ud->builder, "SourceScanBox"); - gtk_widget_show(widget); + if (name == NULL) return; + combo = GTK_COMBO_BOX(GHB_WIDGET(ud->builder, "source_device")); + store = gtk_combo_box_get_model(combo); + if (gtk_tree_model_get_iter_first(store, &iter)) + { + do + { + gtk_tree_model_get(store, &iter, 0, &device, -1); + if (strcmp(name, device) == 0) + { + foundit = TRUE; + break; + } + } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter)); + } + if (foundit) + gtk_combo_box_set_active_iter (combo, &iter); + else + gtk_combo_box_set_active (combo, 0); - progress = GTK_PROGRESS_BAR(GHB_WIDGET(ud->builder, "scan_prog")); - gtk_progress_bar_set_fraction(progress, 0); + g_free(name); +} - label = GTK_LABEL(GHB_WIDGET(ud->builder, "source_scan_label")); - gtk_label_set_text( label, _("Scanning ...") ); +G_MODULE_EXPORT void +dvd_device_changed_cb(GtkComboBoxText *combo, signal_user_data_t *ud) +{ + GtkWidget *dialog; + gint ii; + + g_debug("dvd_device_changed_cb ()"); + ii = gtk_combo_box_get_active (GTK_COMBO_BOX(combo)); + if (ii > 0) + { + const gchar *device; + gchar *name; + dialog = GHB_WIDGET(ud->builder, "source_dialog"); + device = gtk_combo_box_text_get_active_text(combo); + // Protext against unexpected NULL return value + if (device != NULL) + { + name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER(dialog)); + if (name == NULL || strcmp(name, device) != 0) + gtk_file_chooser_select_filename (GTK_FILE_CHOOSER(dialog), device); + if (name != NULL) + g_free(name); + } + } } static void -hide_scan_progress(signal_user_data_t *ud) +source_dialog_extra_widgets( + signal_user_data_t *ud, + GtkWidget *dialog) { - GtkWidget * widget; - GtkProgressBar * progress; + GtkComboBoxText *combo; + GList *drives, *link; - progress = GTK_PROGRESS_BAR(GHB_WIDGET(ud->builder, "scan_prog")); - gtk_progress_bar_set_fraction(progress, 1.0); + g_debug("source_dialog_extra_widgets ()"); + combo = GTK_COMBO_BOX_TEXT(GHB_WIDGET(ud->builder, "source_device")); + gtk_list_store_clear(GTK_LIST_STORE( + gtk_combo_box_get_model(GTK_COMBO_BOX(combo)))); - widget = GHB_WIDGET(ud->builder, "SourceScanBox"); - gtk_widget_hide(widget); + link = drives = dvd_device_list(); + gtk_combo_box_text_append_text (combo, _("Not Selected")); + while (link != NULL) + { + gchar *name = get_dvd_device_name(link->data); + gtk_combo_box_text_append_text(combo, name); + g_free(name); + free_drive(link->data); + link = link->next; + } + g_list_free(drives); + gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0); +} - widget = GHB_WIDGET(ud->builder, "SourceInfoBox"); - gtk_widget_show(widget); +static void break_duration(gint64 duration, gint *hh, gint *mm, gint *ss) +{ + *hh = duration / (60*60); + *mm = (duration / 60) % 60; + *ss = duration % 60; } static void -start_scan( - signal_user_data_t *ud, - const gchar *path, - gint title_id, - gint preview_count) +update_title_duration(signal_user_data_t *ud) { - GtkWidget *widget; - ghb_status_t status; - - ghb_get_status(&status); - if (status.scan.state != GHB_STATE_IDLE) - return; - - widget = GHB_WIDGET(ud->builder, "sourcetoolbutton"); - gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(widget), "hb-stop"); - gtk_tool_button_set_label(GTK_TOOL_BUTTON(widget), _("Stop\nScan")); - gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(widget), _("Stop Scan")); - - widget = GHB_WIDGET(ud->builder, "source_open"); - gtk_widget_set_sensitive(widget, FALSE); - widget = GHB_WIDGET(ud->builder, "source_title_open"); - gtk_widget_set_sensitive(widget, FALSE); - ghb_backend_scan(path, title_id, preview_count, - 90000L * ghb_dict_get_int(ud->prefs, "MinTitleDuration")); -} - -gboolean -ghb_idle_scan(signal_user_data_t *ud) -{ - gchar *path; - // ghb_do_scan replaces "scan_source" key in dict, so we must - // make a copy of the string. - path = g_strdup(ghb_dict_get_string(ud->globals, "scan_source")); - ghb_do_scan(ud, path, 0, TRUE); - g_free(path); - return FALSE; -} - -extern GhbValue *ghb_queue_edit_settings; -static gchar *last_scan_file = NULL; - -void -ghb_do_scan( - signal_user_data_t *ud, - const gchar *filename, - gint title_id, - gboolean force) -{ - int titleindex; + gint hh, mm, ss, start, end; + gchar *text; + GtkWidget *widget; + int title_id, titleindex; const hb_title_t *title; - (void)title; // Silence "unused variable" warning + title_id = ghb_dict_get_int(ud->settings, "title"); + title = ghb_lookup_title(title_id, &titleindex); + widget = GHB_WIDGET (ud->builder, "title_duration"); - g_debug("ghb_do_scan()"); - if (!force && last_scan_file != NULL && - strcmp(last_scan_file, filename) == 0) + if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0) { - if (ghb_queue_edit_settings != NULL) - { - title_id = ghb_dict_get_int(ghb_queue_edit_settings, "title"); - title = ghb_lookup_title(title_id, &titleindex); - ghb_array_replace(ud->settings_array, titleindex, - ghb_queue_edit_settings); - ud->settings = ghb_queue_edit_settings; - ghb_load_settings(ud); - ghb_queue_edit_settings = NULL; - } - else - { - title = ghb_lookup_title(title_id, &titleindex); - load_all_titles(ud, titleindex); - } - return; + start = ghb_dict_get_int(ud->settings, "start_point"); + end = ghb_dict_get_int(ud->settings, "end_point"); + ghb_part_duration(title, start, end, &hh, &mm, &ss); } - if (last_scan_file != NULL) - g_free(last_scan_file); - last_scan_file = NULL; - if (filename != NULL) + else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1) { - last_scan_file = g_strdup(filename); - ghb_dict_set_string(ud->globals, "scan_source", filename); - if (update_source_label(ud, filename)) - { - const gchar *path; - gint preview_count; + gint duration; - show_scan_progress(ud); - path = ghb_dict_get_string(ud->globals, "scan_source"); - prune_logs(ud); + start = ghb_dict_get_int(ud->settings, "start_point"); + end = ghb_dict_get_int(ud->settings, "end_point"); + duration = end - start; + break_duration(duration, &hh, &mm, &ss); + } + else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 2) + { + if (title != NULL) + { + gint64 frames; + gint duration; - preview_count = ghb_dict_get_int(ud->prefs, "preview_count"); - start_scan(ud, path, title_id, preview_count); + start = ghb_dict_get_int(ud->settings, "start_point"); + end = ghb_dict_get_int(ud->settings, "end_point"); + frames = end - start + 1; + duration = frames * title->vrate.den / title->vrate.num; + break_duration(duration, &hh, &mm, &ss); } else { - // TODO: error dialog + hh = mm = ss = 0; } } + text = g_strdup_printf("%02d:%02d:%02d", hh, mm, ss); + gtk_label_set_text(GTK_LABEL(widget), text); + g_free(text); } -static void -do_source_dialog(gboolean single, signal_user_data_t *ud) +void ghb_show_container_options(signal_user_data_t *ud) { - GtkWidget *dialog; - const gchar *sourcename; - gint response; - - g_debug("source_browse_clicked_cb ()"); - sourcename = ghb_dict_get_string(ud->globals, "scan_source"); - GtkWidget *widget; - widget = GHB_WIDGET(ud->builder, "single_title_box"); - if (single) - gtk_widget_show(widget); - else - gtk_widget_hide(widget); - dialog = GHB_WIDGET(ud->builder, "source_dialog"); - source_dialog_extra_widgets(ud, dialog); + GtkWidget *w1, *w2, *w3; + w1 = GHB_WIDGET(ud->builder, "AlignAVStart"); + w2 = GHB_WIDGET(ud->builder, "Mp4HttpOptimize"); + w3 = GHB_WIDGET(ud->builder, "Mp4iPodCompatible"); - gtk_widget_show(dialog); - gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(dialog), sourcename); + const char *mux_id; + const hb_container_t *mux; - response = gtk_dialog_run(GTK_DIALOG (dialog)); - gtk_widget_hide(dialog); - if (response == GTK_RESPONSE_NO) - { - gchar *filename; + mux_id = ghb_dict_get_string(ud->settings, "FileFormat"); + mux = ghb_lookup_container_by_name(mux_id); - filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); - if (filename != NULL) - { - gint title_id; + gint enc = ghb_settings_video_encoder_codec(ud->settings, "VideoEncoder"); - if (single) - title_id = ghb_dict_get_int(ud->settings, "single_title"); - else - title_id = 0; - // ghb_do_scan replaces "scan_source" key in dict, so we must - // be finished with sourcename before calling ghb_do_scan - // since the memory it references will be freed - if (strcmp(sourcename, filename) != 0) - { - ghb_dict_set_string(ud->prefs, "default_source", filename); - ghb_pref_save(ud->prefs, "default_source"); - ghb_dvd_set_current(filename, ud); - } - ghb_do_scan(ud, filename, title_id, TRUE); - g_free(filename); - } - } + gtk_widget_set_visible(w1, (mux->format & HB_MUX_MASK_MP4)); + gtk_widget_set_visible(w2, (mux->format & HB_MUX_MASK_MP4)); + gtk_widget_set_visible(w3, (mux->format & HB_MUX_MASK_MP4) && + (enc == HB_VCODEC_X264_8BIT)); } -#if 0 -G_MODULE_EXPORT void -source_button_clicked_cb(GtkButton *button, signal_user_data_t *ud) -{ - ghb_status_t status; - ghb_get_status(&status); - if (status.scan.state & GHB_STATE_SCANNING) - { - ghb_backend_scan_stop(); - } - else - { - do_source_dialog(FALSE, ud); - } -} -#else -G_MODULE_EXPORT void -source_action_cb(GSimpleAction *action, GVariant *param, - signal_user_data_t *ud) +static void +adjustment_configure( + GtkAdjustment *adj, + double val, + double min, double max, + double step, double page, double page_sz) { - ghb_status_t status; - ghb_get_status(&status); - if (status.scan.state & GHB_STATE_SCANNING) - { - ghb_backend_scan_stop(); - } - else - { - do_source_dialog(FALSE, ud); - } + gtk_adjustment_configure(adj, val, min, max, step, page, page_sz); } -#endif -G_MODULE_EXPORT void -single_title_action_cb(GSimpleAction *action, GVariant * param, - signal_user_data_t *ud) +static void +spin_configure(signal_user_data_t *ud, char *name, double val, double min, double max) { - do_source_dialog(TRUE, ud); -} + GtkSpinButton *spin; + GtkAdjustment *adj; + double step, page, page_sz; -G_MODULE_EXPORT void -dvd_source_activate_cb(GtkWidget *widget, signal_user_data_t *ud) -{ - const gchar *filename; - const gchar *sourcename; + spin = GTK_SPIN_BUTTON(GHB_WIDGET(ud->builder, name)); - // ghb_do_scan replaces "scan_source" key in dict, so we must - // be finished with sourcename before calling ghb_do_scan - // since the memory it references will be freed - sourcename = ghb_dict_get_string(ud->globals, "scan_source"); - filename = gtk_buildable_get_name(GTK_BUILDABLE(widget)); - if (strcmp(sourcename, filename) != 0) - { - ghb_dict_set_string(ud->prefs, "default_source", filename); - ghb_pref_save(ud->prefs, "default_source"); - ghb_dvd_set_current(filename, ud); - } - ghb_do_scan(ud, filename, 0, TRUE); + adj = gtk_spin_button_get_adjustment(spin); + step = gtk_adjustment_get_step_increment(adj); + page = gtk_adjustment_get_page_increment(adj); + page_sz = gtk_adjustment_get_page_size(adj); + + adjustment_configure(adj, val, min, max, step, page, page_sz); } void -ghb_update_destination_extension(signal_user_data_t *ud) +ghb_scale_configure( + signal_user_data_t *ud, + char *name, + double val, double min, double max, + double step, double page, + int digits, gboolean inverted) { - static gchar *containers[] = {".mkv", ".mp4", ".m4v", ".error", NULL}; - gchar *filename; - const gchar *extension; - gint ii; - GtkEntry *entry; - static gboolean busy = FALSE; + GtkScale *scale; + GtkAdjustment *adj; + double page_sz; - g_debug("ghb_update_destination_extension ()"); - // Since this function modifies the thing that triggers it's - // invocation, check to see if busy to prevent accidental infinite - // recursion. - if (busy) - return; - busy = TRUE; - extension = get_extension(ud, ud->settings); - entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "dest_file")); - filename = g_strdup(gtk_entry_get_text(entry)); - for (ii = 0; containers[ii] != NULL; ii++) + scale = GTK_SCALE(GHB_WIDGET(ud->builder, name)); + + adj = gtk_range_get_adjustment(GTK_RANGE(scale)); + page_sz = gtk_adjustment_get_page_size(adj); + + adjustment_configure(adj, val, min, max, step, page, page_sz); + + gtk_scale_set_digits(scale, digits); + gtk_range_set_inverted(GTK_RANGE(scale), inverted); +} + +void +ghb_set_widget_ranges(signal_user_data_t *ud, GhbValue *settings) +{ + int title_id, titleindex; + const hb_title_t * title; + double val; + + title_id = ghb_dict_get_int(settings, "title"); + title = ghb_lookup_title(title_id, &titleindex); + + // Reconfigure the UI combo boxes + ghb_update_ui_combo_box(ud, "AudioTrack", title, FALSE); + ghb_update_ui_combo_box(ud, "SubtitleTrack", title, FALSE); + + if (title != NULL) { - if (g_str_has_suffix(filename, containers[ii])) + + // Set the limits of cropping. hb_set_anamorphic_size crashes if + // you pass it a cropped width or height == 0. + gint vbound, hbound; + vbound = title->geometry.height; + hbound = title->geometry.width; + + val = ghb_dict_get_int(ud->settings, "PictureTopCrop"); + spin_configure(ud, "PictureTopCrop", val, 0, vbound); + val = ghb_dict_get_int(ud->settings, "PictureBottomCrop"); + spin_configure(ud, "PictureBottomCrop", val, 0, vbound); + val = ghb_dict_get_int(ud->settings, "PictureLeftCrop"); + spin_configure(ud, "PictureLeftCrop", val, 0, hbound); + val = ghb_dict_get_int(ud->settings, "PictureRightCrop"); + spin_configure(ud, "PictureRightCrop", val, 0, hbound); + + gint duration = title->duration / 90000; + + if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0) { - gchar *pos; - gchar *new_name; + gint num_chapters = hb_list_count(title->list_chapter); - pos = g_strrstr( filename, "." ); - if (pos == NULL) - { - // No period? shouldn't happen - break; - } - *pos = 0; - if (strcmp(extension, &pos[1]) == 0) - { - // Extension is already correct - break; - } - new_name = g_strjoin(".", filename, extension, NULL); - ghb_ui_update(ud, "dest_file", ghb_string_value(new_name)); - g_free(new_name); - break; + val = ghb_dict_get_int(ud->settings, "start_point"); + spin_configure(ud, "start_point", val, 1, num_chapters); + val = ghb_dict_get_int(ud->settings, "end_point"); + spin_configure(ud, "end_point", val, 1, num_chapters); + } + else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 1) + { + val = ghb_dict_get_int(ud->settings, "start_point"); + spin_configure(ud, "start_point", val, 0, duration * 2 - 1); + val = ghb_dict_get_int(ud->settings, "end_point"); + spin_configure(ud, "end_point", val, 0, duration * 2); + } + else if (ghb_settings_combo_int(ud->settings, "PtoPType") == 2) + { + gdouble max_frames; + max_frames = (gdouble)duration * + title->vrate.num / title->vrate.den; + + val = ghb_dict_get_int(ud->settings, "start_point"); + spin_configure(ud, "start_point", val, 1, max_frames * 2); + val = ghb_dict_get_int(ud->settings, "end_point"); + spin_configure(ud, "end_point", val, 1, max_frames * 2); } + + val = ghb_dict_get_int(ud->settings, "angle"); + spin_configure(ud, "angle", val, 1, title->angle_count); } - g_free(filename); - busy = FALSE; + + float vqmin, vqmax, step, page; + int inverted, digits; + + ghb_vquality_range(ud, &vqmin, &vqmax, &step, &page, &digits, &inverted); + val = ghb_dict_get_double(ud->settings, "VideoQualitySlider"); + ghb_scale_configure(ud, "VideoQualitySlider", val, vqmin, vqmax, + step, page, digits, inverted); } static void -destination_select_title(GtkEntry *entry) +check_chapter_markers(signal_user_data_t *ud) { - const gchar *dest; + GtkWidget *widget; gint start, end; - dest = gtk_entry_get_text(entry); - for (end = strlen(dest)-1; end > 0; end--) + if (ghb_settings_combo_int(ud->settings, "PtoPType") == 0) { - if (dest[end] == '.') - { - break; - } + start = ghb_dict_get_int(ud->settings, "start_point"); + end = ghb_dict_get_int(ud->settings, "end_point"); + widget = GHB_WIDGET (ud->builder, "ChapterMarkers"); + gtk_widget_set_sensitive(widget, end > start); } - for (start = end; start >= 0; start--) +} + +void +ghb_load_settings(signal_user_data_t * ud) +{ + const char * fullname; + int type; + gboolean preset_modified; + static gboolean busy = FALSE; + + if (busy) + return; + busy = TRUE; + + fullname = ghb_dict_get_string(ud->settings, "PresetFullName"); + type = ghb_dict_get_int(ud->settings, "Type"); + preset_modified = ghb_dict_get_bool(ud->settings, "preset_modified"); + if (preset_modified) { - if (dest[start] == G_DIR_SEPARATOR) - { - start++; - break; - } + ghb_clear_presets_selection(ud); + ghb_preset_menu_button_refresh(ud, fullname, type); } - if (start < 0) start = 0; - if (start < end) + else { - gtk_editable_select_region(GTK_EDITABLE(entry), start, end); + ghb_dict_set_bool(ud->settings, "preset_reload", TRUE); + ghb_select_preset(ud, fullname, type); + ghb_dict_set_bool(ud->settings, "preset_reload", FALSE); } + + busy = FALSE; + + ghb_load_post_settings(ud); } -G_MODULE_EXPORT gboolean -destination_grab_cb( - GtkEntry *entry, - signal_user_data_t *ud) +void +ghb_load_post_settings(signal_user_data_t * ud) { - destination_select_title(entry); - return FALSE; -} + static gboolean busy = FALSE; + if (busy) + return; + busy = TRUE; -static gboolean update_default_destination = FALSE; + ud->dont_clear_presets = TRUE; + ud->scale_busy = TRUE; -G_MODULE_EXPORT void -dest_dir_set_cb(GtkFileChooserButton *dest_chooser, signal_user_data_t *ud) -{ - const gchar *dest_file, *dest_dir; - gchar *dest; + ghb_set_widget_ranges(ud, ud->settings); + ghb_check_all_depencencies(ud); + ghb_show_container_options(ud); + check_chapter_markers(ud); - g_debug("dest_dir_set_cb ()"); - ghb_widget_to_setting(ud->settings, (GtkWidget*)dest_chooser); - dest_file = ghb_dict_get_string(ud->settings, "dest_file"); - dest_dir = ghb_dict_get_string(ud->settings, "dest_dir"); - dest = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dest_dir, dest_file); - ghb_dict_set_string(ud->settings, "destination", dest); - GhbValue *dest_dict = ghb_get_job_dest_settings(ud->settings); - ghb_dict_set_string(dest_dict, "File", dest); - g_free(dest); - update_default_destination = TRUE; + ghb_clear_audio_selection(ud->builder); + ghb_clear_subtitle_selection(ud->builder); + ghb_settings_to_ui(ud, ud->settings); + ghb_audio_defaults_to_ui(ud); + ghb_subtitle_defaults_to_ui(ud); + ghb_audio_list_refresh_all(ud); + ghb_subtitle_list_refresh_all(ud); + ghb_chapter_list_refresh_all(ud); + update_title_duration(ud); + ghb_update_title_info(ud); + + ud->dont_clear_presets = FALSE; + ud->scale_busy = FALSE; + busy = FALSE; + + ghb_picture_settings_deps(ud); } -G_MODULE_EXPORT void -dest_file_changed_cb(GtkEntry *entry, signal_user_data_t *ud) +static void +show_scan_progress(signal_user_data_t *ud) { - const gchar *dest_file, *dest_dir; - gchar *dest; + GtkWidget * widget; + GtkProgressBar * progress; + GtkLabel * label; + + widget = GHB_WIDGET(ud->builder, "SourceInfoBox"); + gtk_widget_hide(widget); + + widget = GHB_WIDGET(ud->builder, "SourceScanBox"); + gtk_widget_show(widget); + + progress = GTK_PROGRESS_BAR(GHB_WIDGET(ud->builder, "scan_prog")); + gtk_progress_bar_set_fraction(progress, 0); + + label = GTK_LABEL(GHB_WIDGET(ud->builder, "source_scan_label")); + gtk_label_set_text( label, _("Scanning ...") ); - g_debug("dest_file_changed_cb ()"); - ghb_update_destination_extension(ud); - ghb_widget_to_setting(ud->settings, (GtkWidget*)entry); - // This signal goes off with ever keystroke, so I'm putting this - // update on the timer. - dest_file = ghb_dict_get_string(ud->settings, "dest_file"); - dest_dir = ghb_dict_get_string(ud->settings, "dest_dir"); - dest = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dest_dir, dest_file); - ghb_dict_set_string(ud->settings, "destination", dest); - GhbValue *dest_dict = ghb_get_job_dest_settings(ud->settings); - ghb_dict_set_string(dest_dict, "File", dest); - g_free(dest); - update_default_destination = TRUE; } -G_MODULE_EXPORT void -destination_action_cb(GSimpleAction *action, GVariant *param, - signal_user_data_t *ud) +static void +hide_scan_progress(signal_user_data_t *ud) { - GtkWidget *dialog; - GtkEntry *entry; - const gchar *destname; - gchar *basename; - GtkWindow *hb_window; + GtkWidget * widget; + GtkProgressBar * progress; - hb_window = GTK_WINDOW(GHB_WIDGET(ud->builder, "hb_window")); - destname = ghb_dict_get_string(ud->settings, "destination"); - dialog = gtk_file_chooser_dialog_new("Choose Destination", - hb_window, - GTK_FILE_CHOOSER_ACTION_SAVE, - GHB_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - GHB_STOCK_SAVE, GTK_RESPONSE_ACCEPT, - NULL); - gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), destname); - basename = g_path_get_basename(destname); - gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), basename); - g_free(basename); - if (gtk_dialog_run(GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) - { - char *filename, *dirname; - GtkFileChooser *dest_chooser; + progress = GTK_PROGRESS_BAR(GHB_WIDGET(ud->builder, "scan_prog")); + gtk_progress_bar_set_fraction(progress, 1.0); - filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); - basename = g_path_get_basename(filename); - dirname = g_path_get_dirname(filename); - entry = (GtkEntry*)GHB_WIDGET(ud->builder, "dest_file"); - gtk_entry_set_text(entry, basename); - dest_chooser = GTK_FILE_CHOOSER(GHB_WIDGET(ud->builder, "dest_dir")); - gtk_file_chooser_set_filename(dest_chooser, dirname); - g_free (dirname); - g_free (basename); - g_free (filename); - } - gtk_widget_destroy(dialog); + widget = GHB_WIDGET(ud->builder, "SourceScanBox"); + gtk_widget_hide(widget); + + widget = GHB_WIDGET(ud->builder, "SourceInfoBox"); + gtk_widget_show(widget); } -G_MODULE_EXPORT gboolean -window_destroy_event_cb(GtkWidget *widget, GdkEvent *event, signal_user_data_t *ud) +static void +start_scan( + signal_user_data_t *ud, + const gchar *path, + gint title_id, + gint preview_count) { - g_debug("window_destroy_event_cb ()"); - ghb_hb_cleanup(FALSE); - prune_logs(ud); - g_application_quit(G_APPLICATION(ud->app)); + GtkWidget *widget; + ghb_status_t status; + + ghb_get_status(&status); + if (status.scan.state != GHB_STATE_IDLE) + return; + + widget = GHB_WIDGET(ud->builder, "sourcetoolbutton"); + gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(widget), "hb-stop"); + gtk_tool_button_set_label(GTK_TOOL_BUTTON(widget), _("Stop\nScan")); + gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(widget), _("Stop Scan")); + + widget = GHB_WIDGET(ud->builder, "source_open"); + gtk_widget_set_sensitive(widget, FALSE); + widget = GHB_WIDGET(ud->builder, "source_title_open"); + gtk_widget_set_sensitive(widget, FALSE); + ghb_backend_scan(path, title_id, preview_count, + 90000L * ghb_dict_get_int(ud->prefs, "MinTitleDuration")); +} + +gboolean +ghb_idle_scan(signal_user_data_t *ud) +{ + gchar *path; + // ghb_do_scan replaces "scan_source" key in dict, so we must + // make a copy of the string. + path = g_strdup(ghb_dict_get_string(ud->globals, "scan_source")); + ghb_do_scan(ud, path, 0, TRUE); + g_free(path); return FALSE; } -G_MODULE_EXPORT gboolean -window_delete_event_cb(GtkWidget *widget, GdkEvent *event, signal_user_data_t *ud) +extern GhbValue *ghb_queue_edit_settings; +static gchar *last_scan_file = NULL; + +void +ghb_do_scan( + signal_user_data_t *ud, + const gchar *filename, + gint title_id, + gboolean force) { - gint state = ghb_get_queue_state(); - g_debug("window_delete_event_cb ()"); - if (state & (GHB_STATE_WORKING|GHB_STATE_SEARCHING)) + int titleindex; + const hb_title_t *title; + + (void)title; // Silence "unused variable" warning + + g_debug("ghb_do_scan()"); + if (!force && last_scan_file != NULL && + strcmp(last_scan_file, filename) == 0) { - if (ghb_cancel_encode2(ud, _("Closing HandBrake will terminate encoding.\n"))) + if (ghb_queue_edit_settings != NULL) { - ghb_hb_cleanup(FALSE); + title_id = ghb_dict_get_int(ghb_queue_edit_settings, "title"); + title = ghb_lookup_title(title_id, &titleindex); + ghb_array_replace(ud->settings_array, titleindex, + ghb_queue_edit_settings); + ud->settings = ghb_queue_edit_settings; + ghb_load_settings(ud); + ghb_queue_edit_settings = NULL; + } + else + { + title = ghb_lookup_title(title_id, &titleindex); + load_all_titles(ud, titleindex); + } + return; + } + if (last_scan_file != NULL) + g_free(last_scan_file); + last_scan_file = NULL; + if (filename != NULL) + { + last_scan_file = g_strdup(filename); + ghb_dict_set_string(ud->globals, "scan_source", filename); + if (update_source_label(ud, filename)) + { + const gchar *path; + gint preview_count; + + show_scan_progress(ud); + path = ghb_dict_get_string(ud->globals, "scan_source"); prune_logs(ud); - g_application_quit(G_APPLICATION(ud->app)); - return FALSE; + + preview_count = ghb_dict_get_int(ud->prefs, "preview_count"); + start_scan(ud, path, title_id, preview_count); + } + else + { + // TODO: error dialog } - return TRUE; } - ghb_hb_cleanup(FALSE); - prune_logs(ud); - g_application_quit(G_APPLICATION(ud->app)); - return FALSE; } static void -update_acodec(signal_user_data_t *ud) +do_source_dialog(gboolean single, signal_user_data_t *ud) { - ghb_audio_list_refresh_all(ud); - ghb_grey_combo_options(ud); -} + GtkWidget *dialog; + const gchar *sourcename; + gint response; -G_MODULE_EXPORT void -container_changed_cb(GtkWidget *widget, signal_user_data_t *ud) -{ - g_debug("container_changed_cb ()"); - ghb_widget_to_setting(ud->settings, widget); - const char * mux_id = ghb_dict_get_string(ud->settings, "FileFormat"); - GhbValue *dest_dict = ghb_get_job_dest_settings(ud->settings); - ghb_dict_set_string(dest_dict, "Mux", mux_id); + g_debug("source_browse_clicked_cb ()"); + sourcename = ghb_dict_get_string(ud->globals, "scan_source"); + GtkWidget *widget; + widget = GHB_WIDGET(ud->builder, "single_title_box"); + if (single) + gtk_widget_show(widget); + else + gtk_widget_hide(widget); + dialog = GHB_WIDGET(ud->builder, "source_dialog"); + source_dialog_extra_widgets(ud, dialog); - const hb_container_t *mux = ghb_lookup_container_by_name(mux_id); - if (!(mux->format & HB_MUX_MASK_MP4)) + gtk_widget_show(dialog); + gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(dialog), sourcename); + + response = gtk_dialog_run(GTK_DIALOG (dialog)); + gtk_widget_hide(dialog); + if (response == GTK_RESPONSE_NO) { - ghb_ui_update(ud, "AlignAVStart", ghb_boolean_value(FALSE)); - } + gchar *filename; - ghb_check_dependency(ud, widget, NULL); - ghb_show_container_options(ud); - update_acodec(ud); - ghb_update_destination_extension(ud); - ghb_clear_presets_selection(ud); - ghb_live_reset(ud); - ghb_subtitle_prune(ud); - ghb_subtitle_list_refresh_all(ud); - ghb_audio_list_refresh_selected(ud); + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + if (filename != NULL) + { + gint title_id; + + if (single) + title_id = ghb_dict_get_int(ud->settings, "single_title"); + else + title_id = 0; + // ghb_do_scan replaces "scan_source" key in dict, so we must + // be finished with sourcename before calling ghb_do_scan + // since the memory it references will be freed + if (strcmp(sourcename, filename) != 0) + { + ghb_dict_set_string(ud->prefs, "default_source", filename); + ghb_pref_save(ud->prefs, "default_source"); + ghb_dvd_set_current(filename, ud); + } + ghb_do_scan(ud, filename, title_id, TRUE); + g_free(filename); + } + } } -static gchar* -get_aspect_string(gint aspect_n, gint aspect_d) +#if 0 +G_MODULE_EXPORT void +source_button_clicked_cb(GtkButton *button, signal_user_data_t *ud) { - gchar *aspect; - - if (aspect_d < 10) + ghb_status_t status; + ghb_get_status(&status); + if (status.scan.state & GHB_STATE_SCANNING) { - aspect = g_strdup_printf("%d:%d", aspect_n, aspect_d); + ghb_backend_scan_stop(); } else { - gdouble aspect_nf = (gdouble)aspect_n / aspect_d; - aspect = g_strdup_printf("%.2f:1", aspect_nf); + do_source_dialog(FALSE, ud); } - return aspect; } - -static gchar* -get_rate_string(gint rate_num, gint rate_den) +#else +G_MODULE_EXPORT void +source_action_cb(GSimpleAction *action, GVariant *param, + signal_user_data_t *ud) { - gdouble rate_f = (gdouble)rate_num / rate_den; - gchar *rate_s; - - rate_s = g_strdup_printf("%.6g", rate_f); - return rate_s; + ghb_status_t status; + ghb_get_status(&status); + if (status.scan.state & GHB_STATE_SCANNING) + { + ghb_backend_scan_stop(); + } + else + { + do_source_dialog(FALSE, ud); + } } +#endif -static void -update_aspect_info(signal_user_data_t *ud) +G_MODULE_EXPORT void +single_title_action_cb(GSimpleAction *action, GVariant * param, + signal_user_data_t *ud) { + do_source_dialog(TRUE, ud); } -static void -update_crop_info(signal_user_data_t *ud) +G_MODULE_EXPORT void +dvd_source_activate_cb(GtkWidget *widget, signal_user_data_t *ud) { - GtkWidget *widget; - gchar *text; - gint width, height, crop[4] = {0,}; - gint title_id, titleindex; - const hb_title_t *title; + const gchar *filename; + const gchar *sourcename; - title_id = ghb_dict_get_int(ud->settings, "title"); - title = ghb_lookup_title(title_id, &titleindex); - if (title != NULL) + // ghb_do_scan replaces "scan_source" key in dict, so we must + // be finished with sourcename before calling ghb_do_scan + // since the memory it references will be freed + sourcename = ghb_dict_get_string(ud->globals, "scan_source"); + filename = gtk_buildable_get_name(GTK_BUILDABLE(widget)); + if (strcmp(sourcename, filename) != 0) { - crop[0] = ghb_dict_get_int(ud->settings, "PictureTopCrop"); - crop[1] = ghb_dict_get_int(ud->settings, "PictureBottomCrop"); - crop[2] = ghb_dict_get_int(ud->settings, "PictureLeftCrop"); - crop[3] = ghb_dict_get_int(ud->settings, "PictureRightCrop"); - width = title->geometry.width - crop[2] - crop[3]; - height = title->geometry.height - crop[0] - crop[1]; - text = g_strdup_printf ("%d x %d", width, height); - widget = GHB_WIDGET(ud->builder, "crop_dimensions2"); - gtk_label_set_text(GTK_LABEL(widget), text); - g_free(text); + ghb_dict_set_string(ud->prefs, "default_source", filename); + ghb_pref_save(ud->prefs, "default_source"); + ghb_dvd_set_current(filename, ud); } -} - -static void -update_scale_info(signal_user_data_t *ud) -{ + ghb_do_scan(ud, filename, 0, TRUE); } void -ghb_update_title_info(signal_user_data_t *ud) +ghb_update_destination_extension(signal_user_data_t *ud) { - GtkWidget * widget; - gchar * text; - gchar * aspect; - gchar * rate; - int title_id, titleindex; - int audio_count, subtitle_count; - const hb_title_t * title; - const hb_geometry_t * geo; - gint aspect_n, aspect_d; + static gchar *containers[] = {".mkv", ".mp4", ".m4v", ".error", NULL}; + gchar *filename; + const gchar *extension; + gint ii; + GtkEntry *entry; + static gboolean busy = FALSE; - title_id = ghb_dict_get_int(ud->settings, "title"); - title = ghb_lookup_title(title_id, &titleindex); - if (title == NULL) + g_debug("ghb_update_destination_extension ()"); + // Since this function modifies the thing that triggers it's + // invocation, check to see if busy to prevent accidental infinite + // recursion. + if (busy) return; + busy = TRUE; + extension = get_extension(ud, ud->settings); + entry = GTK_ENTRY(GHB_WIDGET(ud->builder, "dest_file")); + filename = g_strdup(gtk_entry_get_text(entry)); + for (ii = 0; containers[ii] != NULL; ii++) + { + if (g_str_has_suffix(filename, containers[ii])) + { + gchar *pos; + gchar *new_name; - update_title_duration(ud); + pos = g_strrstr( filename, "." ); + if (pos == NULL) + { + // No period? shouldn't happen + break; + } + *pos = 0; + if (strcmp(extension, &pos[1]) == 0) + { + // Extension is already correct + break; + } + new_name = g_strjoin(".", filename, extension, NULL); + ghb_ui_update(ud, "dest_file", ghb_string_value(new_name)); + g_free(new_name); + break; + } + } + g_free(filename); + busy = FALSE; +} - geo = &title->geometry; - hb_reduce(&aspect_n, &aspect_d, geo->width * geo->par.num, - geo->height * geo->par.den); - aspect = get_aspect_string(aspect_n, aspect_d); - rate = get_rate_string(title->vrate.num, title->vrate.den); - audio_count = hb_list_count(title->list_audio); - subtitle_count = hb_list_count(title->list_subtitle); +static void +destination_select_title(GtkEntry *entry) +{ + const gchar *dest; + gint start, end; - text = g_strdup_printf( - ", %dx%d (%dx%d), %s, %s FPS, %d Audio Track%s, %d Subtitle Track%s", - geo->width, geo->height, - geo->width * geo->par.num / geo->par.den, geo->height, - aspect, rate, - audio_count, audio_count == 1 ? "" : "s", - subtitle_count, subtitle_count == 1 ? "" : "s"); + dest = gtk_entry_get_text(entry); + for (end = strlen(dest)-1; end > 0; end--) + { + if (dest[end] == '.') + { + break; + } + } + for (start = end; start >= 0; start--) + { + if (dest[start] == G_DIR_SEPARATOR) + { + start++; + break; + } + } + if (start < 0) start = 0; + if (start < end) + { + gtk_editable_select_region(GTK_EDITABLE(entry), start, end); + } +} - widget = GHB_WIDGET(ud->builder, "source_info_label"); - gtk_label_set_text(GTK_LABEL(widget), text); - free(text); - free(aspect); - free(rate); +G_MODULE_EXPORT gboolean +destination_grab_cb( + GtkEntry *entry, + signal_user_data_t *ud) +{ + destination_select_title(entry); + return FALSE; +} - ghb_update_display_aspect_label(ud); +static gboolean update_default_destination = FALSE; - update_crop_info(ud); - update_aspect_info(ud); - update_scale_info(ud); +G_MODULE_EXPORT void +dest_dir_set_cb(GtkFileChooserButton *dest_chooser, signal_user_data_t *ud) +{ + const gchar *dest_file, *dest_dir; + gchar *dest; + + g_debug("dest_dir_set_cb ()"); + ghb_widget_to_setting(ud->settings, (GtkWidget*)dest_chooser); + dest_file = ghb_dict_get_string(ud->settings, "dest_file"); + dest_dir = ghb_dict_get_string(ud->settings, "dest_dir"); + dest = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dest_dir, dest_file); + ghb_dict_set_string(ud->settings, "destination", dest); + GhbValue *dest_dict = ghb_get_job_dest_settings(ud->settings); + ghb_dict_set_string(dest_dict, "File", dest); + g_free(dest); + update_default_destination = TRUE; } -static void update_meta(GhbValue *settings, const char *name, const char *val) +G_MODULE_EXPORT void +dest_file_changed_cb(GtkEntry *entry, signal_user_data_t *ud) { - GhbValue *metadata = ghb_get_job_metadata_settings(settings); + const gchar *dest_file, *dest_dir; + gchar *dest; - if (val == NULL) - ghb_dict_remove(metadata, name); - else - ghb_dict_set_string(metadata, name, val); + g_debug("dest_file_changed_cb ()"); + ghb_update_destination_extension(ud); + ghb_widget_to_setting(ud->settings, (GtkWidget*)entry); + // This signal goes off with ever keystroke, so I'm putting this + // update on the timer. + dest_file = ghb_dict_get_string(ud->settings, "dest_file"); + dest_dir = ghb_dict_get_string(ud->settings, "dest_dir"); + dest = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", dest_dir, dest_file); + ghb_dict_set_string(ud->settings, "destination", dest); + GhbValue *dest_dict = ghb_get_job_dest_settings(ud->settings); + ghb_dict_set_string(dest_dict, "File", dest); + g_free(dest); + update_default_destination = TRUE; } -void -ghb_update_summary_info(signal_user_data_t *ud) +G_MODULE_EXPORT void +destination_action_cb(GSimpleAction *action, GVariant *param, + signal_user_data_t *ud) { - GString * str; - char * text; - int title_id; - GtkWidget * widget; - GhbValue * titleDict; + GtkWidget *dialog; + GtkEntry *entry; + const gchar *destname; + gchar *basename; + GtkWindow *hb_window; - title_id = ghb_dict_get_int(ud->settings, "title"); - titleDict = ghb_get_title_dict(title_id); - if (titleDict == NULL) + hb_window = GTK_WINDOW(GHB_WIDGET(ud->builder, "hb_window")); + destname = ghb_dict_get_string(ud->settings, "destination"); + dialog = gtk_file_chooser_dialog_new("Choose Destination", + hb_window, + GTK_FILE_CHOOSER_ACTION_SAVE, + GHB_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GHB_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), destname); + basename = g_path_get_basename(destname); + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), basename); + g_free(basename); + if (gtk_dialog_run(GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { - // No title, clear summary - widget = GHB_WIDGET(ud->builder, "tracks_summary"); - gtk_label_set_text(GTK_LABEL(widget), ""); - widget = GHB_WIDGET(ud->builder, "filters_summary"); - gtk_label_set_text(GTK_LABEL(widget), ""); - widget = GHB_WIDGET(ud->builder, "dimensions_summary"); - gtk_label_set_text(GTK_LABEL(widget), "--"); - widget = GHB_WIDGET(ud->builder, "preview_button_image"); - gtk_image_set_from_icon_name(GTK_IMAGE(widget), "hb-icon", 128); - return; + char *filename, *dirname; + GtkFileChooser *dest_chooser; + + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + basename = g_path_get_basename(filename); + dirname = g_path_get_dirname(filename); + entry = (GtkEntry*)GHB_WIDGET(ud->builder, "dest_file"); + gtk_entry_set_text(entry, basename); + dest_chooser = GTK_FILE_CHOOSER(GHB_WIDGET(ud->builder, "dest_dir")); + gtk_file_chooser_set_filename(dest_chooser, dirname); + g_free (dirname); + g_free (basename); + g_free (filename); + } + gtk_widget_destroy(dialog); +} + +G_MODULE_EXPORT gboolean +window_destroy_event_cb(GtkWidget *widget, GdkEvent *event, signal_user_data_t *ud) +{ + g_debug("window_destroy_event_cb ()"); + ghb_hb_cleanup(FALSE); + prune_logs(ud); + g_application_quit(G_APPLICATION(ud->app)); + return FALSE; +} + +G_MODULE_EXPORT gboolean +window_delete_event_cb(GtkWidget *widget, GdkEvent *event, signal_user_data_t *ud) +{ + gint state = ghb_get_queue_state(); + g_debug("window_delete_event_cb ()"); + if (state & (GHB_STATE_WORKING|GHB_STATE_SEARCHING)) + { + if (ghb_cancel_encode2(ud, _("Closing HandBrake will terminate encoding.\n"))) + { + ghb_hb_cleanup(FALSE); + prune_logs(ud); + g_application_quit(G_APPLICATION(ud->app)); + return FALSE; + } + return TRUE; + } + ghb_hb_cleanup(FALSE); + prune_logs(ud); + g_application_quit(G_APPLICATION(ud->app)); + return FALSE; +} + +static void +update_acodec(signal_user_data_t *ud) +{ + ghb_audio_list_refresh_all(ud); + ghb_grey_combo_options(ud); +} + +G_MODULE_EXPORT void +container_changed_cb(GtkWidget *widget, signal_user_data_t *ud) +{ + g_debug("container_changed_cb ()"); + ghb_widget_to_setting(ud->settings, widget); + const char * mux_id = ghb_dict_get_string(ud->settings, "FileFormat"); + GhbValue *dest_dict = ghb_get_job_dest_settings(ud->settings); + ghb_dict_set_string(dest_dict, "Mux", mux_id); + + const hb_container_t *mux = ghb_lookup_container_by_name(mux_id); + if (!(mux->format & HB_MUX_MASK_MP4)) + { + ghb_ui_update(ud, "AlignAVStart", ghb_boolean_value(FALSE)); + } + + ghb_check_dependency(ud, widget, NULL); + ghb_show_container_options(ud); + update_acodec(ud); + ghb_update_destination_extension(ud); + ghb_clear_presets_selection(ud); + ghb_live_reset(ud); + ghb_subtitle_prune(ud); + ghb_subtitle_list_refresh_all(ud); + ghb_audio_list_refresh_selected(ud); +} + +static gchar* +get_aspect_string(gint aspect_n, gint aspect_d) +{ + gchar *aspect; + + if (aspect_d < 10) + { + aspect = g_strdup_printf("%d:%d", aspect_n, aspect_d); + } + else + { + gdouble aspect_nf = (gdouble)aspect_n / aspect_d; + aspect = g_strdup_printf("%.2f:1", aspect_nf); + } + return aspect; +} + +static gchar* +get_rate_string(gint rate_num, gint rate_den) +{ + gdouble rate_f = (gdouble)rate_num / rate_den; + gchar *rate_s; + + rate_s = g_strdup_printf("%.6g", rate_f); + return rate_s; +} + +static void +update_aspect_info(signal_user_data_t *ud) +{ +} + +static void +update_crop_info(signal_user_data_t *ud) +{ + GtkWidget *widget; + gchar *text; + gint width, height, crop[4] = {0,}; + gint title_id, titleindex; + const hb_title_t *title; + + title_id = ghb_dict_get_int(ud->settings, "title"); + title = ghb_lookup_title(title_id, &titleindex); + if (title != NULL) + { + crop[0] = ghb_dict_get_int(ud->settings, "PictureTopCrop"); + crop[1] = ghb_dict_get_int(ud->settings, "PictureBottomCrop"); + crop[2] = ghb_dict_get_int(ud->settings, "PictureLeftCrop"); + crop[3] = ghb_dict_get_int(ud->settings, "PictureRightCrop"); + width = title->geometry.width - crop[2] - crop[3]; + height = title->geometry.height - crop[0] - crop[1]; + text = g_strdup_printf ("%d x %d", width, height); + widget = GHB_WIDGET(ud->builder, "crop_dimensions2"); + gtk_label_set_text(GTK_LABEL(widget), text); + g_free(text); + } +} + +static void +update_scale_info(signal_user_data_t *ud) +{ +} + +void +ghb_update_title_info(signal_user_data_t *ud) +{ + GtkWidget * widget; + gchar * text; + gchar * aspect; + gchar * rate; + int title_id, titleindex; + int audio_count, subtitle_count; + const hb_title_t * title; + const hb_geometry_t * geo; + gint aspect_n, aspect_d; + + title_id = ghb_dict_get_int(ud->settings, "title"); + title = ghb_lookup_title(title_id, &titleindex); + if (title == NULL) + return; + + update_title_duration(ud); + + geo = &title->geometry; + hb_reduce(&aspect_n, &aspect_d, geo->width * geo->par.num, + geo->height * geo->par.den); + aspect = get_aspect_string(aspect_n, aspect_d); + rate = get_rate_string(title->vrate.num, title->vrate.den); + audio_count = hb_list_count(title->list_audio); + subtitle_count = hb_list_count(title->list_subtitle); + + text = g_strdup_printf( + ", %dx%d (%dx%d), %s, %s FPS, %d Audio Track%s, %d Subtitle Track%s", + geo->width, geo->height, + geo->width * geo->par.num / geo->par.den, geo->height, + aspect, rate, + audio_count, audio_count == 1 ? "" : "s", + subtitle_count, subtitle_count == 1 ? "" : "s"); + + widget = GHB_WIDGET(ud->builder, "source_info_label"); + gtk_label_set_text(GTK_LABEL(widget), text); + free(text); + free(aspect); + free(rate); + + ghb_update_display_aspect_label(ud); + + update_crop_info(ud); + update_aspect_info(ud); + update_scale_info(ud); +} + +static void update_meta(GhbValue *settings, const char *name, const char *val) +{ + GhbValue *metadata = ghb_get_job_metadata_settings(settings); + + if (val == NULL) + ghb_dict_remove(metadata, name); + else + ghb_dict_set_string(metadata, name, val); +} + +void +ghb_update_summary_info(signal_user_data_t *ud) +{ + GString * str; + char * text; + int title_id; + GtkWidget * widget; + GhbValue * titleDict; + + title_id = ghb_dict_get_int(ud->settings, "title"); + titleDict = ghb_get_title_dict(title_id); + if (titleDict == NULL) + { + // No title, clear summary + widget = GHB_WIDGET(ud->builder, "tracks_summary"); + gtk_label_set_text(GTK_LABEL(widget), ""); + widget = GHB_WIDGET(ud->builder, "filters_summary"); + gtk_label_set_text(GTK_LABEL(widget), ""); + widget = GHB_WIDGET(ud->builder, "dimensions_summary"); + gtk_label_set_text(GTK_LABEL(widget), "--"); + widget = GHB_WIDGET(ud->builder, "preview_button_image"); + gtk_image_set_from_icon_name(GTK_IMAGE(widget), "hb-icon", 128); + return; } // Video Track @@ -3442,7 +3984,8 @@ ghb_start_next_job(signal_user_data_t *ud) status = ghb_dict_get_int(uiDict, "job_status"); if (status == GHB_QUEUE_PENDING) { - ghb_inhibit_gsm(ud); + ghb_inhibit_suspend(ud); +printf("inhibited %d\n", suspend_inhibited); submit_job(ud, queueDict); ghb_update_pending(ud); @@ -3464,7 +4007,7 @@ ghb_start_next_job(signal_user_data_t *ud) } } // Nothing pending - ghb_uninhibit_gsm(); + ghb_uninhibit_suspend(ud); ghb_notify_done(ud); ghb_update_pending(ud); gtk_widget_hide(progress); @@ -3836,7 +4379,7 @@ ghb_backend_events(signal_user_data_t *ud) } else { - ghb_uninhibit_gsm(); + ghb_uninhibit_suspend(ud); gtk_widget_hide(GTK_WIDGET(progress)); ghb_dict_set_bool(ud->globals, "SkipDiskFreeCheck", FALSE); } @@ -3858,1581 +4401,1068 @@ ghb_backend_events(signal_user_data_t *ud) gtk_progress_bar_set_fraction (live_progress, status.live.progress); g_free(status_str); } - if (status.live.state & GHB_STATE_WORKDONE) - { - switch( status.live.error ) - { - case GHB_ERROR_NONE: - { - ghb_live_encode_done(ud, TRUE); - } break; - default: - { - ghb_live_encode_done(ud, FALSE); - } break; - } - ghb_clear_live_state(GHB_STATE_WORKDONE); - } -} - -G_MODULE_EXPORT gboolean -ghb_timer_cb(gpointer data) -{ - signal_user_data_t *ud = (signal_user_data_t*)data; - - ghb_live_preview_progress(ud); - ghb_backend_events(ud); - if (update_default_destination) - { - const gchar *dest, *def_dest; - gchar *dest_dir; - dest = ghb_dict_get_string(ud->settings, "destination"); - dest_dir = g_path_get_dirname(dest); - def_dest = ghb_dict_get_string(ud->prefs, "destination_dir"); - if (strcmp(dest_dir, def_dest) != 0) - { - ghb_dict_set_string(ud->prefs, "destination_dir", dest_dir); - ghb_pref_save(ud->prefs, "destination_dir"); - } - g_free(dest_dir); - update_default_destination = FALSE; - } - if (update_preview) - { - g_debug("Updating preview\n"); - ghb_set_preview_image (ud); - update_preview = FALSE; - } - -#if !defined(_NO_UPDATE_CHECK) - if (!appcast_busy) - { - const gchar *updates; - updates = ghb_dict_get_string(ud->prefs, "check_updates"); - gint64 duration = 0; - if (strcmp(updates, "daily") == 0) - duration = 60 * 60 * 24; - else if (strcmp(updates, "weekly") == 0) - duration = 60 * 60 * 24 * 7; - else if (strcmp(updates, "monthly") == 0) - duration = 60 * 60 * 24 * 7; - - if (duration != 0) - { - gint64 last; - time_t tt; - - last = ghb_dict_get_int(ud->prefs, "last_update_check"); - time(&tt); - if (last + duration < tt) - { - ghb_dict_set_int(ud->prefs, - "last_update_check", tt); - ghb_pref_save(ud->prefs, "last_update_check"); - GHB_THREAD_NEW("Update Check", (GThreadFunc)ghb_check_update, ud); - } - } - } -#endif - return TRUE; -} - -gboolean scroll_at_bottom(signal_user_data_t *ud, const char *scroll) -{ - GtkScrolledWindow *window; - GtkAdjustment *adj; - double val, upper, ps; - - window = GTK_SCROLLED_WINDOW(GHB_WIDGET(ud->builder, scroll)); - adj = gtk_scrolled_window_get_vadjustment(window); - val = gtk_adjustment_get_value(adj); - upper = gtk_adjustment_get_upper(adj); - ps = gtk_adjustment_get_page_size(adj); - return val >= upper - ps; -} - -G_MODULE_EXPORT gboolean -activity_scroll_to_bottom(signal_user_data_t *ud) -{ - GtkScrolledWindow *window; - GtkAdjustment *adj; - double upper, ps; - - window = GTK_SCROLLED_WINDOW(GHB_WIDGET(ud->builder, "activity_scroll")); - adj = gtk_scrolled_window_get_vadjustment(window); - upper = gtk_adjustment_get_upper(adj); - ps = gtk_adjustment_get_page_size(adj); - gtk_adjustment_set_value(adj, upper - ps); - return FALSE; -} - -G_MODULE_EXPORT gboolean -ghb_log_cb(GIOChannel *source, GIOCondition cond, gpointer data) -{ - gchar *text = NULL; - gsize length, outlength; - GtkTextView *textview; - GtkTextBuffer *buffer; - GtkTextIter iter; - GError *gerror = NULL; - GIOStatus status; - - signal_user_data_t *ud = (signal_user_data_t*)data; - - status = g_io_channel_read_line (source, &text, &length, NULL, &gerror); - // Trim nils from end of text, they cause g_io_channel_write_chars to - // fail with an assertion that aborts - while (length > 0 && text[length-1] == 0) - length--; - if (text != NULL && length > 0) - { - gboolean bottom = FALSE; - gchar *utf8_text; - - bottom = scroll_at_bottom(ud, "activity_scroll"); - textview = GTK_TEXT_VIEW(GHB_WIDGET (ud->builder, "activity_view")); - buffer = gtk_text_view_get_buffer (textview); - gtk_text_buffer_get_end_iter(buffer, &iter); - // Assume logging is in current locale - utf8_text = g_locale_to_utf8(text, -1, NULL, &length, NULL); - if (utf8_text != NULL) - { - gtk_text_buffer_insert(buffer, &iter, utf8_text, -1); - if (bottom) - { - static guint scroll_tok = 0; - GSource *source = NULL; - if (scroll_tok > 0) - source = g_main_context_find_source_by_id(NULL, scroll_tok); - if (source != NULL) - { - g_source_remove(scroll_tok); - } - scroll_tok = g_idle_add((GSourceFunc)activity_scroll_to_bottom, - ud); - } -#if defined(_WIN32) - gsize one = 1; - utf8_text[length-1] = '\r'; -#endif - g_io_channel_write_chars (ud->activity_log, utf8_text, - length, &outlength, NULL); -#if defined(_WIN32) - g_io_channel_write_chars (ud->activity_log, "\n", - one, &one, NULL); -#endif - g_io_channel_flush(ud->activity_log, NULL); - if (ud->job_activity_log) - { - g_io_channel_write_chars (ud->job_activity_log, utf8_text, - length, &outlength, NULL); -#if defined(_WIN32) - g_io_channel_write_chars (ud->activity_log, "\n", - one, &outlength, NULL); -#endif - g_io_channel_flush(ud->job_activity_log, NULL); - } - g_free(utf8_text); - } - } - if (text != NULL) - g_free(text); - - if (status != G_IO_STATUS_NORMAL) - { - // This should never happen, but if it does I would get into an - // infinite loop. Returning false removes this callback. - g_warning("Error while reading activity from pipe"); - if (gerror != NULL) - { - g_warning("%s", gerror->message); - g_error_free (gerror); - } - return FALSE; - } - if (gerror != NULL) - g_error_free (gerror); - return TRUE; -} - -static void -update_activity_labels(signal_user_data_t *ud, gboolean active) -{ - GtkToolButton *button; - - button = GTK_TOOL_BUTTON(GHB_WIDGET(ud->builder, "show_activity")); - - if (!active) - { - gtk_tool_button_set_label(button, "Show\nActivity"); - } - else - { - gtk_tool_button_set_label(button, "Hide\nActivity"); - } -} - -G_MODULE_EXPORT void -show_activity_action_cb(GSimpleAction *action, GVariant *value, - signal_user_data_t *ud) -{ - GtkWidget * activity_window; - gboolean state = g_variant_get_boolean(value); - - g_simple_action_set_state(action, value); - activity_window = GHB_WIDGET(ud->builder, "activity_window"); - gtk_widget_set_visible(activity_window, state); - update_activity_labels(ud, state); -} - -G_MODULE_EXPORT gboolean -presets_window_delete_cb(GtkWidget *xwidget, GdkEvent *event, signal_user_data_t *ud) -{ - GSimpleAction * action; - GVariant * state = g_variant_new_boolean(FALSE); - - action = G_SIMPLE_ACTION(g_action_map_lookup_action( - G_ACTION_MAP(ud->app), "show-presets")); - g_action_change_state(G_ACTION(action), state); - return TRUE; -} - -G_MODULE_EXPORT gboolean -activity_window_delete_cb(GtkWidget *xwidget, GdkEvent *event, signal_user_data_t *ud) -{ - gtk_widget_set_visible(xwidget, FALSE); - GtkWidget *widget = GHB_WIDGET (ud->builder, "show_activity"); - gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), FALSE); - return TRUE; -} - -void -ghb_log(gchar *log, ...) -{ - va_list args; - time_t _now; - struct tm *now; - gchar fmt[362]; - - _now = time(NULL); - now = localtime( &_now ); - snprintf(fmt, 362, "[%02d:%02d:%02d] gtkgui: %s\n", - now->tm_hour, now->tm_min, now->tm_sec, log); - va_start(args, log); - vfprintf(stderr, fmt, args); - va_end(args); -} - -static void -browse_url(const gchar *url) -{ -#if defined(_WIN32) - ShellExecute(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL); -#else - gboolean result; - char *argv[] = - {"xdg-open",NULL,NULL,NULL}; - argv[1] = (gchar*)url; - result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, - NULL, NULL, NULL); - if (result) return; - - argv[0] = "gnome-open"; - result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, - NULL, NULL, NULL); - if (result) return; - - argv[0] = "kfmclient"; - argv[1] = "exec"; - argv[2] = (gchar*)url; - result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, - NULL, NULL, NULL); - if (result) return; - - argv[0] = "firefox"; - argv[1] = (gchar*)url; - argv[2] = NULL; - result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, - NULL, NULL, NULL); -#endif -} - -G_MODULE_EXPORT void -about_action_cb(GSimpleAction *action, GVariant *param, signal_user_data_t *ud) -{ - GtkWidget *widget = GHB_WIDGET (ud->builder, "hb_about"); - gchar *ver; - - ver = g_strdup_printf("%s (%s)", HB_PROJECT_VERSION, HB_PROJECT_BUILD_ARCH); - gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(widget), ver); - g_free(ver); - gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(widget), - HB_PROJECT_URL_WEBSITE); - gtk_about_dialog_set_website_label(GTK_ABOUT_DIALOG(widget), - HB_PROJECT_URL_WEBSITE); - gtk_widget_show (widget); -} - -#define HB_DOCS "https://handbrake.fr/docs/" - -G_MODULE_EXPORT void -guide_action_cb(GSimpleAction *action, GVariant *param, signal_user_data_t *ud) -{ - browse_url(HB_DOCS); -} - -G_MODULE_EXPORT void -hb_about_response_cb(GtkWidget *widget, gint response, signal_user_data_t *ud) -{ - gtk_widget_hide (widget); -} - -static void -update_queue_labels(signal_user_data_t *ud) -{ - GtkToolButton *button; - gboolean active; - gint pending; - const gchar *show_hide; - gchar *str; - - button = GTK_TOOL_BUTTON(GHB_WIDGET(ud->builder, "show_queue")); - active = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(button)); - pending = queue_pending_count(ud->queue); - - if (!active) - { - show_hide = _("Show\nQueue"); - } - else - { - show_hide = _("Hide\nQueue"); - } - if (pending > 0) - { - str = g_strdup_printf("%s (%d)", show_hide, pending); - } - else + if (status.live.state & GHB_STATE_WORKDONE) { - str = g_strdup_printf("%s", show_hide); + switch( status.live.error ) + { + case GHB_ERROR_NONE: + { + ghb_live_encode_done(ud, TRUE); + } break; + default: + { + ghb_live_encode_done(ud, FALSE); + } break; + } + ghb_clear_live_state(GHB_STATE_WORKDONE); } - gtk_tool_button_set_label(button, str); - g_free(str); -} - -static void -show_queue(signal_user_data_t *ud, gboolean show) -{ - GtkWidget *tab; - GtkStack *stack; - - stack = GTK_STACK(GHB_WIDGET(ud->builder, "QueueStack")); - if (show) - tab = GHB_WIDGET(ud->builder, "queue_tab"); - else - tab = GHB_WIDGET(ud->builder, "settings_tab"); - gtk_stack_set_visible_child(stack, tab); - update_queue_labels(ud); -} - -G_MODULE_EXPORT void -show_queue_action_cb(GSimpleAction *action, GVariant *value, - signal_user_data_t *ud) -{ - gboolean state = g_variant_get_boolean(value); - - g_simple_action_set_state(action, value); - show_queue(ud, state); } -static void -presets_window_set_visible(signal_user_data_t *ud, gboolean visible) +G_MODULE_EXPORT gboolean +ghb_timer_cb(gpointer data) { - GtkWidget * presets_window; - GtkWidget * hb_window; - int x, y; + signal_user_data_t *ud = (signal_user_data_t*)data; - hb_window = GHB_WIDGET(ud->builder, "hb_window"); - if (!gtk_widget_is_visible(hb_window)) + ghb_live_preview_progress(ud); + ghb_backend_events(ud); + if (update_default_destination) { - return; + const gchar *dest, *def_dest; + gchar *dest_dir; + dest = ghb_dict_get_string(ud->settings, "destination"); + dest_dir = g_path_get_dirname(dest); + def_dest = ghb_dict_get_string(ud->prefs, "destination_dir"); + if (strcmp(dest_dir, def_dest) != 0) + { + ghb_dict_set_string(ud->prefs, "destination_dir", dest_dir); + ghb_pref_save(ud->prefs, "destination_dir"); + } + g_free(dest_dir); + update_default_destination = FALSE; } - - int w, h; - w = ghb_dict_get_int(ud->prefs, "presets_window_width"); - h = ghb_dict_get_int(ud->prefs, "presets_window_height"); - - presets_window = GHB_WIDGET(ud->builder, "presets_window"); - if (w > 200 && h > 200) + if (update_preview) { - gtk_window_resize(GTK_WINDOW(presets_window), w, h); + g_debug("Updating preview\n"); + ghb_set_preview_image (ud); + update_preview = FALSE; } - gtk_widget_set_visible(presets_window, visible); - if (visible) + +#if !defined(_NO_UPDATE_CHECK) + if (!appcast_busy) { - gtk_window_get_position(GTK_WINDOW(hb_window), &x, &y); - x -= w + 10; - if (x < 0) + const gchar *updates; + updates = ghb_dict_get_string(ud->prefs, "check_updates"); + gint64 duration = 0; + if (strcmp(updates, "daily") == 0) + duration = 60 * 60 * 24; + else if (strcmp(updates, "weekly") == 0) + duration = 60 * 60 * 24 * 7; + else if (strcmp(updates, "monthly") == 0) + duration = 60 * 60 * 24 * 7; + + if (duration != 0) { - gtk_window_move(GTK_WINDOW(hb_window), w + 10, y); - x = 0; + gint64 last; + time_t tt; + + last = ghb_dict_get_int(ud->prefs, "last_update_check"); + time(&tt); + if (last + duration < tt) + { + ghb_dict_set_int(ud->prefs, + "last_update_check", tt); + ghb_pref_save(ud->prefs, "last_update_check"); + GHB_THREAD_NEW("Update Check", (GThreadFunc)ghb_check_update, ud); + } } - gtk_window_move(GTK_WINDOW(presets_window), x, y); } +#endif + return TRUE; } -G_MODULE_EXPORT void -show_presets_action_cb(GSimpleAction *action, GVariant *value, - signal_user_data_t *ud) +gboolean scroll_at_bottom(signal_user_data_t *ud, const char *scroll) { - gboolean state = g_variant_get_boolean(value); + GtkScrolledWindow *window; + GtkAdjustment *adj; + double val, upper, ps; - g_simple_action_set_state(action, value); - presets_window_set_visible(ud, state); + window = GTK_SCROLLED_WINDOW(GHB_WIDGET(ud->builder, scroll)); + adj = gtk_scrolled_window_get_vadjustment(window); + val = gtk_adjustment_get_value(adj); + upper = gtk_adjustment_get_upper(adj); + ps = gtk_adjustment_get_page_size(adj); + return val >= upper - ps; } -static void -chapter_refresh_list_row_ui( - GtkTreeModel *tm, - GtkTreeIter *ti, - GhbValue *chapter_list, - const hb_title_t *title, - int index) +G_MODULE_EXPORT gboolean +activity_scroll_to_bottom(signal_user_data_t *ud) { - const gchar *chapter; - gchar *s_duration, *s_start; - gint hh, mm, ss; - gint64 duration, start; + GtkScrolledWindow *window; + GtkAdjustment *adj; + double upper, ps; - // Update row with settings data - g_debug("Updating chapter row ui"); - chapter = ghb_dict_get_string(ghb_array_get(chapter_list, index), "Name"); - duration = ghb_get_chapter_duration(title, index) / 90000; - break_duration(duration, &hh, &mm, &ss); - s_duration = g_strdup_printf("%02d:%02d:%02d", hh, mm, ss); - start = ghb_get_chapter_start(title, index) / 90000; - break_duration(start, &hh, &mm, &ss); - s_start = g_strdup_printf("%02d:%02d:%02d", hh, mm, ss); - gtk_list_store_set(GTK_LIST_STORE(tm), ti, - 0, index+1, - 1, s_start, - 2, s_duration, - 3, chapter, - 4, TRUE, - -1); - g_free(s_duration); - g_free(s_start); + window = GTK_SCROLLED_WINDOW(GHB_WIDGET(ud->builder, "activity_scroll")); + adj = gtk_scrolled_window_get_vadjustment(window); + upper = gtk_adjustment_get_upper(adj); + ps = gtk_adjustment_get_page_size(adj); + gtk_adjustment_set_value(adj, upper - ps); + return FALSE; } -static void -ghb_clear_chapter_list_ui(GtkBuilder *builder) +G_MODULE_EXPORT gboolean +ghb_log_cb(GIOChannel *source, GIOCondition cond, gpointer data) { - GtkTreeView *tv; - GtkListStore *ts; - - tv = GTK_TREE_VIEW(GHB_WIDGET(builder, "chapters_list")); - ts = GTK_LIST_STORE(gtk_tree_view_get_model(tv)); - gtk_list_store_clear(ts); -} + gchar *text = NULL; + gsize length, outlength; + GtkTextView *textview; + GtkTextBuffer *buffer; + GtkTextIter iter; + GError *gerror = NULL; + GIOStatus status; -static void -chapter_refresh_list_ui(signal_user_data_t *ud) -{ - GhbValue *chapter_list; - gint ii, count, tm_count; - GtkTreeView *tv; - GtkTreeModel *tm; - GtkTreeIter ti; - int title_id, titleindex; - const hb_title_t *title; + signal_user_data_t *ud = (signal_user_data_t*)data; - tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "chapters_list")); - tm = gtk_tree_view_get_model(tv); + status = g_io_channel_read_line (source, &text, &length, NULL, &gerror); + // Trim nils from end of text, they cause g_io_channel_write_chars to + // fail with an assertion that aborts + while (length > 0 && text[length-1] == 0) + length--; + if (text != NULL && length > 0) + { + gboolean bottom = FALSE; + gchar *utf8_text; - tm_count = gtk_tree_model_iter_n_children(tm, NULL); + bottom = scroll_at_bottom(ud, "activity_scroll"); + textview = GTK_TEXT_VIEW(GHB_WIDGET (ud->builder, "activity_view")); + buffer = gtk_text_view_get_buffer (textview); + gtk_text_buffer_get_end_iter(buffer, &iter); + // Assume logging is in current locale + utf8_text = g_locale_to_utf8(text, -1, NULL, &length, NULL); + if (utf8_text != NULL) + { + gtk_text_buffer_insert(buffer, &iter, utf8_text, -1); + if (bottom) + { + static guint scroll_tok = 0; + GSource *source = NULL; + if (scroll_tok > 0) + source = g_main_context_find_source_by_id(NULL, scroll_tok); + if (source != NULL) + { + g_source_remove(scroll_tok); + } + scroll_tok = g_idle_add((GSourceFunc)activity_scroll_to_bottom, + ud); + } +#if defined(_WIN32) + gsize one = 1; + utf8_text[length-1] = '\r'; +#endif + g_io_channel_write_chars (ud->activity_log, utf8_text, + length, &outlength, NULL); +#if defined(_WIN32) + g_io_channel_write_chars (ud->activity_log, "\n", + one, &one, NULL); +#endif + g_io_channel_flush(ud->activity_log, NULL); + if (ud->job_activity_log) + { + g_io_channel_write_chars (ud->job_activity_log, utf8_text, + length, &outlength, NULL); +#if defined(_WIN32) + g_io_channel_write_chars (ud->activity_log, "\n", + one, &outlength, NULL); +#endif + g_io_channel_flush(ud->job_activity_log, NULL); + } + g_free(utf8_text); + } + } + if (text != NULL) + g_free(text); - title_id = ghb_dict_get_int(ud->settings, "title"); - title = ghb_lookup_title(title_id, &titleindex); - chapter_list = ghb_get_job_chapter_list(ud->settings); - count = ghb_array_len(chapter_list); - if (count != tm_count) + if (status != G_IO_STATUS_NORMAL) { - ghb_clear_chapter_list_ui(ud->builder); - for (ii = 0; ii < count; ii++) + // This should never happen, but if it does I would get into an + // infinite loop. Returning false removes this callback. + g_warning("Error while reading activity from pipe"); + if (gerror != NULL) { - gtk_list_store_append(GTK_LIST_STORE(tm), &ti); + g_warning("%s", gerror->message); + g_error_free (gerror); } + return FALSE; } - for (ii = 0; ii < count; ii++) - { - gtk_tree_model_iter_nth_child(tm, &ti, NULL, ii); - chapter_refresh_list_row_ui(tm, &ti, chapter_list, title, ii); - } + if (gerror != NULL) + g_error_free (gerror); + return TRUE; } -void -ghb_chapter_list_refresh_all(signal_user_data_t *ud) +static void +update_activity_labels(signal_user_data_t *ud, gboolean active) { - chapter_refresh_list_ui(ud); -} + GtkToolButton *button; -static gint chapter_edit_key = 0; + button = GTK_TOOL_BUTTON(GHB_WIDGET(ud->builder, "show_activity")); -G_MODULE_EXPORT gboolean -chapter_keypress_cb( - GhbCellRendererText *cell, - GdkEventKey *event, - signal_user_data_t *ud) -{ - chapter_edit_key = event->keyval; - return FALSE; + if (!active) + { + gtk_tool_button_set_label(button, "Show\nActivity"); + } + else + { + gtk_tool_button_set_label(button, "Hide\nActivity"); + } } G_MODULE_EXPORT void -chapter_edited_cb( - GhbCellRendererText *cell, - gchar *path, - gchar *text, - signal_user_data_t *ud) +show_activity_action_cb(GSimpleAction *action, GVariant *value, + signal_user_data_t *ud) { - GtkTreePath *treepath; - GtkListStore *store; - GtkTreeView *treeview; - GtkTreeIter iter; - gint index; - gint *pi; - gint row; - - g_debug("chapter_edited_cb ()"); - g_debug("path (%s)", path); - g_debug("text (%s)", text); - treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "chapters_list")); - store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview)); - treepath = gtk_tree_path_new_from_string (path); - pi = gtk_tree_path_get_indices(treepath); - row = pi[0]; - gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, treepath); - gtk_list_store_set(store, &iter, - 3, text, - 4, TRUE, - -1); - gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 0, &index, -1); - - const GhbValue *chapters; - GhbValue *chapter; - - chapters = ghb_get_job_chapter_list(ud->settings); - chapter = ghb_array_get(chapters, index-1); - ghb_dict_set_string(chapter, "Name", text); - if ((chapter_edit_key == GDK_KEY_Return || chapter_edit_key == GDK_KEY_Down) && - gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter)) - { - GtkTreeViewColumn *column; + GtkWidget * activity_window; + gboolean state = g_variant_get_boolean(value); - gtk_tree_path_next(treepath); - // When a cell has been edited, I want to advance to the - // next cell and start editing it automatically. - // Unfortunately, we may not be in a state here where - // editing is allowed. This happens when the user selects - // a new cell with the mouse instead of just hitting enter. - // Some kind of Gtk quirk. widget_editable==NULL assertion. - // Editing is enabled again once the selection event has been - // processed. So I'm queueing up a callback to be called - // when things go idle. There, I will advance to the next - // cell and initiate editing. - // - // Now, you might be asking why I don't catch the keypress - // event and determine what action to take based on that. - // The Gtk developers in their infinite wisdom have made the - // actual GtkEdit widget being used a private member of - // GtkCellRendererText, so it can not be accessed to hang a - // signal handler off of. And they also do not propagate the - // keypress signals in any other way. So that information is lost. - //g_idle_add((GSourceFunc)next_cell, ud); - // - // Keeping the above comment for posterity. - // I got industrious and made my own CellTextRendererText that - // passes on the key-press-event. So now I have much better - // control of this. - column = gtk_tree_view_get_column(treeview, 3); - gtk_tree_view_set_cursor(treeview, treepath, column, TRUE); - } - else if (chapter_edit_key == GDK_KEY_Up && row > 0) - { - GtkTreeViewColumn *column; - gtk_tree_path_prev(treepath); - column = gtk_tree_view_get_column(treeview, 3); - gtk_tree_view_set_cursor(treeview, treepath, column, TRUE); - } - gtk_tree_path_free (treepath); + g_simple_action_set_state(action, value); + activity_window = GHB_WIDGET(ud->builder, "activity_window"); + gtk_widget_set_visible(activity_window, state); + update_activity_labels(ud, state); } -void -debug_log_handler(const gchar *domain, GLogLevelFlags flags, const gchar *msg, gpointer data) +G_MODULE_EXPORT gboolean +presets_window_delete_cb(GtkWidget *xwidget, GdkEvent *event, signal_user_data_t *ud) { - signal_user_data_t *ud = (signal_user_data_t*)data; + GSimpleAction * action; + GVariant * state = g_variant_new_boolean(FALSE); - if (ud->debug) - { - printf("%s: %s\n", domain, msg); - } + action = G_SIMPLE_ACTION(g_action_map_lookup_action( + G_ACTION_MAP(ud->app), "show-presets")); + g_action_change_state(G_ACTION(action), state); + return TRUE; } -void -warn_log_handler(const gchar *domain, GLogLevelFlags flags, const gchar *msg, gpointer data) +G_MODULE_EXPORT gboolean +activity_window_delete_cb(GtkWidget *xwidget, GdkEvent *event, signal_user_data_t *ud) { - printf("%s: %s\n", domain, msg); + gtk_widget_set_visible(xwidget, FALSE); + GtkWidget *widget = GHB_WIDGET (ud->builder, "show_activity"); + gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(widget), FALSE); + return TRUE; } void -ghb_hbfd(signal_user_data_t *ud, gboolean hbfd) +ghb_log(gchar *log, ...) { - GtkWidget *widget; - g_debug("ghb_hbfd"); - widget = GHB_WIDGET(ud->builder, "queue_pause"); - gtk_widget_set_visible(widget, !hbfd); - widget = GHB_WIDGET(ud->builder, "queue_add"); - gtk_widget_set_visible(widget, !hbfd); - widget = GHB_WIDGET(ud->builder, "show_queue"); - gtk_widget_set_visible(widget, !hbfd); - widget = GHB_WIDGET(ud->builder, "show_activity"); - gtk_widget_set_visible(widget, !hbfd); - - widget = GHB_WIDGET(ud->builder, "container_box"); - gtk_widget_set_visible(widget, !hbfd); - widget = GHB_WIDGET(ud->builder, "SettingsStackSwitcher"); - gtk_widget_set_visible(widget, !hbfd); - widget = GHB_WIDGET(ud->builder, "SettingsStack"); - gtk_widget_set_visible(widget, !hbfd); - widget = GHB_WIDGET(ud->builder, "presets_save"); - gtk_widget_set_visible(widget, !hbfd); - widget = GHB_WIDGET(ud->builder, "presets_remove"); - gtk_widget_set_visible(widget, !hbfd); - widget = GHB_WIDGET (ud->builder, "hb_window"); - gtk_window_resize(GTK_WINDOW(widget), 16, 16); + va_list args; + time_t _now; + struct tm *now; + gchar fmt[362]; + _now = time(NULL); + now = localtime( &_now ); + snprintf(fmt, 362, "[%02d:%02d:%02d] gtkgui: %s\n", + now->tm_hour, now->tm_min, now->tm_sec, log); + va_start(args, log); + vfprintf(stderr, fmt, args); + va_end(args); } -G_MODULE_EXPORT void -hbfd_action_cb(GSimpleAction *action, GVariant *value, signal_user_data_t *ud) +static void +browse_url(const gchar *url) { - gboolean state = g_variant_get_boolean(value); +#if defined(_WIN32) + ShellExecute(NULL, "open", url, NULL, NULL, SW_SHOWNORMAL); +#else + gboolean result; + char *argv[] = + {"xdg-open",NULL,NULL,NULL}; + argv[1] = (gchar*)url; + result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, + NULL, NULL, NULL); + if (result) return; - g_simple_action_set_state(action, value); - ghb_dict_set(ud->prefs, "hbfd", ghb_boolean_value(state)); - ghb_hbfd(ud, state); - ghb_pref_save(ud->prefs, "hbfd"); -} + argv[0] = "gnome-open"; + result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, + NULL, NULL, NULL); + if (result) return; -G_MODULE_EXPORT void -advanced_video_changed_cb(GtkWidget *widget, signal_user_data_t *ud) -{ - g_debug("advanced_video_changed_cb"); - ghb_widget_to_setting(ud->prefs, widget); - const gchar *name = ghb_get_setting_key(widget); - ghb_pref_set(ud->prefs, name); - ghb_show_hide_advanced_video(ud); + argv[0] = "kfmclient"; + argv[1] = "exec"; + argv[2] = (gchar*)url; + result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, + NULL, NULL, NULL); + if (result) return; + + argv[0] = "firefox"; + argv[1] = (gchar*)url; + argv[2] = NULL; + result = g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, + NULL, NULL, NULL); +#endif } G_MODULE_EXPORT void -pref_changed_cb(GtkWidget *widget, signal_user_data_t *ud) +about_action_cb(GSimpleAction *action, GVariant *param, signal_user_data_t *ud) { - ghb_widget_to_setting (ud->prefs, widget); + GtkWidget *widget = GHB_WIDGET (ud->builder, "hb_about"); + gchar *ver; - ghb_check_dependency(ud, widget, NULL); - const gchar *name = ghb_get_setting_key(widget); - ghb_pref_set(ud->prefs, name); + ver = g_strdup_printf("%s (%s)", HB_PROJECT_VERSION, HB_PROJECT_BUILD_ARCH); + gtk_about_dialog_set_version(GTK_ABOUT_DIALOG(widget), ver); + g_free(ver); + gtk_about_dialog_set_website(GTK_ABOUT_DIALOG(widget), + HB_PROJECT_URL_WEBSITE); + gtk_about_dialog_set_website_label(GTK_ABOUT_DIALOG(widget), + HB_PROJECT_URL_WEBSITE); + gtk_widget_show (widget); } +#define HB_DOCS "https://handbrake.fr/docs/" + G_MODULE_EXPORT void -log_level_changed_cb(GtkWidget *widget, signal_user_data_t *ud) +guide_action_cb(GSimpleAction *action, GVariant *param, signal_user_data_t *ud) { - pref_changed_cb(widget, ud); - int level = ghb_dict_get_int(ud->prefs, "LoggingLevel"); - ghb_log_level_set(level); + browse_url(HB_DOCS); } G_MODULE_EXPORT void -use_m4v_changed_cb(GtkWidget *widget, signal_user_data_t *ud) +hb_about_response_cb(GtkWidget *widget, gint response, signal_user_data_t *ud) { - g_debug("use_m4v_changed_cb"); - ghb_widget_to_setting (ud->prefs, widget); - ghb_check_dependency(ud, widget, NULL); - const gchar *name = ghb_get_setting_key(widget); - ghb_pref_set(ud->prefs, name); - ghb_update_destination_extension(ud); + gtk_widget_hide (widget); } -G_MODULE_EXPORT void -vqual_granularity_changed_cb(GtkWidget *widget, signal_user_data_t *ud) +static void +update_queue_labels(signal_user_data_t *ud) { - g_debug("vqual_granularity_changed_cb"); - ghb_widget_to_setting (ud->prefs, widget); - ghb_check_dependency(ud, widget, NULL); - - const gchar *name = ghb_get_setting_key(widget); - ghb_pref_set(ud->prefs, name); + GtkToolButton *button; + gboolean active; + gint pending; + const gchar *show_hide; + gchar *str; - float val, vqmin, vqmax, step, page; - int inverted, digits; + button = GTK_TOOL_BUTTON(GHB_WIDGET(ud->builder, "show_queue")); + active = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(button)); + pending = queue_pending_count(ud->queue); - ghb_vquality_range(ud, &vqmin, &vqmax, &step, &page, &digits, &inverted); - val = ghb_dict_get_double(ud->settings, "VideoQualitySlider"); - ghb_scale_configure(ud, "VideoQualitySlider", val, vqmin, vqmax, - step, page, digits, inverted); + if (!active) + { + show_hide = _("Show\nQueue"); + } + else + { + show_hide = _("Hide\nQueue"); + } + if (pending > 0) + { + str = g_strdup_printf("%s (%d)", show_hide, pending); + } + else + { + str = g_strdup_printf("%s", show_hide); + } + gtk_tool_button_set_label(button, str); + g_free(str); } -G_MODULE_EXPORT void -tweaks_changed_cb(GtkWidget *widget, signal_user_data_t *ud) +static void +show_queue(signal_user_data_t *ud, gboolean show) { - g_debug("tweaks_changed_cb"); - ghb_widget_to_setting (ud->prefs, widget); - const gchar *name = ghb_get_setting_key(widget); - ghb_pref_set(ud->prefs, name); + GtkWidget *tab; + GtkStack *stack; + + stack = GTK_STACK(GHB_WIDGET(ud->builder, "QueueStack")); + if (show) + tab = GHB_WIDGET(ud->builder, "queue_tab"); + else + tab = GHB_WIDGET(ud->builder, "settings_tab"); + gtk_stack_set_visible_child(stack, tab); + update_queue_labels(ud); } G_MODULE_EXPORT void -hbfd_feature_changed_cb(GtkWidget *widget, signal_user_data_t *ud) +show_queue_action_cb(GSimpleAction *action, GVariant *value, + signal_user_data_t *ud) { - ghb_widget_to_setting (ud->prefs, widget); - const gchar *name = ghb_get_setting_key(widget); - ghb_pref_set(ud->prefs, name); + gboolean state = g_variant_get_boolean(value); - gboolean hbfd = ghb_dict_get_bool(ud->prefs, "hbfd_feature"); - if (hbfd) - { - const GhbValue *val; - val = ghb_dict_get_value(ud->prefs, "hbfd"); - ghb_ui_settings_update(ud, ud->prefs, "hbfd", val); - } - widget = GHB_WIDGET(ud->builder, "hbfd"); - gtk_widget_set_visible(widget, hbfd); + g_simple_action_set_state(action, value); + show_queue(ud, state); } -gboolean -ghb_file_menu_add_dvd(signal_user_data_t *ud) +static void +presets_window_set_visible(signal_user_data_t *ud, gboolean visible) { - GList *link, *drives; - static GList *dvd_items = NULL; - - g_debug("ghb_file_menu_add_dvd()"); - GtkMenu *menu = GTK_MENU(GHB_WIDGET(ud->builder, "file_submenu")); + GtkWidget * presets_window; + GtkWidget * hb_window; + int x, y; - // Clear previous dvd items from list - link = dvd_items; - while (link != NULL) + hb_window = GHB_WIDGET(ud->builder, "hb_window"); + if (!gtk_widget_is_visible(hb_window)) { - GtkWidget * widget = GTK_WIDGET(link->data); - // widget_destroy automatically removes widget from container. - gtk_widget_destroy(widget); - link = link->next; + return; } - g_list_free(dvd_items); - dvd_items = NULL; - - int pos = 5; - link = drives = dvd_device_list(); - if (drives != NULL) - { - GtkWidget *widget = gtk_separator_menu_item_new(); - dvd_items = g_list_append(dvd_items, (gpointer)widget); - gtk_menu_shell_insert(GTK_MENU_SHELL(menu), widget, pos++); - gtk_widget_set_visible(widget, TRUE); + int w, h; + w = ghb_dict_get_int(ud->prefs, "presets_window_width"); + h = ghb_dict_get_int(ud->prefs, "presets_window_height"); - while (link != NULL) + presets_window = GHB_WIDGET(ud->builder, "presets_window"); + if (w > 200 && h > 200) + { + gtk_window_resize(GTK_WINDOW(presets_window), w, h); + } + gtk_widget_set_visible(presets_window, visible); + if (visible) + { + gtk_window_get_position(GTK_WINDOW(hb_window), &x, &y); + x -= w + 10; + if (x < 0) { - GtkWidget *widget; - gchar *drive = get_dvd_device_name(link->data); - gchar *name = get_dvd_volume_name(link->data); - - widget = gtk_menu_item_new_with_label(name); - gtk_buildable_set_name(GTK_BUILDABLE(widget), drive); - gtk_widget_set_tooltip_text(widget, _("Scan this DVD source")); - - dvd_items = g_list_append(dvd_items, (gpointer)widget); - gtk_menu_shell_insert(GTK_MENU_SHELL(menu), widget, pos++); - - gtk_widget_set_visible(widget, TRUE); - - // Connect signal to action (menu item) - g_signal_connect(widget, "activate", - (GCallback)dvd_source_activate_cb, ud); - g_free(name); - g_free(drive); - free_drive(link->data); - link = link->next; + gtk_window_move(GTK_WINDOW(hb_window), w + 10, y); + x = 0; } - - g_list_free(drives); + gtk_window_move(GTK_WINDOW(presets_window), x, y); } - - return FALSE; } -gboolean ghb_is_cd(GDrive *gd); - -static GList* -dvd_device_list() +G_MODULE_EXPORT void +show_presets_action_cb(GSimpleAction *action, GVariant *value, + signal_user_data_t *ud) { - GList *dvd_devices = NULL; - -#if defined(_WIN32) - gint ii, drives; - gchar drive[5]; - - strcpy(drive, "A:" G_DIR_SEPARATOR_S); - drives = GetLogicalDrives(); - for (ii = 0; ii < 26; ii++) - { - if (drives & 0x01) - { - guint dtype; - - drive[0] = 'A' + ii; - dtype = GetDriveType(drive); - if (dtype == DRIVE_CDROM) - { - dvd_devices = g_list_append(dvd_devices, - (gpointer)g_strdup(drive)); - } - } - drives >>= 1; - } -#else - GVolumeMonitor *gvm; - GList *drives, *link; + gboolean state = g_variant_get_boolean(value); - gvm = g_volume_monitor_get (); - drives = g_volume_monitor_get_connected_drives (gvm); - link = drives; - while (link != NULL) - { - GDrive *gd; + g_simple_action_set_state(action, value); + presets_window_set_visible(ud, state); +} - gd = (GDrive*)link->data; - if (ghb_is_cd(gd)) - { - dvd_devices = g_list_append(dvd_devices, gd); - } - else - g_object_unref (gd); - link = link->next; - } - g_list_free(drives); -#endif +static void +chapter_refresh_list_row_ui( + GtkTreeModel *tm, + GtkTreeIter *ti, + GhbValue *chapter_list, + const hb_title_t *title, + int index) +{ + const gchar *chapter; + gchar *s_duration, *s_start; + gint hh, mm, ss; + gint64 duration, start; - return dvd_devices; + // Update row with settings data + g_debug("Updating chapter row ui"); + chapter = ghb_dict_get_string(ghb_array_get(chapter_list, index), "Name"); + duration = ghb_get_chapter_duration(title, index) / 90000; + break_duration(duration, &hh, &mm, &ss); + s_duration = g_strdup_printf("%02d:%02d:%02d", hh, mm, ss); + start = ghb_get_chapter_start(title, index) / 90000; + break_duration(start, &hh, &mm, &ss); + s_start = g_strdup_printf("%02d:%02d:%02d", hh, mm, ss); + gtk_list_store_set(GTK_LIST_STORE(tm), ti, + 0, index+1, + 1, s_start, + 2, s_duration, + 3, chapter, + 4, TRUE, + -1); + g_free(s_duration); + g_free(s_start); } -#if defined(__linux__) && defined(_HAVE_GUDEV) -static GUdevClient *udev_ctx = NULL; -#endif +static void +ghb_clear_chapter_list_ui(GtkBuilder *builder) +{ + GtkTreeView *tv; + GtkListStore *ts; -gboolean -ghb_is_cd(GDrive *gd) + tv = GTK_TREE_VIEW(GHB_WIDGET(builder, "chapters_list")); + ts = GTK_LIST_STORE(gtk_tree_view_get_model(tv)); + gtk_list_store_clear(ts); +} + +static void +chapter_refresh_list_ui(signal_user_data_t *ud) { -#if defined(__linux__) && defined(_HAVE_GUDEV) - gchar *device; - GUdevDevice *udd; + GhbValue *chapter_list; + gint ii, count, tm_count; + GtkTreeView *tv; + GtkTreeModel *tm; + GtkTreeIter ti; + int title_id, titleindex; + const hb_title_t *title; - if (udev_ctx == NULL) - return FALSE; + tv = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "chapters_list")); + tm = gtk_tree_view_get_model(tv); - device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE); - if (device == NULL) - return FALSE; + tm_count = gtk_tree_model_iter_n_children(tm, NULL); - udd = g_udev_client_query_by_device_file(udev_ctx, device); - if (udd == NULL) + title_id = ghb_dict_get_int(ud->settings, "title"); + title = ghb_lookup_title(title_id, &titleindex); + chapter_list = ghb_get_job_chapter_list(ud->settings); + count = ghb_array_len(chapter_list); + if (count != tm_count) { - g_message("udev: Failed to lookup device %s", device); - g_free(device); - return FALSE; + ghb_clear_chapter_list_ui(ud->builder); + for (ii = 0; ii < count; ii++) + { + gtk_list_store_append(GTK_LIST_STORE(tm), &ti); + } + } + for (ii = 0; ii < count; ii++) + { + gtk_tree_model_iter_nth_child(tm, &ti, NULL, ii); + chapter_refresh_list_row_ui(tm, &ti, chapter_list, title, ii); } - g_free(device); - - gint val; - val = g_udev_device_get_property_as_int(udd, "ID_CDROM_DVD"); - g_object_unref(udd); - if (val == 1) - return TRUE; - - return FALSE; -#else - return FALSE; -#endif } void -ghb_udev_init() +ghb_chapter_list_refresh_all(signal_user_data_t *ud) { -#if defined(__linux__) && defined(_HAVE_GUDEV) - udev_ctx = g_udev_client_new(NULL); -#endif + chapter_refresh_list_ui(ud); } -#if defined(_WIN32) -static void -handle_media_change(const gchar *device, gboolean insert, signal_user_data_t *ud) +static gint chapter_edit_key = 0; + +G_MODULE_EXPORT gboolean +chapter_keypress_cb( + GhbCellRendererText *cell, + GdkEventKey *event, + signal_user_data_t *ud) { - guint dtype; - static gint ins_count = 0; - static gint rem_count = 0; + chapter_edit_key = event->keyval; + return FALSE; +} - // The media change event in windows bounces around a bit - // so I debounce it here - // DVD insertion detected. Scan it. - dtype = GetDriveType(device); - if (dtype != DRIVE_CDROM) - return; - if (insert) +G_MODULE_EXPORT void +chapter_edited_cb( + GhbCellRendererText *cell, + gchar *path, + gchar *text, + signal_user_data_t *ud) +{ + GtkTreePath *treepath; + GtkListStore *store; + GtkTreeView *treeview; + GtkTreeIter iter; + gint index; + gint *pi; + gint row; + + g_debug("chapter_edited_cb ()"); + g_debug("path (%s)", path); + g_debug("text (%s)", text); + treeview = GTK_TREE_VIEW(GHB_WIDGET(ud->builder, "chapters_list")); + store = GTK_LIST_STORE(gtk_tree_view_get_model(treeview)); + treepath = gtk_tree_path_new_from_string (path); + pi = gtk_tree_path_get_indices(treepath); + row = pi[0]; + gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, treepath); + gtk_list_store_set(store, &iter, + 3, text, + 4, TRUE, + -1); + gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, 0, &index, -1); + + const GhbValue *chapters; + GhbValue *chapter; + + chapters = ghb_get_job_chapter_list(ud->settings); + chapter = ghb_array_get(chapters, index-1); + ghb_dict_set_string(chapter, "Name", text); + if ((chapter_edit_key == GDK_KEY_Return || chapter_edit_key == GDK_KEY_Down) && + gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter)) { - rem_count = 0; - ins_count++; - if (ins_count == 2) - { - GHB_THREAD_NEW("Cache Volume Names", - (GThreadFunc)ghb_cache_volnames, ud); - if (ghb_dict_get_bool(ud->prefs, "AutoScan") && - ud->current_dvd_device != NULL && - strcmp(device, ud->current_dvd_device) == 0) - { - show_scan_progress(ud); - update_source_label(ud, device); - gint preview_count; - preview_count = ghb_dict_get_int(ud->prefs, "preview_count"); - ghb_dict_set_string(ud->globals, "scan_source", device); - start_scan(ud, device, 0, preview_count); - } - } + GtkTreeViewColumn *column; + + gtk_tree_path_next(treepath); + // When a cell has been edited, I want to advance to the + // next cell and start editing it automatically. + // Unfortunately, we may not be in a state here where + // editing is allowed. This happens when the user selects + // a new cell with the mouse instead of just hitting enter. + // Some kind of Gtk quirk. widget_editable==NULL assertion. + // Editing is enabled again once the selection event has been + // processed. So I'm queueing up a callback to be called + // when things go idle. There, I will advance to the next + // cell and initiate editing. + // + // Now, you might be asking why I don't catch the keypress + // event and determine what action to take based on that. + // The Gtk developers in their infinite wisdom have made the + // actual GtkEdit widget being used a private member of + // GtkCellRendererText, so it can not be accessed to hang a + // signal handler off of. And they also do not propagate the + // keypress signals in any other way. So that information is lost. + //g_idle_add((GSourceFunc)next_cell, ud); + // + // Keeping the above comment for posterity. + // I got industrious and made my own CellTextRendererText that + // passes on the key-press-event. So now I have much better + // control of this. + column = gtk_tree_view_get_column(treeview, 3); + gtk_tree_view_set_cursor(treeview, treepath, column, TRUE); } - else + else if (chapter_edit_key == GDK_KEY_Up && row > 0) { - ins_count = 0; - rem_count++; - if (rem_count == 2) - { - GHB_THREAD_NEW("Cache Volume Names", - (GThreadFunc)ghb_cache_volnames, ud); - if (ud->current_dvd_device != NULL && - strcmp(device, ud->current_dvd_device) == 0) - { - ghb_hb_cleanup(TRUE); - prune_logs(ud); - ghb_dict_set_string(ud->globals, "scan_source", "/dev/null"); - start_scan(ud, "/dev/null", 0, 1); - } - } + GtkTreeViewColumn *column; + gtk_tree_path_prev(treepath); + column = gtk_tree_view_get_column(treeview, 3); + gtk_tree_view_set_cursor(treeview, treepath, column, TRUE); } + gtk_tree_path_free (treepath); } -static gchar -FindDriveFromMask(ULONG unitmask) +void +debug_log_handler(const gchar *domain, GLogLevelFlags flags, const gchar *msg, gpointer data) { - gchar cc; - for (cc = 0; cc < 26; cc++) + signal_user_data_t *ud = (signal_user_data_t*)data; + + if (ud->debug) { - if (unitmask & 0x01) - return 'A' + cc; - unitmask >>= 1; + printf("%s: %s\n", domain, msg); } - return 0; } void -wm_drive_changed(MSG *msg, signal_user_data_t *ud) +warn_log_handler(const gchar *domain, GLogLevelFlags flags, const gchar *msg, gpointer data) { - PDEV_BROADCAST_HDR bch = (PDEV_BROADCAST_HDR)msg->lParam; - gchar drive[4]; - - g_strlcpy(drive, "A:" G_DIR_SEPARATOR_S, 4); - switch (msg->wParam) - { - case DBT_DEVICEARRIVAL: - { - if (bch->dbch_devicetype == DBT_DEVTYP_VOLUME) - { - PDEV_BROADCAST_VOLUME bcv = (PDEV_BROADCAST_VOLUME)bch; - - if (bcv->dbcv_flags & DBTF_MEDIA) - { - drive[0] = FindDriveFromMask(bcv->dbcv_unitmask); - handle_media_change(drive, TRUE, ud); - } - } - } break; + printf("%s: %s\n", domain, msg); +} - case DBT_DEVICEREMOVECOMPLETE: - { - if (bch->dbch_devicetype == DBT_DEVTYP_VOLUME) - { - PDEV_BROADCAST_VOLUME bcv = (PDEV_BROADCAST_VOLUME)bch; +void +ghb_hbfd(signal_user_data_t *ud, gboolean hbfd) +{ + GtkWidget *widget; + g_debug("ghb_hbfd"); + widget = GHB_WIDGET(ud->builder, "queue_pause"); + gtk_widget_set_visible(widget, !hbfd); + widget = GHB_WIDGET(ud->builder, "queue_add"); + gtk_widget_set_visible(widget, !hbfd); + widget = GHB_WIDGET(ud->builder, "show_queue"); + gtk_widget_set_visible(widget, !hbfd); + widget = GHB_WIDGET(ud->builder, "show_activity"); + gtk_widget_set_visible(widget, !hbfd); - if (bcv->dbcv_flags & DBTF_MEDIA) - { - drive[0] = FindDriveFromMask(bcv->dbcv_unitmask); - handle_media_change(drive, FALSE, ud); - } - } - } break; - default: ; - } -} + widget = GHB_WIDGET(ud->builder, "container_box"); + gtk_widget_set_visible(widget, !hbfd); + widget = GHB_WIDGET(ud->builder, "SettingsStackSwitcher"); + gtk_widget_set_visible(widget, !hbfd); + widget = GHB_WIDGET(ud->builder, "SettingsStack"); + gtk_widget_set_visible(widget, !hbfd); + widget = GHB_WIDGET(ud->builder, "presets_save"); + gtk_widget_set_visible(widget, !hbfd); + widget = GHB_WIDGET(ud->builder, "presets_remove"); + gtk_widget_set_visible(widget, !hbfd); + widget = GHB_WIDGET (ud->builder, "hb_window"); + gtk_window_resize(GTK_WINDOW(widget), 16, 16); -#else +} G_MODULE_EXPORT void -drive_changed_cb(GVolumeMonitor *gvm, GDrive *gd, signal_user_data_t *ud) +hbfd_action_cb(GSimpleAction *action, GVariant *value, signal_user_data_t *ud) { - gchar *device; - gint state; - - g_debug("drive_changed_cb()"); - GHB_THREAD_NEW("Cache Volume Names", (GThreadFunc)ghb_cache_volnames, ud); + gboolean state = g_variant_get_boolean(value); - state = ghb_get_scan_state(); - device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE); - if (ud->current_dvd_device == NULL || - strcmp(device, ud->current_dvd_device) != 0 || - state != GHB_STATE_IDLE ) - { - return; - } - if (g_drive_has_media(gd)) - { - if (ghb_dict_get_bool(ud->prefs, "AutoScan")) - { - show_scan_progress(ud); - update_source_label(ud, device); - gint preview_count; - preview_count = ghb_dict_get_int(ud->prefs, "preview_count"); - ghb_dict_set_string(ud->globals, "scan_source", device); - start_scan(ud, device, 0, preview_count); - } - } - else - { - ghb_hb_cleanup(TRUE); - prune_logs(ud); - ghb_dict_set_string(ud->globals, "scan_source", "/dev/null"); - start_scan(ud, "/dev/null", 0, 1); - } + g_simple_action_set_state(action, value); + ghb_dict_set(ud->prefs, "hbfd", ghb_boolean_value(state)); + ghb_hbfd(ud, state); + ghb_pref_save(ud->prefs, "hbfd"); } -#endif - -#if !defined(_WIN32) -#define GPM_DBUS_PM_SERVICE "org.freedesktop.PowerManagement" -#define GPM_DBUS_PM_PATH "/org/freedesktop/PowerManagement" -#define GPM_DBUS_PM_INTERFACE "org.freedesktop.PowerManagement" -#define GPM_DBUS_INHIBIT_PATH "/org/freedesktop/PowerManagement/Inhibit" -#define GPM_DBUS_INHIBIT_INTERFACE "org.freedesktop.PowerManagement.Inhibit" -static gboolean gpm_inhibited = FALSE; -static guint gpm_cookie = -1; -#endif -static gboolean -ghb_can_suspend_gpm() +G_MODULE_EXPORT void +advanced_video_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { - gboolean can_suspend = FALSE; -#if !defined(_WIN32) - DBusGConnection *conn; - DBusGProxy *proxy; - GError *error = NULL; - gboolean res; + g_debug("advanced_video_changed_cb"); + ghb_widget_to_setting(ud->prefs, widget); + const gchar *name = ghb_get_setting_key(widget); + ghb_pref_set(ud->prefs, name); + ghb_show_hide_advanced_video(ud); +} +G_MODULE_EXPORT void +pref_changed_cb(GtkWidget *widget, signal_user_data_t *ud) +{ + ghb_widget_to_setting (ud->prefs, widget); - g_debug("ghb_can_suspend_gpm()"); - conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); - if (error != NULL) - { - g_warning("DBUS cannot connect: %s", error->message); - g_error_free(error); - return FALSE; - } - proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE, - GPM_DBUS_PM_PATH, GPM_DBUS_PM_INTERFACE); - if (proxy == NULL) - { - g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE); - dbus_g_connection_unref(conn); - return FALSE; - } - res = dbus_g_proxy_call(proxy, "CanSuspend", &error, - G_TYPE_INVALID, - G_TYPE_BOOLEAN, &can_suspend, - G_TYPE_INVALID); - if (!res) - { - if (error != NULL) - { - g_warning("CanSuspend failed: %s", error->message); - g_error_free(error); - } - else - g_warning("CanSuspend failed"); - // Try to shutdown anyway - can_suspend = TRUE; - } - g_object_unref(G_OBJECT(proxy)); - dbus_g_connection_unref(conn); -#endif - return can_suspend; + ghb_check_dependency(ud, widget, NULL); + const gchar *name = ghb_get_setting_key(widget); + ghb_pref_set(ud->prefs, name); } -static void -ghb_suspend_gpm() +G_MODULE_EXPORT void +log_level_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { -#if !defined(_WIN32) - DBusGConnection *conn; - DBusGProxy *proxy; - GError *error = NULL; - gboolean res; - + pref_changed_cb(widget, ud); + int level = ghb_dict_get_int(ud->prefs, "LoggingLevel"); + ghb_log_level_set(level); +} - g_debug("ghb_suspend_gpm()"); - conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); - if (error != NULL) - { - g_warning("DBUS cannot connect: %s", error->message); - g_error_free(error); - return; - } - proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE, - GPM_DBUS_PM_PATH, GPM_DBUS_PM_INTERFACE); - if (proxy == NULL) - { - g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE); - dbus_g_connection_unref(conn); - return; - } - res = dbus_g_proxy_call(proxy, "Suspend", &error, - G_TYPE_INVALID, - G_TYPE_INVALID); - if (!res) - { - if (error != NULL) - { - g_warning("Suspend failed: %s", error->message); - g_error_free(error); - } - else - g_warning("Suspend failed"); - } - g_object_unref(G_OBJECT(proxy)); - dbus_g_connection_unref(conn); -#endif +G_MODULE_EXPORT void +use_m4v_changed_cb(GtkWidget *widget, signal_user_data_t *ud) +{ + g_debug("use_m4v_changed_cb"); + ghb_widget_to_setting (ud->prefs, widget); + ghb_check_dependency(ud, widget, NULL); + const gchar *name = ghb_get_setting_key(widget); + ghb_pref_set(ud->prefs, name); + ghb_update_destination_extension(ud); } -#if !defined(_WIN32) -static gboolean -ghb_can_shutdown_gpm() +G_MODULE_EXPORT void +vqual_granularity_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { - gboolean can_shutdown = FALSE; - DBusGConnection *conn; - DBusGProxy *proxy; - GError *error = NULL; - gboolean res; + g_debug("vqual_granularity_changed_cb"); + ghb_widget_to_setting (ud->prefs, widget); + ghb_check_dependency(ud, widget, NULL); + const gchar *name = ghb_get_setting_key(widget); + ghb_pref_set(ud->prefs, name); - g_debug("ghb_can_shutdown_gpm()"); - conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); - if (error != NULL) - { - g_warning("DBUS cannot connect: %s", error->message); - g_error_free(error); - return FALSE; - } - proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE, - GPM_DBUS_PM_PATH, GPM_DBUS_PM_INTERFACE); - if (proxy == NULL) - { - g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE); - dbus_g_connection_unref(conn); - return FALSE; - } - res = dbus_g_proxy_call(proxy, "CanShutdown", &error, - G_TYPE_INVALID, - G_TYPE_BOOLEAN, &can_shutdown, - G_TYPE_INVALID); - if (!res) - { - if (error != NULL) - { - g_warning("CanShutdown failed: %s", error->message); - g_error_free(error); - } - else - g_warning("CanShutdown failed"); - // Try to shutdown anyway - can_shutdown = TRUE; - } - g_object_unref(G_OBJECT(proxy)); - dbus_g_connection_unref(conn); - return can_shutdown; + float val, vqmin, vqmax, step, page; + int inverted, digits; + + ghb_vquality_range(ud, &vqmin, &vqmax, &step, &page, &digits, &inverted); + val = ghb_dict_get_double(ud->settings, "VideoQualitySlider"); + ghb_scale_configure(ud, "VideoQualitySlider", val, vqmin, vqmax, + step, page, digits, inverted); } -#endif -#if !defined(_WIN32) -static void -ghb_shutdown_gpm() +G_MODULE_EXPORT void +tweaks_changed_cb(GtkWidget *widget, signal_user_data_t *ud) { - DBusGConnection *conn; - DBusGProxy *proxy; - GError *error = NULL; - gboolean res; + g_debug("tweaks_changed_cb"); + ghb_widget_to_setting (ud->prefs, widget); + const gchar *name = ghb_get_setting_key(widget); + ghb_pref_set(ud->prefs, name); +} +G_MODULE_EXPORT void +hbfd_feature_changed_cb(GtkWidget *widget, signal_user_data_t *ud) +{ + ghb_widget_to_setting (ud->prefs, widget); + const gchar *name = ghb_get_setting_key(widget); + ghb_pref_set(ud->prefs, name); - g_debug("ghb_shutdown_gpm()"); - conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); - if (error != NULL) - { - g_warning("DBUS cannot connect: %s", error->message); - g_error_free(error); - return; - } - proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE, - GPM_DBUS_PM_PATH, GPM_DBUS_PM_INTERFACE); - if (proxy == NULL) - { - g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE); - dbus_g_connection_unref(conn); - return; - } - res = dbus_g_proxy_call(proxy, "Shutdown", &error, - G_TYPE_INVALID, - G_TYPE_INVALID); - if (!res) + gboolean hbfd = ghb_dict_get_bool(ud->prefs, "hbfd_feature"); + if (hbfd) { - if (error != NULL) - { - g_warning("Shutdown failed: %s", error->message); - g_error_free(error); - } - else - g_warning("Shutdown failed"); + const GhbValue *val; + val = ghb_dict_get_value(ud->prefs, "hbfd"); + ghb_ui_settings_update(ud, ud->prefs, "hbfd", val); } - g_object_unref(G_OBJECT(proxy)); - dbus_g_connection_unref(conn); + widget = GHB_WIDGET(ud->builder, "hbfd"); + gtk_widget_set_visible(widget, hbfd); } -#endif -void -ghb_inhibit_gpm() +gboolean +ghb_file_menu_add_dvd(signal_user_data_t *ud) { -#if !defined(_WIN32) - DBusGConnection *conn; - DBusGProxy *proxy; - GError *error = NULL; - gboolean res; + GList *link, *drives; + static GList *dvd_items = NULL; + g_debug("ghb_file_menu_add_dvd()"); + GtkMenu *menu = GTK_MENU(GHB_WIDGET(ud->builder, "file_submenu")); - if (gpm_inhibited) - { - // Already inhibited - return; - } - g_debug("ghb_inhibit_gpm()"); - conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); - if (error != NULL) - { - g_warning("DBUS cannot connect: %s", error->message); - g_error_free(error); - return; - } - proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE, - GPM_DBUS_INHIBIT_PATH, GPM_DBUS_INHIBIT_INTERFACE); - if (proxy == NULL) + // Clear previous dvd items from list + link = dvd_items; + while (link != NULL) { - g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE); - dbus_g_connection_unref(conn); - return; + GtkWidget * widget = GTK_WIDGET(link->data); + // widget_destroy automatically removes widget from container. + gtk_widget_destroy(widget); + link = link->next; } - res = dbus_g_proxy_call(proxy, "Inhibit", &error, - G_TYPE_STRING, "ghb", - G_TYPE_STRING, "Encoding", - G_TYPE_INVALID, - G_TYPE_UINT, &gpm_cookie, - G_TYPE_INVALID); - gpm_inhibited = TRUE; - if (!res) + g_list_free(dvd_items); + dvd_items = NULL; + + int pos = 5; + link = drives = dvd_device_list(); + if (drives != NULL) { - if (error != NULL) + GtkWidget *widget = gtk_separator_menu_item_new(); + dvd_items = g_list_append(dvd_items, (gpointer)widget); + + gtk_menu_shell_insert(GTK_MENU_SHELL(menu), widget, pos++); + gtk_widget_set_visible(widget, TRUE); + + while (link != NULL) { - g_warning("Inhibit failed: %s", error->message); - g_error_free(error); - gpm_cookie = -1; + GtkWidget *widget; + gchar *drive = get_dvd_device_name(link->data); + gchar *name = get_dvd_volume_name(link->data); + + widget = gtk_menu_item_new_with_label(name); + gtk_buildable_set_name(GTK_BUILDABLE(widget), drive); + gtk_widget_set_tooltip_text(widget, _("Scan this DVD source")); + + dvd_items = g_list_append(dvd_items, (gpointer)widget); + gtk_menu_shell_insert(GTK_MENU_SHELL(menu), widget, pos++); + + gtk_widget_set_visible(widget, TRUE); + + // Connect signal to action (menu item) + g_signal_connect(widget, "activate", + (GCallback)dvd_source_activate_cb, ud); + g_free(name); + g_free(drive); + free_drive(link->data); + link = link->next; } - else - g_warning("Inhibit failed"); - gpm_cookie = -1; - gpm_inhibited = FALSE; + + g_list_free(drives); } - g_object_unref(G_OBJECT(proxy)); - dbus_g_connection_unref(conn); -#endif + + return FALSE; } -void -ghb_uninhibit_gpm() +gboolean ghb_is_cd(GDrive *gd); + +static GList* +dvd_device_list() { -#if !defined(_WIN32) - DBusGConnection *conn; - DBusGProxy *proxy; - GError *error = NULL; - gboolean res; + GList *dvd_devices = NULL; - g_debug("ghb_uninhibit_gpm() gpm_cookie %u", gpm_cookie); +#if defined(_WIN32) + gint ii, drives; + gchar drive[5]; - if (!gpm_inhibited) - { - // Not inhibited - return; - } - conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); - if (error != NULL) - { - g_warning("DBUS cannot connect: %s", error->message); - g_error_free(error); - return; - } - proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_PM_SERVICE, - GPM_DBUS_INHIBIT_PATH, GPM_DBUS_INHIBIT_INTERFACE); - if (proxy == NULL) + strcpy(drive, "A:" G_DIR_SEPARATOR_S); + drives = GetLogicalDrives(); + for (ii = 0; ii < 26; ii++) { - g_warning("Could not get DBUS proxy: %s", GPM_DBUS_PM_SERVICE); - dbus_g_connection_unref(conn); - return; + if (drives & 0x01) + { + guint dtype; + + drive[0] = 'A' + ii; + dtype = GetDriveType(drive); + if (dtype == DRIVE_CDROM) + { + dvd_devices = g_list_append(dvd_devices, + (gpointer)g_strdup(drive)); + } + } + drives >>= 1; } - res = dbus_g_proxy_call(proxy, "UnInhibit", &error, - G_TYPE_UINT, gpm_cookie, - G_TYPE_INVALID, - G_TYPE_INVALID); - if (!res) +#else + GVolumeMonitor *gvm; + GList *drives, *link; + + gvm = g_volume_monitor_get (); + drives = g_volume_monitor_get_connected_drives (gvm); + link = drives; + while (link != NULL) { - if (error != NULL) + GDrive *gd; + + gd = (GDrive*)link->data; + if (ghb_is_cd(gd)) { - g_warning("UnInhibit failed: %s", error->message); - g_error_free(error); + dvd_devices = g_list_append(dvd_devices, gd); } else - g_warning("UnInhibit failed"); + g_object_unref (gd); + link = link->next; } - gpm_inhibited = FALSE; - dbus_g_connection_unref(conn); - g_object_unref(G_OBJECT(proxy)); + g_list_free(drives); #endif -} - -#if !defined(_WIN32) -// For inhibit and shutdown -#define GPM_DBUS_SM_SERVICE "org.gnome.SessionManager" -#define GPM_DBUS_SM_PATH "/org/gnome/SessionManager" -#define GPM_DBUS_SM_INTERFACE "org.gnome.SessionManager" + return dvd_devices; +} +#if defined(__linux__) && defined(_HAVE_GUDEV) +static GUdevClient *udev_ctx = NULL; #endif -static gboolean -ghb_can_shutdown_gsm() +gboolean +ghb_is_cd(GDrive *gd) { - gboolean can_shutdown = FALSE; -#if !defined(_WIN32) - DBusGConnection *conn; - DBusGProxy *proxy; - GError *error = NULL; - gboolean res; - +#if defined(__linux__) && defined(_HAVE_GUDEV) + gchar *device; + GUdevDevice *udd; - g_debug("ghb_can_shutdown_gpm()"); - conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); - if (error != NULL) - { - g_warning("DBUS cannot connect: %s", error->message); - g_error_free(error); + if (udev_ctx == NULL) return FALSE; - } - proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SM_SERVICE, - GPM_DBUS_SM_PATH, GPM_DBUS_SM_INTERFACE); - if (proxy == NULL) - { - g_warning("Could not get DBUS proxy: %s", GPM_DBUS_SM_SERVICE); - dbus_g_connection_unref(conn); + + device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE); + if (device == NULL) return FALSE; - } - res = dbus_g_proxy_call(proxy, "CanShutdown", &error, - G_TYPE_INVALID, - G_TYPE_BOOLEAN, &can_shutdown, - G_TYPE_INVALID); - g_object_unref(G_OBJECT(proxy)); - dbus_g_connection_unref(conn); - if (!res) + + udd = g_udev_client_query_by_device_file(udev_ctx, device); + if (udd == NULL) { - if (error != NULL) - { - g_error_free(error); - } - // Try to shutdown anyway - can_shutdown = TRUE; - // Try the gpm version - return ghb_can_shutdown_gpm(); + g_message("udev: Failed to lookup device %s", device); + g_free(device); + return FALSE; } + g_free(device); + + gint val; + val = g_udev_device_get_property_as_int(udd, "ID_CDROM_DVD"); + g_object_unref(udd); + if (val == 1) + return TRUE; + + return FALSE; +#else + return FALSE; #endif - return can_shutdown; } -static void -ghb_shutdown_gsm() +void +ghb_udev_init() { -#if !defined(_WIN32) - DBusGConnection *conn; - DBusGProxy *proxy; - GError *error = NULL; - gboolean res; +#if defined(__linux__) && defined(_HAVE_GUDEV) + udev_ctx = g_udev_client_new(NULL); +#endif +} +#if defined(_WIN32) +static void +handle_media_change(const gchar *device, gboolean insert, signal_user_data_t *ud) +{ + guint dtype; + static gint ins_count = 0; + static gint rem_count = 0; - g_debug("ghb_shutdown_gpm()"); - conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); - if (error != NULL) - { - g_warning("DBUS cannot connect: %s", error->message); - g_error_free(error); + // The media change event in windows bounces around a bit + // so I debounce it here + // DVD insertion detected. Scan it. + dtype = GetDriveType(device); + if (dtype != DRIVE_CDROM) return; - } - proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SM_SERVICE, - GPM_DBUS_SM_PATH, GPM_DBUS_SM_INTERFACE); - if (proxy == NULL) + if (insert) { - g_warning("Could not get DBUS proxy: %s", GPM_DBUS_SM_SERVICE); - dbus_g_connection_unref(conn); - return; + rem_count = 0; + ins_count++; + if (ins_count == 2) + { + GHB_THREAD_NEW("Cache Volume Names", + (GThreadFunc)ghb_cache_volnames, ud); + if (ghb_dict_get_bool(ud->prefs, "AutoScan") && + ud->current_dvd_device != NULL && + strcmp(device, ud->current_dvd_device) == 0) + { + show_scan_progress(ud); + update_source_label(ud, device); + gint preview_count; + preview_count = ghb_dict_get_int(ud->prefs, "preview_count"); + ghb_dict_set_string(ud->globals, "scan_source", device); + start_scan(ud, device, 0, preview_count); + } + } } - res = dbus_g_proxy_call(proxy, "Shutdown", &error, - G_TYPE_INVALID, - G_TYPE_INVALID); - g_object_unref(G_OBJECT(proxy)); - dbus_g_connection_unref(conn); - if (!res) + else { - if (error != NULL) + ins_count = 0; + rem_count++; + if (rem_count == 2) { - g_error_free(error); + GHB_THREAD_NEW("Cache Volume Names", + (GThreadFunc)ghb_cache_volnames, ud); + if (ud->current_dvd_device != NULL && + strcmp(device, ud->current_dvd_device) == 0) + { + ghb_hb_cleanup(TRUE); + prune_logs(ud); + ghb_dict_set_string(ud->globals, "scan_source", "/dev/null"); + start_scan(ud, "/dev/null", 0, 1); + } } - // Try the gpm version - ghb_shutdown_gpm(); } -#endif } -void -ghb_inhibit_gsm(signal_user_data_t *ud) +static gchar +FindDriveFromMask(ULONG unitmask) { -#if !defined(_WIN32) - DBusGConnection *conn; - DBusGProxy *proxy; - GError *error = NULL; - gboolean res; - guint xid; - GtkWidget *widget; - - - if (gpm_inhibited) - { - // Already inhibited - return; - } - g_debug("ghb_inhibit_gsm()"); - conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); - if (error != NULL) - { - g_warning("DBUS cannot connect: %s", error->message); - g_error_free(error); - return; - } - proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SM_SERVICE, - GPM_DBUS_SM_PATH, GPM_DBUS_SM_INTERFACE); - if (proxy == NULL) + gchar cc; + for (cc = 0; cc < 26; cc++) { - g_warning("Could not get DBUS proxy: %s", GPM_DBUS_SM_SERVICE); - dbus_g_connection_unref(conn); - return; + if (unitmask & 0x01) + return 'A' + cc; + unitmask >>= 1; } - widget = GHB_WIDGET(ud->builder, "hb_window"); - xid = GDK_WINDOW_XID(gtk_widget_get_window(widget)); - res = dbus_g_proxy_call(proxy, "Inhibit", &error, - G_TYPE_STRING, "ghb", - G_TYPE_UINT, xid, - G_TYPE_STRING, "Encoding", - G_TYPE_UINT, 1 | 4, - G_TYPE_INVALID, - G_TYPE_UINT, &gpm_cookie, - G_TYPE_INVALID); - gpm_inhibited = TRUE; - g_object_unref(G_OBJECT(proxy)); - dbus_g_connection_unref(conn); - if (!res) + return 0; +} + +void +wm_drive_changed(MSG *msg, signal_user_data_t *ud) +{ + PDEV_BROADCAST_HDR bch = (PDEV_BROADCAST_HDR)msg->lParam; + gchar drive[4]; + + g_strlcpy(drive, "A:" G_DIR_SEPARATOR_S, 4); + switch (msg->wParam) { - if (error != NULL) + case DBT_DEVICEARRIVAL: { - g_error_free(error); - gpm_cookie = -1; - } - gpm_cookie = -1; - gpm_inhibited = FALSE; - // Try the gpm version - ghb_inhibit_gpm(); + if (bch->dbch_devicetype == DBT_DEVTYP_VOLUME) + { + PDEV_BROADCAST_VOLUME bcv = (PDEV_BROADCAST_VOLUME)bch; + + if (bcv->dbcv_flags & DBTF_MEDIA) + { + drive[0] = FindDriveFromMask(bcv->dbcv_unitmask); + handle_media_change(drive, TRUE, ud); + } + } + } break; + + case DBT_DEVICEREMOVECOMPLETE: + { + if (bch->dbch_devicetype == DBT_DEVTYP_VOLUME) + { + PDEV_BROADCAST_VOLUME bcv = (PDEV_BROADCAST_VOLUME)bch; + + if (bcv->dbcv_flags & DBTF_MEDIA) + { + drive[0] = FindDriveFromMask(bcv->dbcv_unitmask); + handle_media_change(drive, FALSE, ud); + } + } + } break; + default: ; } -#endif } -void -ghb_uninhibit_gsm() +#else + +G_MODULE_EXPORT void +drive_changed_cb(GVolumeMonitor *gvm, GDrive *gd, signal_user_data_t *ud) { -#if !defined(_WIN32) - DBusGConnection *conn; - DBusGProxy *proxy; - GError *error = NULL; - gboolean res; + gchar *device; + gint state; - g_debug("ghb_uninhibit_gsm() gpm_cookie %u", gpm_cookie); + g_debug("drive_changed_cb()"); + GHB_THREAD_NEW("Cache Volume Names", (GThreadFunc)ghb_cache_volnames, ud); - if (!gpm_inhibited) - { - // Not inhibited - return; - } - conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); - if (error != NULL) - { - g_warning("DBUS cannot connect: %s", error->message); - g_error_free(error); - return; - } - proxy = dbus_g_proxy_new_for_name(conn, GPM_DBUS_SM_SERVICE, - GPM_DBUS_SM_PATH, GPM_DBUS_SM_INTERFACE); - if (proxy == NULL) + state = ghb_get_scan_state(); + device = g_drive_get_identifier(gd, G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE); + if (ud->current_dvd_device == NULL || + strcmp(device, ud->current_dvd_device) != 0 || + state != GHB_STATE_IDLE ) { - g_warning("Could not get DBUS proxy: %s", GPM_DBUS_SM_SERVICE); - dbus_g_connection_unref(conn); return; } - res = dbus_g_proxy_call(proxy, "Uninhibit", &error, - G_TYPE_UINT, gpm_cookie, - G_TYPE_INVALID, - G_TYPE_INVALID); - dbus_g_connection_unref(conn); - g_object_unref(G_OBJECT(proxy)); - if (!res) + if (g_drive_has_media(gd)) { - if (error != NULL) + if (ghb_dict_get_bool(ud->prefs, "AutoScan")) { - g_error_free(error); + show_scan_progress(ud); + update_source_label(ud, device); + gint preview_count; + preview_count = ghb_dict_get_int(ud->prefs, "preview_count"); + ghb_dict_set_string(ud->globals, "scan_source", device); + start_scan(ud, device, 0, preview_count); } - ghb_uninhibit_gpm(); } - gpm_inhibited = FALSE; -#endif + else + { + ghb_hb_cleanup(TRUE); + prune_logs(ud); + ghb_dict_set_string(ud->globals, "scan_source", "/dev/null"); + start_scan(ud, "/dev/null", 0, 1); + } } +#endif G_MODULE_EXPORT gboolean easter_egg_cb( diff --git a/gtk/src/callbacks.h b/gtk/src/callbacks.h index 472ceea13..80530762e 100644 --- a/gtk/src/callbacks.h +++ b/gtk/src/callbacks.h @@ -68,8 +68,6 @@ void ghb_do_scan( signal_user_data_t *ud, const gchar *filename, gint titlenum, gboolean force); void ghb_log(gchar *log, ...); gpointer ghb_check_update(signal_user_data_t *ud); -void ghb_uninhibit_gsm(void); -void ghb_inhibit_gsm(signal_user_data_t *ud); #if defined(_WIN32) void wm_drive_changed(MSG *msg, signal_user_data_t *ud); #endif