mirror of
https://github.com/Rafostar/clapper.git
synced 2025-08-30 07:42:23 +02:00
lib: Introduce Clapper GTK integration library
An easy to use GTK integration library. Meant as a GtkVideo alternative. While GtkVideo is more of a simple example of video playback under GTK4, this acts as a full-fledged video player in the form of a GtkWidget that can be placed anywhere within application. The widget offers customization options for both top header and bottom playback controls panels. The Clapper playback API is exposed under widget "player" property making it easy for the programmer to set media and control playback programically. The new library will be distributed with Clapper player. This includes public headers and GObject Introspection support. Licensed under LGPL-2.1-or-later.
This commit is contained in:
801
src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c
Normal file
801
src/lib/clapper-gtk/clapper-gtk-extra-menu-button.c
Normal file
@@ -0,0 +1,801 @@
|
||||
/* 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-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));
|
||||
gchar *sign = NULL;
|
||||
gdouble volume = g_ascii_strtod (text, &sign);
|
||||
|
||||
if (volume < 0 || volume > 200
|
||||
|| (sign && sign[0] != '\0' && sign[0] != '%'))
|
||||
return GTK_INPUT_ERROR;
|
||||
|
||||
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));
|
||||
gchar *sign = NULL;
|
||||
gdouble speed = g_ascii_strtod (text, &sign);
|
||||
|
||||
if (speed < 0.05 || speed > 2.0
|
||||
|| (sign && sign[0] != '\0' && sign[0] != 'x'))
|
||||
return GTK_INPUT_ERROR;
|
||||
|
||||
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 (¤t_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 },
|
||||
};
|
||||
|
||||
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");
|
||||
}
|
Reference in New Issue
Block a user