Files
clapper/src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c
Rafał Dzięgiel 6ca1bccd1a clapper-gtk: Ensure private types before template init
We need to ensure types that are not part of public API, otherwise GtkBuilder will fail to use them
(e81aad25a1/gtk/glade/README.glade (L15)).
2024-06-16 14:51:56 +02:00

806 lines
26 KiB
C

/* Clapper GTK Integration Library
* Copyright (C) 2024 Rafał Dzięgiel <rafostar.github@gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/**
* ClapperGtkExtraMenuButton:
*
* A menu button with extra options.
*/
#include "config.h"
#include <math.h>
#include <glib/gi18n-lib.h>
#include <clapper/clapper.h>
#include "clapper-gtk-extra-menu-button.h"
#include "clapper-gtk-stream-check-button-private.h"
#include "clapper-gtk-utils-private.h"
#define PERCENTAGE_ROUND(a) (round ((gdouble) a / 0.01) * 0.01)
#define DEFAULT_CAN_OPEN_SUBTITLES FALSE
#define GST_CAT_DEFAULT clapper_gtk_extra_menu_button_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
struct _ClapperGtkExtraMenuButton
{
GtkWidget parent;
GtkWidget *menu_button;
GtkWidget *volume_box;
GtkWidget *volume_button;
GtkWidget *volume_spin;
GtkWidget *speed_box;
GtkWidget *speed_button;
GtkWidget *speed_spin;
GtkWidget *top_separator;
GtkWidget *video_list_view;
GtkScrolledWindow *video_sw;
GtkWidget *audio_list_view;
GtkScrolledWindow *audio_sw;
GtkWidget *subtitle_list_view;
GtkScrolledWindow *subtitle_sw;
ClapperPlayer *player;
ClapperMediaItem *current_item;
GSimpleActionGroup *action_group;
gboolean mute;
GBinding *volume_binding;
GBinding *speed_binding;
GBinding *video_binding;
GBinding *audio_binding;
GBinding *subtitle_binding;
gboolean can_open_subtitles;
};
#define parent_class clapper_gtk_extra_menu_button_parent_class
G_DEFINE_TYPE (ClapperGtkExtraMenuButton, clapper_gtk_extra_menu_button, GTK_TYPE_WIDGET)
enum
{
PROP_0,
PROP_VOLUME_VISIBLE,
PROP_SPEED_VISIBLE,
PROP_CAN_OPEN_SUBTITLES,
PROP_LAST
};
enum
{
SIGNAL_OPEN_SUBTITLES,
SIGNAL_LAST
};
static GParamSpec *param_specs[PROP_LAST] = { NULL, };
static guint signals[SIGNAL_LAST] = { 0, };
static void
_set_action_enabled (ClapperGtkExtraMenuButton *self, const gchar *name, gboolean enabled)
{
GAction *action = g_action_map_lookup_action (G_ACTION_MAP (self->action_group), name);
gboolean was_enabled = g_action_get_enabled (action);
if (was_enabled == enabled)
return;
g_simple_action_set_enabled (G_SIMPLE_ACTION (action), enabled);
}
static gboolean
_double_transform_func (GBinding *binding, const GValue *from_value,
GValue *to_value, gpointer user_data G_GNUC_UNUSED)
{
gdouble val_dbl = g_value_get_double (from_value);
g_value_set_double (to_value, PERCENTAGE_ROUND (val_dbl));
return TRUE;
}
static gint
volume_spin_input_cb (GtkSpinButton *spin_button, gdouble *value, ClapperGtkExtraMenuButton *self)
{
const gchar *text = gtk_editable_get_text (GTK_EDITABLE (spin_button));
gdouble volume = g_strtod (text, NULL);
if (volume < 0)
volume = 0;
else if (volume > 200)
volume = 200;
volume /= 100.0;
if (volume > 0.99 && volume < 1.01)
volume = 1.0;
*value = volume;
return TRUE;
}
static gboolean
volume_spin_output_cb (GtkSpinButton *spin_button, ClapperGtkExtraMenuButton *self)
{
GtkAdjustment *adjustment = gtk_spin_button_get_adjustment (spin_button);
gdouble volume = gtk_adjustment_get_value (adjustment);
gchar *volume_str;
volume_str = g_strdup_printf ("%.0lf%%", volume * 100);
gtk_editable_set_text (GTK_EDITABLE (spin_button), volume_str);
g_free (volume_str);
return TRUE;
}
static void
volume_spin_changed_cb (GtkSpinButton *spin_button, ClapperGtkExtraMenuButton *self)
{
GtkAdjustment *adjustment = gtk_spin_button_get_adjustment (spin_button);
gdouble volume = gtk_adjustment_get_value (adjustment);
gtk_button_set_icon_name (GTK_BUTTON (self->volume_button),
clapper_gtk_get_icon_name_for_volume ((!self->mute) ? volume : 0));
}
static gint
speed_spin_input_cb (GtkSpinButton *spin_button, gdouble *value, ClapperGtkExtraMenuButton *self)
{
const gchar *text = gtk_editable_get_text (GTK_EDITABLE (spin_button));
gdouble speed = g_strtod (text, NULL);
if (speed < 0.05)
speed = 0.05;
else if (speed > 2.0)
speed = 2.0;
if (speed > 0.99 && speed < 1.01)
speed = 1.0;
*value = speed;
return TRUE;
}
static gboolean
speed_spin_output_cb (GtkSpinButton *spin_button, ClapperGtkExtraMenuButton *self)
{
GtkAdjustment *adjustment = gtk_spin_button_get_adjustment (spin_button);
gdouble speed = gtk_adjustment_get_value (adjustment);
gchar *speed_str;
speed_str = g_strdup_printf ("%.2lfx", speed);
gtk_editable_set_text (GTK_EDITABLE (spin_button), speed_str);
g_free (speed_str);
return TRUE;
}
static void
speed_spin_changed_cb (GtkSpinButton *spin_button, ClapperGtkExtraMenuButton *self)
{
GtkAdjustment *adjustment = gtk_spin_button_get_adjustment (spin_button);
gdouble speed = gtk_adjustment_get_value (adjustment);
gtk_button_set_icon_name (GTK_BUTTON (self->speed_button),
clapper_gtk_get_icon_name_for_speed (speed));
}
static void
visible_submenu_changed_cb (GtkPopoverMenu *popover_menu,
GParamSpec *pspec G_GNUC_UNUSED, ClapperGtkExtraMenuButton *self)
{
gchar *name = NULL;
gboolean in_video, in_audio, in_subtitles;
g_object_get (popover_menu, "visible-submenu", &name, NULL);
/* TODO: Check if we have to compare translated strings here */
GST_DEBUG ("Visible submenu changed to: \"%s\"", name);
in_video = (g_strcmp0 (name, _("Video")) == 0);
in_audio = (g_strcmp0 (name, _("Audio")) == 0);
in_subtitles = (g_strcmp0 (name, _("Subtitles")) == 0);
/* XXX: This works around the issue where popover does not adapt its
* width when navigating submenus making spin buttons unnecesary centered */
gtk_scrolled_window_set_propagate_natural_width (self->video_sw, in_video);
gtk_scrolled_window_set_propagate_natural_width (self->audio_sw, in_audio);
gtk_scrolled_window_set_propagate_natural_width (self->subtitle_sw, in_subtitles);
g_free (name);
}
static void
_subtitles_enabled_changed_cb (ClapperPlayer *player,
GParamSpec *pspec G_GNUC_UNUSED, ClapperGtkExtraMenuButton *self)
{
GAction *action = g_action_map_lookup_action (
G_ACTION_MAP (self->action_group), "subtitle-stream-enabled");
GVariant *variant = g_action_get_state (action);
gboolean was_enabled, enabled;
was_enabled = g_variant_get_boolean (variant);
enabled = clapper_player_get_subtitles_enabled (player);
g_variant_unref (variant);
if (was_enabled == enabled)
return;
variant = g_variant_ref_sink (g_variant_new_boolean (enabled));
g_simple_action_set_state (G_SIMPLE_ACTION (action), variant);
g_variant_unref (variant);
}
static void
_player_mute_changed_cb (ClapperPlayer *player,
GParamSpec *pspec G_GNUC_UNUSED, ClapperGtkExtraMenuButton *self)
{
self->mute = clapper_player_get_mute (player);
volume_spin_changed_cb (GTK_SPIN_BUTTON (self->volume_spin), self);
}
static void
_queue_current_item_changed_cb (ClapperQueue *queue,
GParamSpec *pspec G_GNUC_UNUSED, ClapperGtkExtraMenuButton *self)
{
ClapperMediaItem *current_item = clapper_queue_get_current_item (queue);
/* NOTE: This is also called after popover "map" signal */
if (gst_object_replace ((GstObject **) &self->current_item, GST_OBJECT_CAST (current_item))) {
_set_action_enabled (self, "open-subtitle-stream",
(self->can_open_subtitles && self->current_item != NULL));
}
gst_clear_object (&current_item);
}
static void
change_subtitle_stream_enabled (GSimpleAction *action, GVariant *value, gpointer user_data)
{
ClapperGtkExtraMenuButton *self = CLAPPER_GTK_EXTRA_MENU_BUTTON_CAST (user_data);
gboolean enable = g_variant_get_boolean (value);
if (G_LIKELY (self->player != NULL))
clapper_player_set_subtitles_enabled (self->player, enable);
g_simple_action_set_state (action, value);
}
static void
open_subtitle_stream (GSimpleAction *action, GVariant *param, gpointer user_data)
{
ClapperGtkExtraMenuButton *self = CLAPPER_GTK_EXTRA_MENU_BUTTON_CAST (user_data);
/* We should not be here otherwise */
if (G_LIKELY (self->can_open_subtitles && self->current_item != NULL))
g_signal_emit (self, signals[SIGNAL_OPEN_SUBTITLES], 0, self->current_item);
}
static void
_determine_top_separator_visibility (ClapperGtkExtraMenuButton *self)
{
gboolean visible = gtk_widget_get_visible (self->volume_box)
|| gtk_widget_get_visible (self->speed_box);
gtk_widget_set_visible (self->top_separator, visible);
}
/**
* clapper_gtk_extra_menu_button_new:
*
* Creates a new #ClapperGtkExtraMenuButton instance.
*
* Returns: a new extra menu button #GtkWidget.
*/
GtkWidget *
clapper_gtk_extra_menu_button_new (void)
{
return g_object_new (CLAPPER_GTK_TYPE_EXTRA_MENU_BUTTON, NULL);
}
/**
* clapper_gtk_extra_menu_button_set_volume_visible:
* @button: a #ClapperGtkExtraMenuButton
* @visible: whether visible
*
* Set whether volume control inside popover should be visible.
*/
void
clapper_gtk_extra_menu_button_set_volume_visible (ClapperGtkExtraMenuButton *self, gboolean visible)
{
g_return_if_fail (CLAPPER_GTK_IS_EXTRA_MENU_BUTTON (self));
if (gtk_widget_get_visible (self->volume_box) != visible) {
gtk_widget_set_visible (self->volume_box, visible);
_determine_top_separator_visibility (self);
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_VOLUME_VISIBLE]);
}
}
/**
* clapper_gtk_extra_menu_button_get_volume_visible:
* @button: a #ClapperGtkExtraMenuButton
*
* Get whether volume control inside popover is visible.
*
* Returns: TRUE if volume control is visible, %FALSE otherwise.
*/
gboolean
clapper_gtk_extra_menu_button_get_volume_visible (ClapperGtkExtraMenuButton *self)
{
g_return_val_if_fail (CLAPPER_GTK_IS_EXTRA_MENU_BUTTON (self), FALSE);
return gtk_widget_get_visible (self->volume_box);
}
/**
* clapper_gtk_extra_menu_button_set_speed_visible:
* @button: a #ClapperGtkExtraMenuButton
* @visible: whether visible
*
* Set whether speed control inside popover should be visible.
*/
void
clapper_gtk_extra_menu_button_set_speed_visible (ClapperGtkExtraMenuButton *self, gboolean visible)
{
g_return_if_fail (CLAPPER_GTK_IS_EXTRA_MENU_BUTTON (self));
if (gtk_widget_get_visible (self->speed_box) != visible) {
gtk_widget_set_visible (self->speed_box, visible);
_determine_top_separator_visibility (self);
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_SPEED_VISIBLE]);
}
}
/**
* clapper_gtk_extra_menu_button_get_speed_visible:
* @button: a #ClapperGtkExtraMenuButton
*
* Get whether speed control inside popover is visible.
*
* Returns: %TRUE if speed control is visible, %FALSE otherwise.
*/
gboolean
clapper_gtk_extra_menu_button_get_speed_visible (ClapperGtkExtraMenuButton *self)
{
g_return_val_if_fail (CLAPPER_GTK_IS_EXTRA_MENU_BUTTON (self), FALSE);
return gtk_widget_get_visible (self->speed_box);
}
/**
* clapper_gtk_extra_menu_button_set_can_open_subtitles:
* @button: a #ClapperGtkExtraMenuButton
* @allowed: whether opening subtitles should be allowed
*
* Set whether an option to open external subtitle stream should be allowed.
*
* Note that this [class@Gtk.Widget] can only add subtitles to currently playing
* [class@Clapper.MediaItem]. When no media is selected, option to open subtitles
* will not be shown regardless how this option is set.
*/
void
clapper_gtk_extra_menu_button_set_can_open_subtitles (ClapperGtkExtraMenuButton *self, gboolean allowed)
{
gboolean changed;
g_return_if_fail (CLAPPER_GTK_IS_EXTRA_MENU_BUTTON (self));
if ((changed = self->can_open_subtitles != allowed)) {
self->can_open_subtitles = allowed;
_set_action_enabled (self, "open-subtitle-stream",
(self->can_open_subtitles && self->current_item != NULL));
g_object_notify_by_pspec (G_OBJECT (self), param_specs[PROP_CAN_OPEN_SUBTITLES]);
}
}
/**
* clapper_gtk_extra_menu_button_get_can_open_subtitles:
* @button: a #ClapperGtkExtraMenuButton
*
* Get whether an option to open external subtitle stream inside popover is visible.
*
* Returns: %TRUE if open subtitles is visible, %FALSE otherwise.
*/
gboolean
clapper_gtk_extra_menu_button_get_can_open_subtitles (ClapperGtkExtraMenuButton *self)
{
g_return_val_if_fail (CLAPPER_GTK_IS_EXTRA_MENU_BUTTON (self), FALSE);
return self->can_open_subtitles;
}
static void
clapper_gtk_extra_menu_button_init (ClapperGtkExtraMenuButton *self)
{
static GActionEntry action_entries[] = {
{ "subtitle-stream-enabled", NULL, NULL, "true", change_subtitle_stream_enabled },
{ "open-subtitle-stream", open_subtitle_stream, NULL, NULL, NULL },
};
/* Ensure private types */
g_type_ensure (CLAPPER_GTK_TYPE_STREAM_CHECK_BUTTON);
gtk_widget_init_template (GTK_WIDGET (self));
self->action_group = g_simple_action_group_new ();
g_object_bind_property (self, "css-classes", self->menu_button,
"css-classes", G_BINDING_DEFAULT);
g_action_map_add_action_entries (G_ACTION_MAP (self->action_group),
action_entries, G_N_ELEMENTS (action_entries), self);
gtk_widget_insert_action_group (GTK_WIDGET (self),
"clappergtk", G_ACTION_GROUP (self->action_group));
/* Set default values */
self->can_open_subtitles = DEFAULT_CAN_OPEN_SUBTITLES;
_set_action_enabled (self, "open-subtitle-stream", self->can_open_subtitles);
}
static void
clapper_gtk_extra_menu_button_compute_expand (GtkWidget *widget,
gboolean *hexpand_p, gboolean *vexpand_p)
{
GtkWidget *child;
gboolean hexpand = FALSE;
gboolean vexpand = FALSE;
if ((child = gtk_widget_get_first_child (widget))) {
hexpand = gtk_widget_compute_expand (child, GTK_ORIENTATION_HORIZONTAL);
vexpand = gtk_widget_compute_expand (child, GTK_ORIENTATION_VERTICAL);
}
*hexpand_p = hexpand;
*vexpand_p = vexpand;
}
static void
clapper_gtk_extra_menu_button_realize (GtkWidget *widget)
{
ClapperGtkExtraMenuButton *self = CLAPPER_GTK_EXTRA_MENU_BUTTON_CAST (widget);
GST_TRACE_OBJECT (self, "Realize");
if ((self->player = clapper_gtk_get_player_from_ancestor (GTK_WIDGET (self)))) {
ClapperStreamList *stream_list;
GtkSingleSelection *selection;
g_signal_connect (self->player, "notify::mute",
G_CALLBACK (_player_mute_changed_cb), self);
self->mute = clapper_player_get_mute (self->player);
stream_list = clapper_player_get_video_streams (self->player);
selection = gtk_single_selection_new (gst_object_ref (stream_list));
gtk_single_selection_set_autoselect (selection, FALSE);
self->video_binding = g_object_bind_property (stream_list, "current-index",
selection, "selected", G_BINDING_SYNC_CREATE);
gtk_list_view_set_model (GTK_LIST_VIEW (self->video_list_view),
GTK_SELECTION_MODEL (selection));
g_object_unref (selection);
stream_list = clapper_player_get_audio_streams (self->player);
selection = gtk_single_selection_new (gst_object_ref (stream_list));
gtk_single_selection_set_autoselect (selection, FALSE);
self->audio_binding = g_object_bind_property (stream_list, "current-index",
selection, "selected", G_BINDING_SYNC_CREATE);
gtk_list_view_set_model (GTK_LIST_VIEW (self->audio_list_view),
GTK_SELECTION_MODEL (selection));
g_object_unref (selection);
stream_list = clapper_player_get_subtitle_streams (self->player);
selection = gtk_single_selection_new (gst_object_ref (stream_list));
gtk_single_selection_set_autoselect (selection, FALSE);
self->subtitle_binding = g_object_bind_property (stream_list, "current-index",
selection, "selected", G_BINDING_SYNC_CREATE);
gtk_list_view_set_model (GTK_LIST_VIEW (self->subtitle_list_view),
GTK_SELECTION_MODEL (selection));
g_object_unref (selection);
}
GTK_WIDGET_CLASS (parent_class)->realize (widget);
}
static void
clapper_gtk_extra_menu_button_unrealize (GtkWidget *widget)
{
ClapperGtkExtraMenuButton *self = CLAPPER_GTK_EXTRA_MENU_BUTTON_CAST (widget);
GST_TRACE_OBJECT (self, "Unrealize");
g_clear_pointer (&self->video_binding, g_binding_unbind);
g_clear_pointer (&self->audio_binding, g_binding_unbind);
g_clear_pointer (&self->subtitle_binding, g_binding_unbind);
gtk_list_view_set_model (GTK_LIST_VIEW (self->video_list_view), NULL);
gtk_list_view_set_model (GTK_LIST_VIEW (self->audio_list_view), NULL);
gtk_list_view_set_model (GTK_LIST_VIEW (self->subtitle_list_view), NULL);
if (self->player) {
g_signal_handlers_disconnect_by_func (self->player, _player_mute_changed_cb, self);
self->player = NULL;
}
GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
}
static void
popover_map_cb (GtkWidget *widget, ClapperGtkExtraMenuButton *self)
{
ClapperQueue *queue;
GST_TRACE_OBJECT (self, "Popover map");
if (G_UNLIKELY (self->player == NULL))
return;
queue = clapper_player_get_queue (self->player);
self->volume_binding = g_object_bind_property_full (self->player, "volume",
self->volume_spin, "value",
G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE,
(GBindingTransformFunc) _double_transform_func,
(GBindingTransformFunc) _double_transform_func,
NULL, NULL);
self->speed_binding = g_object_bind_property_full (self->player, "speed",
self->speed_spin, "value",
G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE,
(GBindingTransformFunc) _double_transform_func,
(GBindingTransformFunc) _double_transform_func,
NULL, NULL);
g_signal_connect (self->player, "notify::subtitles-enabled",
G_CALLBACK (_subtitles_enabled_changed_cb), self);
_subtitles_enabled_changed_cb (self->player, NULL, self);
g_signal_connect (queue, "notify::current-item",
G_CALLBACK (_queue_current_item_changed_cb), self);
_queue_current_item_changed_cb (queue, NULL, self);
}
static void
popover_unmap_cb (GtkWidget *widget, ClapperGtkExtraMenuButton *self)
{
ClapperQueue *queue;
GST_TRACE_OBJECT (self, "Popover unmap");
if (G_UNLIKELY (self->player == NULL))
return;
queue = clapper_player_get_queue (self->player);
g_clear_pointer (&self->volume_binding, g_binding_unbind);
g_clear_pointer (&self->speed_binding, g_binding_unbind);
g_signal_handlers_disconnect_by_func (self->player, _subtitles_enabled_changed_cb, self);
g_signal_handlers_disconnect_by_func (queue, _queue_current_item_changed_cb, self);
}
static void
clapper_gtk_extra_menu_button_dispose (GObject *object)
{
ClapperGtkExtraMenuButton *self = CLAPPER_GTK_EXTRA_MENU_BUTTON_CAST (object);
gtk_widget_dispose_template (GTK_WIDGET (object), CLAPPER_GTK_TYPE_EXTRA_MENU_BUTTON);
g_clear_pointer (&self->menu_button, gtk_widget_unparent);
G_OBJECT_CLASS (parent_class)->dispose (object);
}
static void
clapper_gtk_extra_menu_button_finalize (GObject *object)
{
ClapperGtkExtraMenuButton *self = CLAPPER_GTK_EXTRA_MENU_BUTTON_CAST (object);
GST_TRACE_OBJECT (self, "Finalize");
gst_clear_object (&self->current_item);
g_object_unref (self->action_group);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
clapper_gtk_extra_menu_button_get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
ClapperGtkExtraMenuButton *self = CLAPPER_GTK_EXTRA_MENU_BUTTON_CAST (object);
switch (prop_id) {
case PROP_VOLUME_VISIBLE:
g_value_set_boolean (value, clapper_gtk_extra_menu_button_get_volume_visible (self));
break;
case PROP_SPEED_VISIBLE:
g_value_set_boolean (value, clapper_gtk_extra_menu_button_get_speed_visible (self));
break;
case PROP_CAN_OPEN_SUBTITLES:
g_value_set_boolean (value, clapper_gtk_extra_menu_button_get_can_open_subtitles (self));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_gtk_extra_menu_button_set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
ClapperGtkExtraMenuButton *self = CLAPPER_GTK_EXTRA_MENU_BUTTON_CAST (object);
switch (prop_id) {
case PROP_VOLUME_VISIBLE:
clapper_gtk_extra_menu_button_set_volume_visible (self, g_value_get_boolean (value));
break;
case PROP_SPEED_VISIBLE:
clapper_gtk_extra_menu_button_set_speed_visible (self, g_value_get_boolean (value));
break;
case PROP_CAN_OPEN_SUBTITLES:
clapper_gtk_extra_menu_button_set_can_open_subtitles (self, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
clapper_gtk_extra_menu_button_class_init (ClapperGtkExtraMenuButtonClass *klass)
{
GObjectClass *gobject_class = (GObjectClass *) klass;
GtkWidgetClass *widget_class = (GtkWidgetClass *) klass;
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "clappergtkextramenubutton", 0,
"Clapper GTK Extra Menu Button");
clapper_gtk_init_translations ();
gobject_class->get_property = clapper_gtk_extra_menu_button_get_property;
gobject_class->set_property = clapper_gtk_extra_menu_button_set_property;
gobject_class->dispose = clapper_gtk_extra_menu_button_dispose;
gobject_class->finalize = clapper_gtk_extra_menu_button_finalize;
widget_class->compute_expand = clapper_gtk_extra_menu_button_compute_expand;
widget_class->realize = clapper_gtk_extra_menu_button_realize;
widget_class->unrealize = clapper_gtk_extra_menu_button_unrealize;
/**
* ClapperGtkExtraMenuButton:volume-visible:
*
* Visibility of volume control inside popover.
*/
param_specs[PROP_VOLUME_VISIBLE] = g_param_spec_boolean ("volume-visible",
NULL, NULL, TRUE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperGtkExtraMenuButton:speed-visible:
*
* Visibility of speed control inside popover.
*/
param_specs[PROP_SPEED_VISIBLE] = g_param_spec_boolean ("speed-visible",
NULL, NULL, TRUE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperGtkExtraMenuButton:can-open-subtitles:
*
* Visibility of open subtitles option inside popover.
*/
param_specs[PROP_CAN_OPEN_SUBTITLES] = g_param_spec_boolean ("can-open-subtitles",
NULL, NULL, FALSE,
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
/**
* ClapperGtkExtraMenuButton::open-subtitles:
* @button: a #ClapperGtkExtraMenuButton
* @item: a #ClapperMediaItem
*
* A signal that user wants to open subtitles file.
*
* Implementation should add a way for user to select subtitles to open
* such as by e.g. using [class@Gtk.FileDialog] and then add them to the
* @item using [method@Clapper.MediaItem.set_suburi] method.
*
* This signal will pass the [class@Clapper.MediaItem] that was current when
* user clicked the open button and subtitles should be added to this @item.
* This avoids situations where another item starts playing before user selects
* subtitles file to be opened. When using asynchronous operations to open file,
* implementation should [method@GObject.Object.ref] the item to ensure that it
* stays valid until finish.
*
* Note that this signal will not be emitted if open button is not visible by
* setting [method@ClapperGtk.ExtraMenuButton.set_can_open_subtitles] to %TRUE,
* so you do not have to implement handler for it otherwise.
*/
signals[SIGNAL_OPEN_SUBTITLES] = g_signal_new ("open-subtitles",
G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
0, NULL, NULL, NULL, G_TYPE_NONE, 1, CLAPPER_TYPE_MEDIA_ITEM);
g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
gtk_widget_class_set_template_from_resource (widget_class,
CLAPPER_GTK_RESOURCE_PREFIX "/ui/clapper-gtk-extra-menu-button.ui");
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, menu_button);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, volume_box);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, volume_button);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, volume_spin);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, speed_box);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, speed_button);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, speed_spin);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, top_separator);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, video_list_view);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, video_sw);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, audio_list_view);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, audio_sw);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, subtitle_list_view);
gtk_widget_class_bind_template_child (widget_class, ClapperGtkExtraMenuButton, subtitle_sw);
gtk_widget_class_bind_template_callback (widget_class, volume_spin_input_cb);
gtk_widget_class_bind_template_callback (widget_class, volume_spin_output_cb);
gtk_widget_class_bind_template_callback (widget_class, volume_spin_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, speed_spin_input_cb);
gtk_widget_class_bind_template_callback (widget_class, speed_spin_output_cb);
gtk_widget_class_bind_template_callback (widget_class, speed_spin_changed_cb);
gtk_widget_class_bind_template_callback (widget_class, popover_map_cb);
gtk_widget_class_bind_template_callback (widget_class, popover_unmap_cb);
gtk_widget_class_bind_template_callback (widget_class, visible_submenu_changed_cb);
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_BUTTON);
gtk_widget_class_set_css_name (widget_class, "clapper-gtk-extra-menu-button");
}