mirror of
https://github.com/Rafostar/clapper.git
synced 2025-08-29 23:32:04 +02:00
999 lines
33 KiB
C
999 lines
33 KiB
C
/* Clapper Application
|
|
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <glib/gi18n.h>
|
|
#include <gst/gst.h>
|
|
#include <clapper/clapper.h>
|
|
|
|
#include "clapper-app-preferences-window.h"
|
|
#include "clapper-app-application.h"
|
|
#include "clapper-app-file-dialog.h"
|
|
#include "clapper-app-utils.h"
|
|
|
|
#define GST_CAT_DEFAULT clapper_app_preferences_window_debug
|
|
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
|
|
|
|
struct _ClapperAppPreferencesWindow
|
|
{
|
|
AdwPreferencesWindow parent;
|
|
|
|
AdwComboRow *seek_method_combo_row;
|
|
AdwComboRow *seek_unit_combo_row;
|
|
AdwSpinRow *seek_value_spin_row;
|
|
AdwSwitchRow *server_switch_row;
|
|
|
|
AdwSpinRow *audio_offset_spin_row;
|
|
AdwSpinRow *subtitle_offset_spin_row;
|
|
GtkFontDialogButton *font_dialog_button;
|
|
|
|
GtkStack *enhancers_stack;
|
|
GtkWidget *browse_enhancers_page;
|
|
GtkWidget *no_enhancers_page;
|
|
|
|
AdwNavigationPage *enhancers_subpage;
|
|
AdwComboRow *enhancers_combo_row;
|
|
AdwPreferencesGroup *enhancer_config_group;
|
|
|
|
AdwNavigationPage *plugins_subpage;
|
|
AdwComboRow *plugins_combo_row;
|
|
AdwComboRow *features_combo_row;
|
|
AdwPreferencesGroup *overrides_group;
|
|
|
|
GSettings *settings;
|
|
|
|
GList *enhancer_pspec_rows;
|
|
|
|
GList *features;
|
|
GtkStringList *plugins_list;
|
|
|
|
GPtrArray *rank_rows;
|
|
gulong ranks_setting_changed_id;
|
|
|
|
gboolean ranking_has_plugins_model;
|
|
};
|
|
|
|
#define parent_class clapper_app_preferences_window_parent_class
|
|
G_DEFINE_TYPE (ClapperAppPreferencesWindow, clapper_app_preferences_window, ADW_TYPE_PREFERENCES_WINDOW);
|
|
|
|
typedef struct
|
|
{
|
|
ClapperAppPreferencesWindow *prefs;
|
|
GHashTable *parsed_overrides;
|
|
gboolean updated;
|
|
} ClapperAppPreferencesIterRanksData;
|
|
|
|
typedef struct
|
|
{
|
|
GSettings *settings;
|
|
const gchar *key;
|
|
} ClapperAppPreferencesResetData;
|
|
|
|
typedef struct
|
|
{
|
|
GSettings *settings;
|
|
GParamSpec *pspec;
|
|
guint flag;
|
|
} ClapperAppPreferencesFlagMapData;
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_RANK_ROWS,
|
|
PROP_LAST
|
|
};
|
|
|
|
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
|
|
|
|
static void
|
|
_flag_map_data_free (ClapperAppPreferencesFlagMapData *data)
|
|
{
|
|
GST_TRACE ("Destroying flag map data: %p", data);
|
|
g_object_unref (data->settings);
|
|
g_free (data);
|
|
}
|
|
|
|
static void
|
|
_reset_button_closure (ClapperAppPreferencesResetData *data, GClosure *closure)
|
|
{
|
|
GST_TRACE ("Destroying reset button data: %p", data);
|
|
g_object_unref (data->settings);
|
|
g_free (data);
|
|
}
|
|
|
|
static void
|
|
_reset_button_clicked_cb (GtkButton *button, ClapperAppPreferencesResetData *data)
|
|
{
|
|
g_settings_reset (data->settings, data->key);
|
|
}
|
|
|
|
static void
|
|
file_selection_row_activated_cb (AdwActionRow *action_row, GParamSpec *pspec)
|
|
{
|
|
GtkApplication *gtk_app;
|
|
GtkWidget *window;
|
|
|
|
if (!(window = gtk_widget_get_ancestor (GTK_WIDGET (action_row), GTK_TYPE_WINDOW))) {
|
|
GST_ERROR ("Could not get a hold of parent window");
|
|
return;
|
|
}
|
|
|
|
gtk_app = gtk_window_get_application (GTK_WINDOW (window));
|
|
|
|
if (pspec->flags & CLAPPER_ENHANCER_PARAM_FILEPATH)
|
|
clapper_app_file_dialog_select_prefs_file (gtk_app, action_row);
|
|
else
|
|
clapper_app_file_dialog_select_prefs_dir (gtk_app, action_row);
|
|
}
|
|
|
|
static gboolean
|
|
_get_enum_mapping (GValue *value, GVariant *variant, GParamSpec *pspec)
|
|
{
|
|
GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_peek (pspec->value_type));
|
|
const gchar *selected_str = g_variant_get_string (variant, NULL);
|
|
guint i, selected = 0;
|
|
|
|
for (i = 0; i < enum_class->n_values; ++i) {
|
|
if (g_strcmp0 (selected_str, enum_class->values[i].value_nick) == 0) {
|
|
selected = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
g_value_set_uint (value, selected);
|
|
return TRUE;
|
|
}
|
|
|
|
static GVariant *
|
|
_set_enum_mapping (GValue *value, GVariantType *exp_type, GParamSpec *pspec)
|
|
{
|
|
GEnumClass *enum_class = G_ENUM_CLASS (g_type_class_peek (pspec->value_type));
|
|
guint selected = g_value_get_uint (value);
|
|
|
|
if (G_UNLIKELY (selected == GTK_INVALID_LIST_POSITION))
|
|
selected = 0;
|
|
|
|
return g_variant_new_string (enum_class->values[selected].value_nick);
|
|
}
|
|
|
|
static gboolean
|
|
_get_flag_mapping (GValue *value, GVariant *variant, ClapperAppPreferencesFlagMapData *data)
|
|
{
|
|
guint flags = g_settings_get_flags (data->settings, data->pspec->name);
|
|
|
|
g_value_set_boolean (value, (flags & data->flag));
|
|
return TRUE;
|
|
}
|
|
|
|
static GVariant *
|
|
_set_flag_mapping (GValue *value, GVariantType *exp_type, ClapperAppPreferencesFlagMapData *data)
|
|
{
|
|
GFlagsClass *flags_class = G_FLAGS_CLASS (g_type_class_peek (data->pspec->value_type));
|
|
GStrvBuilder *builder;
|
|
GVariant *variant;
|
|
gchar **strv;
|
|
gboolean active = g_value_get_boolean (value);
|
|
guint i, flags = g_settings_get_flags (data->settings, data->pspec->name);
|
|
|
|
if (active)
|
|
flags |= data->flag;
|
|
else
|
|
flags &= ~(data->flag);
|
|
|
|
builder = g_strv_builder_new ();
|
|
|
|
for (i = 0; i < flags_class->n_values; ++i) {
|
|
if (flags & flags_class->values[i].value)
|
|
g_strv_builder_add (builder, flags_class->values[i].value_nick);
|
|
}
|
|
|
|
strv = g_strv_builder_end (builder);
|
|
g_strv_builder_unref (builder);
|
|
|
|
variant = g_variant_new_strv ((const gchar *const *) strv, -1);
|
|
g_strfreev (strv);
|
|
|
|
return variant;
|
|
}
|
|
|
|
static gboolean
|
|
_add_enhancer_config_row (ClapperAppPreferencesWindow *self, GParamSpec *pspec,
|
|
GSettings *enhancer_settings)
|
|
{
|
|
GtkWidget *row = NULL, *reset_button;
|
|
ClapperAppPreferencesResetData *reset_data;
|
|
const gchar *bind_prop = NULL;
|
|
gboolean is_enum = FALSE, is_flags = FALSE;
|
|
|
|
switch (pspec->value_type) {
|
|
case G_TYPE_BOOLEAN:{
|
|
row = adw_switch_row_new ();
|
|
break;
|
|
}
|
|
case G_TYPE_INT:{
|
|
GParamSpecInt *p = (GParamSpecInt *) pspec;
|
|
row = adw_spin_row_new_with_range (p->minimum, p->maximum, 1);
|
|
break;
|
|
}
|
|
case G_TYPE_UINT:{
|
|
GParamSpecUInt *p = (GParamSpecUInt *) pspec;
|
|
row = adw_spin_row_new_with_range (p->minimum, p->maximum, 1);
|
|
break;
|
|
}
|
|
case G_TYPE_DOUBLE:{
|
|
GParamSpecDouble *p = (GParamSpecDouble *) pspec;
|
|
row = adw_spin_row_new_with_range (p->minimum, p->maximum, 0.25);
|
|
break;
|
|
}
|
|
case G_TYPE_STRING:{
|
|
if (pspec->flags & (CLAPPER_ENHANCER_PARAM_FILEPATH | CLAPPER_ENHANCER_PARAM_DIRPATH)) {
|
|
GtkWidget *image;
|
|
|
|
image = gtk_image_new_from_icon_name ("document-open-symbolic");
|
|
gtk_widget_set_margin_end (image, 10); // matches other rows
|
|
|
|
row = adw_action_row_new ();
|
|
adw_action_row_add_suffix (ADW_ACTION_ROW (row), image);
|
|
adw_action_row_set_activatable_widget (ADW_ACTION_ROW (row), image);
|
|
|
|
g_signal_connect (row, "activated",
|
|
G_CALLBACK (file_selection_row_activated_cb), pspec);
|
|
} else {
|
|
row = adw_entry_row_new ();
|
|
}
|
|
break;
|
|
}
|
|
default:{
|
|
if ((is_enum = G_IS_PARAM_SPEC_ENUM (pspec))) {
|
|
GtkExpression *expression;
|
|
AdwEnumListModel *enum_model;
|
|
|
|
row = adw_combo_row_new ();
|
|
expression = gtk_property_expression_new (ADW_TYPE_ENUM_LIST_ITEM, NULL, "nick");
|
|
adw_combo_row_set_expression (ADW_COMBO_ROW (row), expression);
|
|
|
|
enum_model = adw_enum_list_model_new (pspec->value_type);
|
|
adw_combo_row_set_model (ADW_COMBO_ROW (row), G_LIST_MODEL (enum_model));
|
|
|
|
gtk_expression_unref (expression);
|
|
g_object_unref (enum_model);
|
|
break;
|
|
} else if ((is_flags = G_IS_PARAM_SPEC_FLAGS (pspec))) {
|
|
GFlagsClass *flags_class = G_FLAGS_CLASS (g_type_class_peek (pspec->value_type));
|
|
guint i;
|
|
|
|
row = adw_expander_row_new ();
|
|
|
|
for (i = 0; i < flags_class->n_values; ++i) {
|
|
GtkWidget *flag_row = adw_switch_row_new ();
|
|
ClapperAppPreferencesFlagMapData *fm_data;
|
|
|
|
adw_preferences_row_set_title (ADW_PREFERENCES_ROW (flag_row),
|
|
flags_class->values[i].value_nick);
|
|
|
|
fm_data = g_new (ClapperAppPreferencesFlagMapData, 1);
|
|
fm_data->settings = g_object_ref (enhancer_settings);
|
|
fm_data->pspec = pspec;
|
|
fm_data->flag = flags_class->values[i].value;
|
|
|
|
GST_TRACE ("Created flag map data: %p", fm_data);
|
|
|
|
g_settings_bind_with_mapping (enhancer_settings, pspec->name, flag_row,
|
|
"active", G_SETTINGS_BIND_DEFAULT,
|
|
(GSettingsBindGetMapping) _get_flag_mapping,
|
|
(GSettingsBindSetMapping) _set_flag_mapping,
|
|
fm_data, (GDestroyNotify) _flag_map_data_free);
|
|
|
|
adw_expander_row_add_row (ADW_EXPANDER_ROW (row), flag_row);
|
|
}
|
|
break;
|
|
}
|
|
g_warning ("Unsupported enhancer \"%s\" property type: %s",
|
|
pspec->name, g_type_name (pspec->value_type));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
reset_button = gtk_button_new_from_icon_name ("view-refresh-symbolic");
|
|
gtk_widget_set_tooltip_text (reset_button, _("Restore default"));
|
|
|
|
gtk_widget_set_halign (reset_button, GTK_ALIGN_CENTER);
|
|
gtk_widget_set_valign (reset_button, GTK_ALIGN_CENTER);
|
|
gtk_widget_add_css_class (reset_button, "circular");
|
|
|
|
gtk_widget_set_tooltip_text (row, g_param_spec_get_blurb (pspec));
|
|
|
|
adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), g_param_spec_get_nick (pspec));
|
|
|
|
if (ADW_IS_SWITCH_ROW (row)) {
|
|
bind_prop = "active";
|
|
} else if (ADW_IS_SPIN_ROW (row)) {
|
|
bind_prop = "value";
|
|
adw_spin_row_set_numeric (ADW_SPIN_ROW (row), TRUE);
|
|
} else if (ADW_IS_ENTRY_ROW (row)) {
|
|
bind_prop = "text";
|
|
} else if (ADW_IS_COMBO_ROW (row)) {
|
|
bind_prop = "selected";
|
|
} else if (ADW_IS_ACTION_ROW (row)) {
|
|
bind_prop = "subtitle";
|
|
} else if (!is_flags) { // In case of flags we bind individual widgets
|
|
g_assert_not_reached ();
|
|
return FALSE;
|
|
}
|
|
|
|
if (ADW_IS_ENTRY_ROW (row))
|
|
adw_entry_row_add_prefix (ADW_ENTRY_ROW (row), reset_button);
|
|
else if (ADW_IS_ACTION_ROW (row))
|
|
adw_action_row_add_prefix (ADW_ACTION_ROW (row), reset_button);
|
|
else if (ADW_IS_EXPANDER_ROW (row))
|
|
adw_expander_row_add_prefix (ADW_EXPANDER_ROW (row), reset_button);
|
|
|
|
if (is_enum) {
|
|
g_settings_bind_with_mapping (enhancer_settings, pspec->name, row,
|
|
bind_prop, G_SETTINGS_BIND_DEFAULT,
|
|
(GSettingsBindGetMapping) _get_enum_mapping,
|
|
(GSettingsBindSetMapping) _set_enum_mapping,
|
|
pspec, NULL);
|
|
} else if (!is_flags) {
|
|
g_settings_bind (enhancer_settings, pspec->name, row,
|
|
bind_prop, G_SETTINGS_BIND_DEFAULT);
|
|
}
|
|
|
|
reset_data = g_new (ClapperAppPreferencesResetData, 1);
|
|
reset_data->settings = g_object_ref (enhancer_settings);
|
|
reset_data->key = pspec->name;
|
|
|
|
GST_TRACE ("Created reset button data: %p", reset_data);
|
|
|
|
g_signal_connect_data (reset_button, "clicked",
|
|
G_CALLBACK (_reset_button_clicked_cb), reset_data,
|
|
(GClosureNotify) _reset_button_closure, G_CONNECT_DEFAULT);
|
|
|
|
adw_preferences_group_add (self->enhancer_config_group, row);
|
|
self->enhancer_pspec_rows = g_list_append (self->enhancer_pspec_rows, row);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
selected_enhancer_changed_cb (AdwComboRow *combo_row,
|
|
GParamSpec *pspec G_GNUC_UNUSED, ClapperAppPreferencesWindow *self)
|
|
{
|
|
guint selected = adw_combo_row_get_selected (combo_row);
|
|
|
|
/* Remove old rows */
|
|
if (self->enhancer_pspec_rows) {
|
|
GList *el;
|
|
|
|
for (el = self->enhancer_pspec_rows; el; el = g_list_next (el))
|
|
adw_preferences_group_remove (self->enhancer_config_group, GTK_WIDGET (el->data));
|
|
|
|
g_clear_list (&self->enhancer_pspec_rows, NULL);
|
|
}
|
|
|
|
/* Add new rows */
|
|
if (selected != GTK_INVALID_LIST_POSITION) {
|
|
ClapperEnhancerProxyList *proxies = clapper_get_global_enhancer_proxies ();
|
|
ClapperEnhancerProxy *proxy = clapper_enhancer_proxy_list_peek_proxy (proxies, selected);
|
|
GParamSpec **pspecs;
|
|
guint n_pspecs;
|
|
gboolean has_props = FALSE;
|
|
|
|
if ((pspecs = clapper_enhancer_proxy_get_target_properties (proxy, &n_pspecs))) {
|
|
GSettings *enhancer_settings = NULL;
|
|
guint i;
|
|
|
|
for (i = 0; i < n_pspecs; ++i) {
|
|
if (pspecs[i]->flags & CLAPPER_ENHANCER_PARAM_GLOBAL) {
|
|
if (!enhancer_settings)
|
|
enhancer_settings = clapper_enhancer_proxy_get_settings (proxy);
|
|
if (enhancer_settings)
|
|
has_props |= _add_enhancer_config_row (self, pspecs[i], enhancer_settings);
|
|
}
|
|
}
|
|
|
|
g_clear_object (&enhancer_settings);
|
|
}
|
|
|
|
if (!has_props) {
|
|
GtkWidget *row = adw_action_row_new ();
|
|
|
|
adw_preferences_row_set_title (ADW_PREFERENCES_ROW (row), _("No configurable properties"));
|
|
adw_preferences_group_add (self->enhancer_config_group, row);
|
|
self->enhancer_pspec_rows = g_list_append (self->enhancer_pspec_rows, row);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
enhancers_config_activated_cb (AdwActionRow *action_row, ClapperAppPreferencesWindow *self)
|
|
{
|
|
/* If no model set yet */
|
|
if (!adw_combo_row_get_model (self->enhancers_combo_row)) {
|
|
ClapperEnhancerProxyList *proxies = clapper_get_global_enhancer_proxies ();
|
|
|
|
adw_combo_row_set_model (self->enhancers_combo_row, G_LIST_MODEL (proxies));
|
|
adw_combo_row_set_selected (self->enhancers_combo_row, GTK_INVALID_LIST_POSITION);
|
|
|
|
GST_DEBUG ("Populated names combo row in enhancers subpage");
|
|
|
|
if (clapper_enhancer_proxy_list_get_n_proxies (proxies) > 0)
|
|
gtk_stack_set_visible_child (self->enhancers_stack, self->browse_enhancers_page);
|
|
else
|
|
gtk_stack_set_visible_child (self->enhancers_stack, self->no_enhancers_page);
|
|
}
|
|
|
|
adw_preferences_window_push_subpage (ADW_PREFERENCES_WINDOW (self), self->enhancers_subpage);
|
|
}
|
|
|
|
/* Sort by plugin name and if the same, sort by element name */
|
|
static gint
|
|
_compare_plugins_cb (gconstpointer ptr_a, gconstpointer ptr_b)
|
|
{
|
|
GstPluginFeature *feature_a = GST_PLUGIN_FEATURE_CAST (ptr_a);
|
|
GstPluginFeature *feature_b = GST_PLUGIN_FEATURE_CAST (ptr_b);
|
|
gint result;
|
|
|
|
result = strcmp (
|
|
gst_plugin_feature_get_plugin_name (feature_a),
|
|
gst_plugin_feature_get_plugin_name (feature_b));
|
|
|
|
if (result == 0) {
|
|
result = strcmp (
|
|
gst_plugin_feature_get_name (feature_a),
|
|
gst_plugin_feature_get_name (feature_b));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static gint
|
|
_compare_names_cb (gconstpointer ptr_a, gconstpointer ptr_b)
|
|
{
|
|
GstPluginFeature *feature = GST_PLUGIN_FEATURE_CAST (ptr_a);
|
|
const gchar *plugin_name = (const gchar *) ptr_b;
|
|
|
|
return strcmp (gst_plugin_feature_get_plugin_name (feature), plugin_name);
|
|
}
|
|
|
|
static gboolean
|
|
_prefs_rows_compare_func (gconstpointer ptr_a, gconstpointer ptr_b)
|
|
{
|
|
AdwPreferencesRow *row = (AdwPreferencesRow *) ptr_a;
|
|
const gchar *name = (const gchar *) ptr_b;
|
|
|
|
return (strcmp (adw_preferences_row_get_title (row), name) == 0);
|
|
}
|
|
|
|
static gboolean
|
|
_find_rank_overide_for_name (ClapperAppPreferencesWindow *self,
|
|
const gchar *plugin_feature, guint *index)
|
|
{
|
|
return g_ptr_array_find_with_equal_func (self->rank_rows,
|
|
plugin_feature, (GEqualFunc) _prefs_rows_compare_func, index);
|
|
}
|
|
|
|
static gboolean
|
|
_plugin_feature_filter_cb (GstPluginFeature *feature, gpointer user_data G_GNUC_UNUSED)
|
|
{
|
|
return GST_IS_ELEMENT_FACTORY (feature);
|
|
}
|
|
|
|
static void
|
|
remove_rank_override_button_clicked_cb (GtkButton *button, ClapperAppPreferencesWindow *self)
|
|
{
|
|
GtkWidget *spin_row;
|
|
const gchar *feature_name;
|
|
|
|
spin_row = gtk_widget_get_ancestor (GTK_WIDGET (button), ADW_TYPE_SPIN_ROW);
|
|
feature_name = adw_preferences_row_get_title (ADW_PREFERENCES_ROW (spin_row));
|
|
|
|
GST_DEBUG ("Removing rank override for: %s", feature_name);
|
|
|
|
g_ptr_array_remove (self->rank_rows, spin_row);
|
|
adw_preferences_group_remove (self->overrides_group, GTK_WIDGET (spin_row));
|
|
|
|
gtk_widget_set_visible (GTK_WIDGET (self->overrides_group), self->rank_rows->len > 0);
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_RANK_ROWS]);
|
|
}
|
|
|
|
static void
|
|
_add_rank_override (ClapperAppPreferencesWindow *self,
|
|
const gchar *feature_name, GstRank rank, gboolean from_env)
|
|
{
|
|
GtkWidget *spin_row, *remove_button;
|
|
|
|
spin_row = adw_spin_row_new_with_range (0, G_MAXINT, 1);
|
|
remove_button = gtk_button_new_from_icon_name ("user-trash-symbolic");
|
|
|
|
gtk_widget_set_halign (remove_button, GTK_ALIGN_CENTER);
|
|
gtk_widget_set_valign (remove_button, GTK_ALIGN_CENTER);
|
|
gtk_widget_add_css_class (remove_button, "circular");
|
|
|
|
adw_preferences_row_set_title (ADW_PREFERENCES_ROW (spin_row), feature_name);
|
|
adw_action_row_add_prefix (ADW_ACTION_ROW (spin_row), remove_button);
|
|
adw_spin_row_set_numeric (ADW_SPIN_ROW (spin_row), TRUE);
|
|
adw_spin_row_set_value (ADW_SPIN_ROW (spin_row), rank);
|
|
gtk_widget_set_sensitive (spin_row, !from_env);
|
|
|
|
if (!from_env) {
|
|
g_signal_connect (remove_button, "clicked",
|
|
G_CALLBACK (remove_rank_override_button_clicked_cb), self);
|
|
}
|
|
|
|
adw_preferences_group_add (self->overrides_group, spin_row);
|
|
g_ptr_array_add (self->rank_rows, spin_row);
|
|
}
|
|
|
|
static void
|
|
_iter_ranks_func (const gchar *feature_name, GstRank rank,
|
|
gboolean from_env, ClapperAppPreferencesIterRanksData *data)
|
|
{
|
|
ClapperAppPreferencesWindow *self = data->prefs;
|
|
guint index = 0;
|
|
|
|
if (_find_rank_overide_for_name (self, feature_name, &index)) {
|
|
GtkWidget *spin_row = g_ptr_array_index (self->rank_rows, index);
|
|
|
|
if (rank != adw_spin_row_get_value (ADW_SPIN_ROW (spin_row))) {
|
|
adw_spin_row_set_value (ADW_SPIN_ROW (spin_row), rank);
|
|
data->updated = TRUE;
|
|
}
|
|
if (from_env == gtk_widget_get_sensitive (spin_row)) {
|
|
gtk_widget_set_sensitive (spin_row, !from_env);
|
|
data->updated = TRUE;
|
|
}
|
|
} else {
|
|
_add_rank_override (self, feature_name, rank, from_env);
|
|
data->updated = TRUE;
|
|
}
|
|
g_hash_table_insert (data->parsed_overrides,
|
|
g_strdup (feature_name), GINT_TO_POINTER (rank));
|
|
}
|
|
|
|
static void
|
|
_update_rank_overrides (ClapperAppPreferencesWindow *self)
|
|
{
|
|
ClapperAppPreferencesIterRanksData *data;
|
|
gint i;
|
|
|
|
data = g_new (ClapperAppPreferencesIterRanksData, 1);
|
|
data->prefs = self;
|
|
data->parsed_overrides = g_hash_table_new (g_str_hash, g_str_equal);
|
|
data->updated = FALSE;
|
|
|
|
GST_DEBUG ("Updating rank overrides");
|
|
|
|
clapper_app_utils_iterate_plugin_feature_ranks (self->settings,
|
|
(ClapperAppUtilsIterRanks) _iter_ranks_func, data);
|
|
|
|
for (i = self->rank_rows->len - 1; i >= 0; --i) {
|
|
AdwPreferencesRow *prefs_row = ADW_PREFERENCES_ROW (g_ptr_array_index (self->rank_rows, i));
|
|
const gchar *feature_name = adw_preferences_row_get_title (prefs_row);
|
|
|
|
if (!g_hash_table_contains (data->parsed_overrides, feature_name)) {
|
|
g_ptr_array_remove_index (self->rank_rows, i);
|
|
adw_preferences_group_remove (self->overrides_group, GTK_WIDGET (prefs_row));
|
|
data->updated = TRUE;
|
|
}
|
|
}
|
|
|
|
if (data->updated) {
|
|
gtk_widget_set_visible (GTK_WIDGET (self->overrides_group), self->rank_rows->len > 0);
|
|
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_RANK_ROWS]);
|
|
}
|
|
|
|
g_hash_table_unref (data->parsed_overrides);
|
|
g_free (data);
|
|
}
|
|
|
|
static void
|
|
add_override_button_clicked_cb (GtkButton *button, ClapperAppPreferencesWindow *self)
|
|
{
|
|
GstPluginFeature *plugin_feature;
|
|
GstRank rank;
|
|
GtkStringObject *string_obj;
|
|
const gchar *feature_name;
|
|
|
|
string_obj = GTK_STRING_OBJECT (adw_combo_row_get_selected_item (self->features_combo_row));
|
|
|
|
/* Should never happen, as button is insensitive when no selection */
|
|
if (G_UNLIKELY (string_obj == NULL))
|
|
return;
|
|
|
|
feature_name = gtk_string_object_get_string (string_obj);
|
|
|
|
GST_DEBUG ("Adding rank override for: %s", feature_name);
|
|
|
|
plugin_feature = gst_registry_lookup_feature (gst_registry_get (), feature_name);
|
|
rank = gst_plugin_feature_get_rank (plugin_feature);
|
|
|
|
_add_rank_override (self, feature_name, rank, FALSE);
|
|
gtk_widget_set_visible (GTK_WIDGET (self->overrides_group), self->rank_rows->len > 0);
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_RANK_ROWS]);
|
|
|
|
gst_object_unref (plugin_feature);
|
|
}
|
|
|
|
static GtkStringList *
|
|
_make_plugin_features_string_list (ClapperAppPreferencesWindow *self, const gchar *plugin_name)
|
|
{
|
|
GList *features, *feat;
|
|
GtkStringList *features_list;
|
|
GStrvBuilder *builder;
|
|
gchar **features_names;
|
|
|
|
GST_DEBUG ("Reading plugin features for plugin: %s", plugin_name);
|
|
|
|
features = g_list_find_custom (self->features, plugin_name, (GCompareFunc) _compare_names_cb);
|
|
builder = g_strv_builder_new ();
|
|
|
|
for (feat = features; feat != NULL; feat = feat->next) {
|
|
GstPluginFeature *feature = GST_PLUGIN_FEATURE_CAST (feat->data);
|
|
const gchar *feature_name = gst_plugin_feature_get_name (feature);
|
|
|
|
if (strcmp (gst_plugin_feature_get_plugin_name (feature), plugin_name) != 0)
|
|
break;
|
|
|
|
g_strv_builder_add (builder, feature_name);
|
|
}
|
|
|
|
features_names = g_strv_builder_end (builder);
|
|
g_strv_builder_unref (builder);
|
|
|
|
features_list = gtk_string_list_new ((const gchar *const *) features_names);
|
|
g_strfreev (features_names);
|
|
|
|
GST_DEBUG ("Found plugin features: %u", g_list_model_get_n_items (G_LIST_MODEL (features_list)));
|
|
|
|
return features_list;
|
|
}
|
|
|
|
static gboolean
|
|
list_has_selection_closure (ClapperAppPreferencesWindow *self, guint selected)
|
|
{
|
|
return (selected != GTK_INVALID_LIST_POSITION);
|
|
}
|
|
|
|
static GtkStringList *
|
|
ranking_features_model_closure (ClapperAppPreferencesWindow *self, GtkStringObject *string_obj)
|
|
{
|
|
if (!string_obj || !self->ranking_has_plugins_model)
|
|
return NULL;
|
|
|
|
return _make_plugin_features_string_list (self, gtk_string_object_get_string (string_obj));
|
|
}
|
|
|
|
static gboolean
|
|
add_override_button_sensitive_closure (ClapperAppPreferencesWindow *self,
|
|
GtkStringObject *string_obj, GPtrArray *rank_rows)
|
|
{
|
|
return (string_obj && !_find_rank_overide_for_name (self,
|
|
gtk_string_object_get_string (string_obj), NULL));
|
|
}
|
|
|
|
static void
|
|
plugin_feature_ranks_settings_changed_cb (GSettings *settings,
|
|
gchar *key G_GNUC_UNUSED, ClapperAppPreferencesWindow *self)
|
|
{
|
|
GST_DEBUG ("Plugin feature ranks stored setting changed");
|
|
_update_rank_overrides (self);
|
|
}
|
|
|
|
static void
|
|
_ensure_plugins_and_features_lists (ClapperAppPreferencesWindow *self)
|
|
{
|
|
GList *feat;
|
|
GStrvBuilder *builder;
|
|
gchar **plugin_names;
|
|
const gchar *last_plugin_name = NULL;
|
|
|
|
/* Return if we were here already. Features can be NULL
|
|
* when no plugins are found at specified directory */
|
|
if (self->features || self->plugins_list)
|
|
return;
|
|
|
|
GST_DEBUG ("Reading available plugin features...");
|
|
|
|
self->features = gst_registry_feature_filter (gst_registry_get (),
|
|
(GstPluginFeatureFilter) _plugin_feature_filter_cb,
|
|
FALSE, NULL);
|
|
self->features = g_list_sort (self->features, (GCompareFunc) _compare_plugins_cb);
|
|
|
|
builder = g_strv_builder_new ();
|
|
|
|
for (feat = self->features; feat != NULL; feat = feat->next) {
|
|
GstPluginFeature *feature = GST_PLUGIN_FEATURE_CAST (feat->data);
|
|
const gchar *plugin_name = gst_plugin_feature_get_plugin_name (feature);
|
|
|
|
if (g_strcmp0 (plugin_name, last_plugin_name) != 0) {
|
|
g_strv_builder_add (builder, plugin_name);
|
|
last_plugin_name = plugin_name;
|
|
}
|
|
}
|
|
|
|
plugin_names = g_strv_builder_end (builder);
|
|
g_strv_builder_unref (builder);
|
|
|
|
GST_DEBUG ("Read all available plugin features");
|
|
|
|
self->plugins_list = gtk_string_list_new ((const gchar *const *) plugin_names);
|
|
g_strfreev (plugin_names);
|
|
}
|
|
|
|
static void
|
|
plugin_ranking_activated_cb (AdwActionRow *action_row, ClapperAppPreferencesWindow *self)
|
|
{
|
|
_ensure_plugins_and_features_lists (self);
|
|
|
|
if (!self->ranking_has_plugins_model) {
|
|
adw_combo_row_set_model (self->plugins_combo_row, G_LIST_MODEL (self->plugins_list));
|
|
adw_combo_row_set_selected (self->plugins_combo_row, GTK_INVALID_LIST_POSITION);
|
|
|
|
GST_DEBUG ("Populated plugins combo row in ranking subpage");
|
|
|
|
/* This is needed here so we will not populate plugin features row after setting
|
|
* model and unset it again after changing back to GTK_INVALID_LIST_POSITION */
|
|
self->ranking_has_plugins_model = TRUE;
|
|
}
|
|
|
|
if (self->ranks_setting_changed_id == 0) {
|
|
self->ranks_setting_changed_id = g_signal_connect (self->settings,
|
|
"changed::plugin-feature-ranks",
|
|
G_CALLBACK (plugin_feature_ranks_settings_changed_cb), self);
|
|
}
|
|
_update_rank_overrides (self);
|
|
|
|
adw_preferences_window_push_subpage (ADW_PREFERENCES_WINDOW (self), self->plugins_subpage);
|
|
}
|
|
|
|
static void
|
|
plugin_ranking_unrealize_cb (GtkWidget *widget, ClapperAppPreferencesWindow *self)
|
|
{
|
|
GString *string;
|
|
gchar *ranks_str;
|
|
guint i;
|
|
|
|
/* Since we are closing ranking subpage, disconnect this
|
|
* signal as we do not need to update widgets immediately */
|
|
if (self->ranks_setting_changed_id != 0) {
|
|
g_signal_handler_disconnect (self->settings, self->ranks_setting_changed_id);
|
|
self->ranks_setting_changed_id = 0;
|
|
}
|
|
|
|
GST_DEBUG ("Saving current rank overrides");
|
|
string = g_string_new (NULL);
|
|
|
|
for (i = 0; i < self->rank_rows->len; ++i) {
|
|
GtkWidget *spin_row = g_ptr_array_index (self->rank_rows, i);
|
|
GstRank rank;
|
|
const gchar *feature_name;
|
|
|
|
/* Insensitive are from env, we do not want to save these */
|
|
if (!gtk_widget_get_sensitive (spin_row))
|
|
continue;
|
|
|
|
rank = adw_spin_row_get_value (ADW_SPIN_ROW (spin_row));
|
|
feature_name = adw_preferences_row_get_title (ADW_PREFERENCES_ROW (spin_row));
|
|
|
|
if (string->len == 0)
|
|
g_string_append_printf (string, "%s:%i", feature_name, rank);
|
|
else
|
|
g_string_append_printf (string, ",%s:%i", feature_name, rank);
|
|
}
|
|
|
|
ranks_str = g_string_free_and_steal (string);
|
|
g_settings_set_string (self->settings, "plugin-feature-ranks", ranks_str);
|
|
g_free (ranks_str);
|
|
}
|
|
|
|
static gchar *
|
|
seek_method_name_closure (AdwEnumListItem *list_item, gpointer *user_data G_GNUC_UNUSED)
|
|
{
|
|
switch (adw_enum_list_item_get_value (list_item)) {
|
|
case CLAPPER_PLAYER_SEEK_METHOD_ACCURATE:
|
|
return g_strdup (_("Accurate"));
|
|
case CLAPPER_PLAYER_SEEK_METHOD_NORMAL:
|
|
return g_strdup (_("Normal"));
|
|
case CLAPPER_PLAYER_SEEK_METHOD_FAST:
|
|
return g_strdup (_("Fast"));
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
_get_font_mapping (GValue *value,
|
|
GVariant *variant, gpointer user_data G_GNUC_UNUSED)
|
|
{
|
|
PangoFontDescription *desc;
|
|
const gchar *desc_str = g_variant_get_string (variant, NULL);
|
|
|
|
desc = pango_font_description_from_string (desc_str);
|
|
g_value_set_boxed (value, desc);
|
|
|
|
pango_font_description_free (desc);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static GVariant *
|
|
_set_font_mapping (const GValue *value,
|
|
const GVariantType *expected_type, gpointer user_data G_GNUC_UNUSED)
|
|
{
|
|
PangoFontDescription *desc;
|
|
gchar *desc_str;
|
|
|
|
desc = (PangoFontDescription *) g_value_get_boxed (value);
|
|
desc_str = pango_font_description_to_string (desc);
|
|
|
|
return g_variant_new_take_string (desc_str);
|
|
}
|
|
|
|
GtkWidget *
|
|
clapper_app_preferences_window_new (GtkApplication *gtk_app)
|
|
{
|
|
ClapperAppPreferencesWindow *window;
|
|
|
|
window = g_object_new (CLAPPER_APP_TYPE_PREFERENCES_WINDOW,
|
|
"application", gtk_app,
|
|
"transient-for", gtk_application_get_active_window (gtk_app),
|
|
NULL);
|
|
|
|
return GTK_WIDGET (window);
|
|
}
|
|
|
|
static void
|
|
clapper_app_preferences_window_init (ClapperAppPreferencesWindow *self)
|
|
{
|
|
gtk_widget_init_template (GTK_WIDGET (self));
|
|
|
|
self->rank_rows = g_ptr_array_new ();
|
|
|
|
self->settings = g_settings_new (CLAPPER_APP_ID);
|
|
|
|
g_settings_bind (self->settings, "seek-method",
|
|
self->seek_method_combo_row, "selected", G_SETTINGS_BIND_DEFAULT);
|
|
g_settings_bind (self->settings, "seek-unit",
|
|
self->seek_unit_combo_row, "selected", G_SETTINGS_BIND_DEFAULT);
|
|
g_settings_bind (self->settings, "seek-value",
|
|
self->seek_value_spin_row, "value", G_SETTINGS_BIND_DEFAULT);
|
|
|
|
#if CLAPPER_HAVE_SERVER
|
|
g_settings_bind (self->settings, "server-enabled",
|
|
self->server_switch_row, "active", G_SETTINGS_BIND_DEFAULT);
|
|
#else
|
|
gtk_widget_set_sensitive (GTK_WIDGET (self->server_switch_row), FALSE);
|
|
#endif
|
|
|
|
g_settings_bind (self->settings, "audio-offset",
|
|
self->audio_offset_spin_row, "value", G_SETTINGS_BIND_DEFAULT);
|
|
g_settings_bind (self->settings, "subtitle-offset",
|
|
self->subtitle_offset_spin_row, "value", G_SETTINGS_BIND_DEFAULT);
|
|
g_settings_bind_with_mapping (self->settings, "subtitle-font-desc",
|
|
self->font_dialog_button, "font-desc", G_SETTINGS_BIND_DEFAULT,
|
|
(GSettingsBindGetMapping) _get_font_mapping,
|
|
(GSettingsBindSetMapping) _set_font_mapping,
|
|
NULL, NULL);
|
|
}
|
|
|
|
static void
|
|
clapper_app_preferences_window_dispose (GObject *object)
|
|
{
|
|
ClapperAppPreferencesWindow *self = CLAPPER_APP_PREFERENCES_WINDOW_CAST (object);
|
|
|
|
g_clear_pointer (&self->rank_rows, g_ptr_array_unref);
|
|
gtk_widget_dispose_template (GTK_WIDGET (self), CLAPPER_APP_TYPE_PREFERENCES_WINDOW);
|
|
|
|
G_OBJECT_CLASS (parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
clapper_app_preferences_window_finalize (GObject *object)
|
|
{
|
|
ClapperAppPreferencesWindow *self = CLAPPER_APP_PREFERENCES_WINDOW_CAST (object);
|
|
|
|
GST_TRACE_OBJECT (self, "Finalize");
|
|
|
|
g_object_unref (self->settings);
|
|
|
|
g_clear_list (&self->enhancer_pspec_rows, NULL);
|
|
g_clear_object (&self->plugins_list);
|
|
|
|
if (self->features)
|
|
gst_plugin_feature_list_free (self->features);
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
clapper_app_preferences_get_property (GObject *object, guint prop_id,
|
|
GValue *value, GParamSpec *pspec)
|
|
{
|
|
ClapperAppPreferencesWindow *self = CLAPPER_APP_PREFERENCES_WINDOW_CAST (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_RANK_ROWS:
|
|
g_value_set_boxed (value, self->rank_rows);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
clapper_app_preferences_window_class_init (ClapperAppPreferencesWindowClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = (GObjectClass *) klass;
|
|
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
|
|
|
|
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clapperapppreferenceswindow", 0,
|
|
"Clapper App Preferences Window");
|
|
|
|
gobject_class->get_property = clapper_app_preferences_get_property;
|
|
gobject_class->dispose = clapper_app_preferences_window_dispose;
|
|
gobject_class->finalize = clapper_app_preferences_window_finalize;
|
|
|
|
gtk_widget_class_set_template_from_resource (widget_class,
|
|
CLAPPER_APP_RESOURCE_PREFIX "/ui/clapper-app-preferences-window.ui");
|
|
|
|
param_specs[PROP_RANK_ROWS] = g_param_spec_boxed ("rank-rows",
|
|
NULL, NULL, G_TYPE_PTR_ARRAY,
|
|
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
|
|
|
g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
|
|
|
|
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, seek_method_combo_row);
|
|
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, seek_unit_combo_row);
|
|
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, seek_value_spin_row);
|
|
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, server_switch_row);
|
|
|
|
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, audio_offset_spin_row);
|
|
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, subtitle_offset_spin_row);
|
|
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, font_dialog_button);
|
|
|
|
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, enhancers_stack);
|
|
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, browse_enhancers_page);
|
|
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, no_enhancers_page);
|
|
|
|
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, enhancers_subpage);
|
|
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, enhancers_combo_row);
|
|
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, enhancer_config_group);
|
|
|
|
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, plugins_subpage);
|
|
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, plugins_combo_row);
|
|
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, features_combo_row);
|
|
gtk_widget_class_bind_template_child (widget_class, ClapperAppPreferencesWindow, overrides_group);
|
|
|
|
gtk_widget_class_bind_template_callback (widget_class, seek_method_name_closure);
|
|
|
|
gtk_widget_class_bind_template_callback (widget_class, enhancers_config_activated_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, selected_enhancer_changed_cb);
|
|
|
|
gtk_widget_class_bind_template_callback (widget_class, plugin_ranking_activated_cb);
|
|
gtk_widget_class_bind_template_callback (widget_class, plugin_ranking_unrealize_cb);
|
|
|
|
gtk_widget_class_bind_template_callback (widget_class, list_has_selection_closure);
|
|
gtk_widget_class_bind_template_callback (widget_class, ranking_features_model_closure);
|
|
gtk_widget_class_bind_template_callback (widget_class, add_override_button_sensitive_closure);
|
|
gtk_widget_class_bind_template_callback (widget_class, add_override_button_clicked_cb);
|
|
}
|