Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-3.0-or-later
2 : : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
3 : :
4 : : #define G_LOG_DOMAIN "valent-media-remote"
5 : :
6 : : #include "config.h"
7 : :
8 : : #include <adwaita.h>
9 : : #include <glib/gi18n-lib.h>
10 : : #include <gtk/gtk.h>
11 : : #include <libvalent-core.h>
12 : : #include <libvalent-media.h>
13 : :
14 : : #include "valent-media-remote.h"
15 : : #include "valent-ui-utils.h"
16 : : #include "valent-ui-utils-private.h"
17 : :
18 : : /* Time (ms) to delay the seek command when moving the position slider. Minimal
19 : : * testing indicates values in the 50-100ms work well. */
20 : : #define MEDIA_SEEK_DELAY (75)
21 : :
22 : :
23 : : struct _ValentMediaRemote
24 : : {
25 : : AdwWindow parent_instance;
26 : :
27 : : GListModel *players;
28 : : ValentMediaPlayer *player;
29 : : unsigned int timer_id;
30 : : unsigned int seek_id;
31 : :
32 : : /* template */
33 : : GtkDropDown *media_player;
34 : : GtkStack *media_art_stack;
35 : : GtkImage *media_art;
36 : : GtkLabel *media_title;
37 : : GtkLabel *media_artist;
38 : : GtkLabel *media_album;
39 : : GtkScale *media_position;
40 : : GtkAdjustment *media_position_adjustment;
41 : : GtkLabel *media_position_current;
42 : : GtkLabel *media_position_length;
43 : : GtkButton *play_pause_button;
44 : : GtkImage *repeat_button;
45 : : GtkImage *repeat_image;
46 : : GtkVolumeButton *volume_button;
47 : : };
48 : :
49 [ + + + - ]: 142 : G_DEFINE_FINAL_TYPE (ValentMediaRemote, valent_media_remote, ADW_TYPE_WINDOW)
50 : :
51 : : enum {
52 : : PROP_0,
53 : : PROP_PLAYERS,
54 : : PROP_SHUFFLE,
55 : : N_PROPERTIES
56 : : };
57 : :
58 : : static GParamSpec *properties[N_PROPERTIES] = { NULL, };
59 : :
60 : :
61 : : static gboolean
62 : 0 : valent_media_remote_timer_tick (gpointer data)
63 : : {
64 : 0 : ValentMediaRemote *self = VALENT_MEDIA_REMOTE (data);
65 : 0 : g_autofree char *length = NULL;
66 : 0 : g_autofree char *current = NULL;
67 : 0 : double value = 0.0;
68 : 0 : double upper = 0.0;
69 : :
70 [ # # ]: 0 : g_assert (VALENT_IS_MEDIA_REMOTE (self));
71 : :
72 : 0 : value = gtk_adjustment_get_value (self->media_position_adjustment);
73 : 0 : upper = gtk_adjustment_get_upper (self->media_position_adjustment);
74 : :
75 : 0 : current = valent_media_time_to_string (value * 1000L, TOTEM_TIME_FLAG_NONE);
76 : 0 : gtk_label_set_label (self->media_position_current, current);
77 : :
78 : 0 : length = valent_media_time_to_string (upper * 1000L, TOTEM_TIME_FLAG_NONE);
79 : 0 : gtk_label_set_label (self->media_position_length, length);
80 : :
81 : 0 : gtk_adjustment_set_value (self->media_position_adjustment, value + 1.0);
82 : :
83 : 0 : return G_SOURCE_CONTINUE;
84 : : }
85 : :
86 : : /*
87 : : * Interface
88 : : */
89 : : static void
90 : 2 : valent_media_remote_clear (ValentMediaRemote *self)
91 : : {
92 : 2 : GtkWidget *widget = GTK_WIDGET (self);
93 : :
94 : 2 : gtk_image_set_from_icon_name (self->media_art, "valent-media-albumart-symbolic");
95 : :
96 : 2 : gtk_stack_set_visible_child_name (self->media_art_stack, "fallback");
97 : 2 : gtk_label_set_label (self->media_artist, "");
98 : 2 : gtk_label_set_label (self->media_title, "");
99 : 2 : gtk_label_set_label (self->media_album, "");
100 : :
101 : 2 : gtk_adjustment_set_value (self->media_position_adjustment, 0.0);
102 : 2 : gtk_adjustment_set_upper (self->media_position_adjustment, 0.0);
103 : 2 : gtk_label_set_label (self->media_position_current, "");
104 : 2 : gtk_label_set_label (self->media_position_length, "");
105 : :
106 : 2 : gtk_widget_action_set_enabled (widget, "remote.next", FALSE);
107 : 2 : gtk_widget_action_set_enabled (widget, "remote.pause", FALSE);
108 : 2 : gtk_widget_action_set_enabled (widget, "remote.play", FALSE);
109 : 2 : gtk_widget_action_set_enabled (widget, "remote.previous", FALSE);
110 : 2 : gtk_widget_action_set_enabled (widget, "remote.seek", FALSE);
111 : 2 : gtk_widget_action_set_enabled (widget, "remote.stop", FALSE);
112 : 2 : }
113 : :
114 : : static void
115 : 6 : valent_media_remote_update_flags (ValentMediaRemote *self)
116 : : {
117 : 6 : GtkWidget *widget = GTK_WIDGET (self);
118 : 6 : ValentMediaActions flags = VALENT_MEDIA_ACTION_NONE;
119 : :
120 [ + - ]: 6 : g_assert (VALENT_IS_MEDIA_REMOTE (self));
121 : :
122 [ - + ]: 6 : if (self->player == NULL)
123 : 0 : return valent_media_remote_clear (self);
124 : :
125 : 6 : flags = valent_media_player_get_flags (self->player);
126 : :
127 : 6 : gtk_widget_action_set_enabled (widget, "remote.next",
128 : 6 : (flags & VALENT_MEDIA_ACTION_NEXT) != 0);
129 : 6 : gtk_widget_action_set_enabled (widget, "remote.pause",
130 : 6 : (flags & VALENT_MEDIA_ACTION_PAUSE) != 0);
131 : 6 : gtk_widget_action_set_enabled (widget, "remote.play",
132 : : (flags & VALENT_MEDIA_ACTION_PLAY) != 0);
133 : 6 : gtk_widget_action_set_enabled (widget, "remote.previous",
134 : 6 : (flags & VALENT_MEDIA_ACTION_PREVIOUS) != 0);
135 : 6 : gtk_widget_action_set_enabled (widget, "remote.seek",
136 : 6 : (flags & VALENT_MEDIA_ACTION_SEEK) != 0);
137 : 6 : gtk_widget_action_set_enabled (widget, "remote.stop",
138 : 6 : (flags & VALENT_MEDIA_ACTION_STOP) != 0);
139 : : }
140 : :
141 : : static void
142 : 18 : valent_media_remote_update_position (ValentMediaRemote *self)
143 : : {
144 : 18 : double position = 0.0;
145 : 18 : g_autofree char *position_str = NULL;
146 : :
147 [ + - ]: 18 : g_assert (VALENT_IS_MEDIA_REMOTE (self));
148 : :
149 [ - + ]: 18 : if (self->player == NULL)
150 : 0 : return valent_media_remote_clear (self);
151 : :
152 : 18 : position = valent_media_player_get_position (self->player);
153 : 18 : gtk_adjustment_set_value (self->media_position_adjustment, position);
154 : :
155 : 18 : position_str = valent_media_time_to_string (position * 1000L, TOTEM_TIME_FLAG_NONE);
156 : 18 : gtk_label_set_label (self->media_position_current, position_str);
157 : : }
158 : :
159 : : static void
160 : 15 : valent_media_remote_update_metadata (ValentMediaRemote *self)
161 : : {
162 : 15 : g_autoptr (GVariant) metadata = NULL;
163 [ + - ]: 15 : g_autoptr (GIcon) icon = NULL;
164 [ - + ]: 15 : g_autofree const char **artists = NULL;
165 : 15 : g_autofree char *length_str = NULL;
166 : 15 : const char *title;
167 : 15 : const char *album;
168 : 15 : const char *art_url;
169 : 15 : int64_t length_us = 0;
170 : 15 : double length = -1.0;
171 : :
172 [ + - ]: 15 : g_assert (VALENT_IS_MEDIA_REMOTE (self));
173 : :
174 [ - + ]: 15 : if (self->player == NULL)
175 : 0 : return valent_media_remote_clear (self);
176 : :
177 : 15 : metadata = valent_media_player_get_metadata (self->player);
178 : :
179 [ + + ]: 15 : if (g_variant_lookup (metadata, "xesam:artist", "^a&s", &artists) &&
180 [ + - + - ]: 10 : artists[0] != NULL && *artists[0] != '\0')
181 : 10 : {
182 : 10 : g_autofree char *artist = NULL;
183 : :
184 : 10 : artist = g_strjoinv (", ", (char **)artists);
185 : 10 : gtk_label_set_label (self->media_artist, artist);
186 : : }
187 : : else
188 : : {
189 : 5 : gtk_label_set_label (self->media_artist, "");
190 : : }
191 : :
192 [ + + ]: 15 : if (g_variant_lookup (metadata, "xesam:album", "&s", &album))
193 : 10 : gtk_label_set_label (self->media_album, album);
194 : : else
195 : 5 : gtk_label_set_label (self->media_album, "");
196 : :
197 [ + + ]: 15 : if (g_variant_lookup (metadata, "xesam:title", "&s", &title))
198 : 10 : gtk_label_set_label (self->media_title, title);
199 : : else
200 : 5 : gtk_label_set_label (self->media_title, "");
201 : :
202 [ - + ]: 15 : if (g_variant_lookup (metadata, "mpris:artUrl", "&s", &art_url))
203 : : {
204 : 15 : g_autoptr (GFile) file = NULL;
205 : :
206 : 0 : file = g_file_new_for_uri (art_url);
207 : :
208 [ # # ]: 0 : if (g_file_query_exists (file, NULL))
209 : 0 : icon = g_file_icon_new (file);
210 : : }
211 : :
212 : 15 : gtk_image_set_from_gicon (self->media_art, icon);
213 [ + - ]: 30 : gtk_stack_set_visible_child_name (self->media_art_stack,
214 : : icon != NULL ? "art" : "fallback");
215 : :
216 : : /* Convert microseconds to seconds */
217 [ + + ]: 15 : if (g_variant_lookup (metadata, "mpris:length", "x", &length_us))
218 : 10 : length = length_us / G_TIME_SPAN_SECOND;
219 : :
220 : 15 : gtk_adjustment_set_upper (self->media_position_adjustment, length);
221 : 15 : length_str = valent_media_time_to_string (length * 1000L, TOTEM_TIME_FLAG_NONE);
222 : 15 : gtk_label_set_label (self->media_position_length, length_str);
223 : :
224 : 15 : valent_media_remote_update_position (self);
225 : : }
226 : :
227 : : static void
228 : 4 : valent_media_remote_update_repeat (ValentMediaRemote *self)
229 : : {
230 : 4 : ValentMediaRepeat repeat = VALENT_MEDIA_REPEAT_NONE;
231 : :
232 [ + - ]: 4 : g_assert (VALENT_IS_MEDIA_REMOTE (self));
233 : :
234 [ + - ]: 4 : if (self->player != NULL)
235 : 4 : repeat = valent_media_player_get_repeat (self->player);
236 : :
237 [ + + + - ]: 4 : switch (repeat)
238 : : {
239 : 2 : case VALENT_MEDIA_REPEAT_NONE:
240 : 2 : gtk_image_set_from_icon_name (self->repeat_image,
241 : : "media-playlist-consecutive-symbolic");
242 : 4 : gtk_accessible_update_property (GTK_ACCESSIBLE (self->repeat_button),
243 : 2 : GTK_ACCESSIBLE_PROPERTY_LABEL, _("Enable Repeat"),
244 : : -1);
245 : 2 : break;
246 : :
247 : 1 : case VALENT_MEDIA_REPEAT_ALL:
248 : 1 : gtk_image_set_from_icon_name (self->repeat_image,
249 : : "media-playlist-repeat-symbolic");
250 : 2 : gtk_accessible_update_property (GTK_ACCESSIBLE (self->repeat_button),
251 : 1 : GTK_ACCESSIBLE_PROPERTY_LABEL, _("Repeat All"),
252 : : -1);
253 : 1 : break;
254 : :
255 : 1 : case VALENT_MEDIA_REPEAT_ONE:
256 : 1 : gtk_image_set_from_icon_name (self->repeat_image,
257 : : "media-playlist-repeat-song-symbolic");
258 : 2 : gtk_accessible_update_property (GTK_ACCESSIBLE (self->repeat_button),
259 : 1 : GTK_ACCESSIBLE_PROPERTY_LABEL, _("Repeat One"),
260 : : -1);
261 : 1 : break;
262 : : }
263 : 4 : }
264 : :
265 : : static void
266 : 9 : valent_media_remote_update_state (ValentMediaRemote *self)
267 : : {
268 : 9 : GtkWidget *child;
269 : 9 : ValentMediaState state;
270 : :
271 [ + - ]: 9 : g_assert (VALENT_IS_MEDIA_REMOTE (self));
272 : :
273 [ - + ]: 9 : if (self->player == NULL)
274 : 0 : return valent_media_remote_clear (self);
275 : :
276 : 9 : child = gtk_button_get_child (self->play_pause_button);
277 : 9 : state = valent_media_player_get_state (self->player);
278 : :
279 [ + + ]: 9 : if (state == VALENT_MEDIA_STATE_PLAYING)
280 : : {
281 : 2 : gtk_actionable_set_action_name (GTK_ACTIONABLE (self->play_pause_button),
282 : : "remote.pause");
283 : 2 : gtk_image_set_from_icon_name (GTK_IMAGE (child),
284 : : "media-playback-pause-symbolic");
285 : 2 : gtk_accessible_update_property (GTK_ACCESSIBLE (self->play_pause_button),
286 : 2 : GTK_ACCESSIBLE_PROPERTY_LABEL, _("Pause"),
287 : : -1);
288 : :
289 [ + + ]: 2 : if (self->timer_id == 0)
290 : 1 : self->timer_id = g_timeout_add_seconds (1, valent_media_remote_timer_tick, self);
291 : : }
292 : : else
293 : : {
294 : 7 : gtk_actionable_set_action_name (GTK_ACTIONABLE (self->play_pause_button),
295 : : "remote.play");
296 : 7 : gtk_image_set_from_icon_name (GTK_IMAGE (child),
297 : : "media-playback-start-symbolic");
298 : 7 : gtk_widget_set_tooltip_text (GTK_WIDGET (self->play_pause_button),
299 : 7 : _("Play"));
300 : 7 : gtk_accessible_update_property (GTK_ACCESSIBLE (self->play_pause_button),
301 : 7 : GTK_ACCESSIBLE_PROPERTY_LABEL, _("Play"),
302 : : -1);
303 [ + + ]: 7 : g_clear_handle_id (&self->timer_id, g_source_remove);
304 : : }
305 : :
306 [ + + ]: 9 : if (state == VALENT_MEDIA_STATE_STOPPED)
307 : : {
308 : 3 : g_object_freeze_notify (G_OBJECT (self->media_position_adjustment));
309 : 3 : gtk_adjustment_set_value (self->media_position_adjustment, 0.0);
310 : 3 : gtk_adjustment_set_upper (self->media_position_adjustment, 0.0);
311 : 3 : g_object_thaw_notify (G_OBJECT (self->media_position_adjustment));
312 : : }
313 : :
314 : 9 : valent_media_remote_update_metadata (self);
315 : : }
316 : :
317 : : static void
318 : 3 : valent_media_remote_update_shuffle (ValentMediaRemote *self)
319 : : {
320 [ + - ]: 3 : g_assert (VALENT_IS_MEDIA_REMOTE (self));
321 : :
322 : 3 : g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SHUFFLE]);
323 : 3 : }
324 : :
325 : : static void
326 : 1 : valent_media_remote_update_volume (ValentMediaRemote *self)
327 : : {
328 : 1 : double volume = 0.0;
329 : :
330 [ + - ]: 1 : g_assert (VALENT_IS_MEDIA_REMOTE (self));
331 : :
332 [ + - ]: 1 : if (self->player == NULL)
333 : : return;
334 : :
335 : 1 : volume = valent_media_player_get_volume (self->player);
336 : 1 : gtk_scale_button_set_value (GTK_SCALE_BUTTON (self->volume_button), volume);
337 : : }
338 : :
339 : : static void
340 : 3 : on_selected_item (GObject *object,
341 : : GParamSpec *pspec,
342 : : ValentMediaRemote *self)
343 : : {
344 : 3 : ValentMediaPlayer *player;
345 : :
346 [ + - ]: 3 : g_assert (VALENT_IS_MEDIA_REMOTE (self));
347 : :
348 [ + + ]: 3 : if (self->player != NULL)
349 : : {
350 [ - + ]: 1 : g_clear_handle_id (&self->seek_id, g_source_remove);
351 [ - + ]: 1 : g_clear_handle_id (&self->timer_id, g_source_remove);
352 : 1 : g_signal_handlers_disconnect_by_data (self->player, self);
353 [ + - ]: 1 : g_clear_object (&self->player);
354 : : }
355 : :
356 : 3 : player = gtk_drop_down_get_selected_item (GTK_DROP_DOWN (object));
357 : :
358 [ + + ]: 3 : if (g_set_object (&self->player, player))
359 : : {
360 : 1 : g_signal_connect_object (self->player,
361 : : "notify::flags",
362 : : G_CALLBACK (valent_media_remote_update_flags),
363 : : self, G_CONNECT_SWAPPED);
364 : 1 : g_signal_connect_object (self->player,
365 : : "notify::metadata",
366 : : G_CALLBACK (valent_media_remote_update_metadata),
367 : : self, G_CONNECT_SWAPPED);
368 : 1 : g_signal_connect_object (self->player,
369 : : "notify::position",
370 : : G_CALLBACK (valent_media_remote_update_position),
371 : : self, G_CONNECT_SWAPPED);
372 : 1 : g_signal_connect_object (self->player,
373 : : "notify::repeat",
374 : : G_CALLBACK (valent_media_remote_update_repeat),
375 : : self, G_CONNECT_SWAPPED);
376 : 1 : g_signal_connect_object (self->player,
377 : : "notify::shuffle",
378 : : G_CALLBACK (valent_media_remote_update_shuffle),
379 : : self, G_CONNECT_SWAPPED);
380 : 1 : g_signal_connect_object (self->player,
381 : : "notify::state",
382 : : G_CALLBACK (valent_media_remote_update_state),
383 : : self, G_CONNECT_SWAPPED);
384 : 1 : g_signal_connect_object (self->player,
385 : : "notify::volume",
386 : : G_CALLBACK (valent_media_remote_update_volume),
387 : : self, G_CONNECT_SWAPPED);
388 : : }
389 : : else
390 : : {
391 : 2 : valent_media_remote_clear (self);
392 : 2 : return;
393 : : }
394 : :
395 : 1 : valent_media_remote_update_flags (self);
396 : 1 : valent_media_remote_update_metadata (self);
397 : 1 : valent_media_remote_update_position (self);
398 : 1 : valent_media_remote_update_repeat (self);
399 : 1 : valent_media_remote_update_shuffle (self);
400 : 1 : valent_media_remote_update_state (self);
401 : 1 : valent_media_remote_update_volume (self);
402 : : }
403 : :
404 : : static gboolean
405 : 0 : on_change_value_cb (gpointer data)
406 : : {
407 : 0 : ValentMediaRemote *self = VALENT_MEDIA_REMOTE (data);
408 : 0 : double lower, upper, value, page_size;
409 : :
410 [ # # ]: 0 : g_assert (VALENT_IS_MEDIA_REMOTE (self));
411 : :
412 : 0 : self->seek_id = 0;
413 : :
414 [ # # ]: 0 : if (self->player == NULL)
415 : : return G_SOURCE_REMOVE;
416 : :
417 : 0 : lower = gtk_adjustment_get_lower (self->media_position_adjustment);
418 : 0 : upper = gtk_adjustment_get_upper (self->media_position_adjustment);
419 : 0 : value = gtk_adjustment_get_value (self->media_position_adjustment);
420 : 0 : page_size = gtk_adjustment_get_page_size (self->media_position_adjustment);
421 [ # # ]: 0 : value = CLAMP (value, lower, (upper - page_size));
422 : :
423 : 0 : valent_media_player_set_position (self->player, value);
424 : :
425 : 0 : return G_SOURCE_REMOVE;
426 : : }
427 : :
428 : : static gboolean
429 : 0 : on_change_value (GtkRange *range,
430 : : GtkScrollType scroll,
431 : : double value,
432 : : ValentMediaRemote *self)
433 : : {
434 : 0 : double lower, upper, page_size;
435 : :
436 [ # # ]: 0 : g_assert (VALENT_IS_MEDIA_REMOTE (self));
437 : :
438 [ # # ]: 0 : if (self->player == NULL)
439 : : return GDK_EVENT_STOP;
440 : :
441 [ # # ]: 0 : g_clear_handle_id (&self->seek_id, g_source_remove);
442 : 0 : self->seek_id = g_timeout_add (MEDIA_SEEK_DELAY, on_change_value_cb, self);
443 : :
444 : 0 : lower = gtk_adjustment_get_lower (self->media_position_adjustment);
445 : 0 : upper = gtk_adjustment_get_upper (self->media_position_adjustment);
446 : 0 : page_size = gtk_adjustment_get_page_size (self->media_position_adjustment);
447 [ # # ]: 0 : value = CLAMP (value, lower, (upper - page_size));
448 : :
449 : 0 : gtk_adjustment_set_value (self->media_position_adjustment, value);
450 : :
451 : 0 : return GDK_EVENT_STOP;
452 : : }
453 : :
454 : : static void
455 : 1 : on_volume_changed (GtkScaleButton *button,
456 : : double value,
457 : : ValentMediaRemote *self)
458 : : {
459 [ + - ]: 1 : g_assert (VALENT_IS_MEDIA_REMOTE (self));
460 : :
461 [ + - ]: 1 : if (self->player == NULL)
462 : : return;
463 : :
464 [ - + - + ]: 1 : if (!G_APPROX_VALUE (valent_media_player_get_volume (self->player), value, 0.01))
465 : 0 : valent_media_player_set_volume (self->player, value);
466 : : }
467 : :
468 : : /*
469 : : * GAction
470 : : */
471 : : static void
472 : 10 : remote_player_action (GtkWidget *widget,
473 : : const char *action_name,
474 : : GVariant *parameter)
475 : : {
476 : 10 : ValentMediaRemote *self = VALENT_MEDIA_REMOTE (widget);
477 : :
478 [ + - ]: 10 : g_assert (VALENT_IS_MEDIA_REMOTE (self));
479 : :
480 [ + - + - ]: 10 : if (self->player == NULL || action_name == NULL)
481 : : return;
482 : :
483 [ + + ]: 10 : else if (g_str_equal (action_name, "remote.next"))
484 : 1 : valent_media_player_next (self->player);
485 : :
486 [ + + ]: 9 : else if (g_str_equal (action_name, "remote.pause"))
487 : 1 : valent_media_player_pause (self->player);
488 : :
489 [ + + ]: 8 : else if (g_str_equal (action_name, "remote.play"))
490 : 1 : valent_media_player_play (self->player);
491 : :
492 [ + + ]: 7 : else if (g_str_equal (action_name, "remote.previous"))
493 : 1 : valent_media_player_previous (self->player);
494 : :
495 [ + + ]: 6 : else if (g_str_equal (action_name, "remote.repeat"))
496 : : {
497 : 3 : const char *icon_name = gtk_image_get_icon_name (self->repeat_image);
498 : :
499 [ + + ]: 3 : if (g_str_equal (icon_name, "media-playlist-consecutive-symbolic"))
500 : 1 : valent_media_player_set_repeat (self->player, VALENT_MEDIA_REPEAT_ALL);
501 : :
502 [ + + ]: 2 : else if (g_str_equal (icon_name, "media-playlist-repeat-symbolic"))
503 : 1 : valent_media_player_set_repeat (self->player, VALENT_MEDIA_REPEAT_ONE);
504 : :
505 [ + - ]: 1 : else if (g_str_equal (icon_name, "media-playlist-repeat-song-symbolic"))
506 : 1 : valent_media_player_set_repeat (self->player, VALENT_MEDIA_REPEAT_NONE);
507 : : }
508 : :
509 [ + + ]: 3 : else if (g_str_equal (action_name, "remote.seek"))
510 : 2 : valent_media_player_seek (self->player, g_variant_get_double (parameter));
511 : :
512 [ + - ]: 1 : else if (g_str_equal (action_name, "remote.stop"))
513 : 1 : valent_media_player_stop (self->player);
514 : : }
515 : :
516 : : /*
517 : : * GObject
518 : : */
519 : : static void
520 : 1 : valent_media_remote_dispose (GObject *object)
521 : : {
522 : 1 : ValentMediaRemote *self = VALENT_MEDIA_REMOTE (object);
523 : :
524 [ - + ]: 1 : g_clear_handle_id (&self->seek_id, g_source_remove);
525 [ - + ]: 1 : g_clear_handle_id (&self->timer_id, g_source_remove);
526 : :
527 [ - + ]: 1 : if (self->player != NULL)
528 : : {
529 : 0 : g_signal_handlers_disconnect_by_data (self->player, self);
530 [ # # ]: 0 : g_clear_object (&self->player);
531 : : }
532 : :
533 [ + - ]: 1 : g_clear_object (&self->players);
534 : :
535 : 1 : gtk_widget_dispose_template (GTK_WIDGET (object), VALENT_TYPE_MEDIA_REMOTE);
536 : :
537 : 1 : G_OBJECT_CLASS (valent_media_remote_parent_class)->dispose (object);
538 : 1 : }
539 : :
540 : : static void
541 : 10 : valent_media_remote_get_property (GObject *object,
542 : : guint prop_id,
543 : : GValue *value,
544 : : GParamSpec *pspec)
545 : : {
546 : 10 : ValentMediaRemote *self = VALENT_MEDIA_REMOTE (object);
547 : :
548 [ + + - ]: 10 : switch (prop_id)
549 : : {
550 : 3 : case PROP_PLAYERS:
551 : 3 : g_value_set_object (value, self->players);
552 : 3 : break;
553 : :
554 : 7 : case PROP_SHUFFLE:
555 [ + + ]: 7 : if (self->player != NULL)
556 : 5 : g_value_set_boolean (value, valent_media_player_get_shuffle (self->player));
557 : : else
558 : 2 : g_value_set_boolean (value, FALSE);
559 : : break;
560 : :
561 : 0 : default:
562 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
563 : : }
564 : 10 : }
565 : :
566 : : static void
567 : 3 : valent_media_remote_set_property (GObject *object,
568 : : guint prop_id,
569 : : const GValue *value,
570 : : GParamSpec *pspec)
571 : : {
572 : 3 : ValentMediaRemote *self = VALENT_MEDIA_REMOTE (object);
573 : :
574 [ + + - ]: 3 : switch (prop_id)
575 : : {
576 : 1 : case PROP_PLAYERS:
577 : 1 : self->players = g_value_dup_object (value);
578 : 1 : break;
579 : :
580 : 2 : case PROP_SHUFFLE:
581 [ + - ]: 2 : if (self->player != NULL)
582 : 2 : valent_media_player_set_shuffle (self->player, g_value_get_boolean (value));
583 : : break;
584 : :
585 : 0 : default:
586 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
587 : : }
588 : 3 : }
589 : :
590 : : static void
591 : 1 : valent_media_remote_class_init (ValentMediaRemoteClass *klass)
592 : : {
593 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
594 : 1 : GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
595 : :
596 : 1 : object_class->dispose = valent_media_remote_dispose;
597 : 1 : object_class->get_property = valent_media_remote_get_property;
598 : 1 : object_class->set_property = valent_media_remote_set_property;
599 : :
600 : 1 : gtk_widget_class_set_template_from_resource (widget_class, "/ca/andyholmes/Valent/ui/valent-media-remote.ui");
601 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentMediaRemote, media_player);
602 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentMediaRemote, media_art_stack);
603 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentMediaRemote, media_art);
604 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentMediaRemote, media_title);
605 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentMediaRemote, media_artist);
606 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentMediaRemote, media_album);
607 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentMediaRemote, media_position);
608 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentMediaRemote, media_position_adjustment);
609 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentMediaRemote, media_position_current);
610 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentMediaRemote, media_position_length);
611 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentMediaRemote, play_pause_button);
612 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentMediaRemote, repeat_button);
613 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentMediaRemote, repeat_image);
614 : 1 : gtk_widget_class_bind_template_child (widget_class, ValentMediaRemote, volume_button);
615 : 1 : gtk_widget_class_bind_template_callback (widget_class, on_selected_item);
616 : 1 : gtk_widget_class_bind_template_callback (widget_class, on_change_value);
617 : 1 : gtk_widget_class_bind_template_callback (widget_class, on_volume_changed);
618 : :
619 : 1 : gtk_widget_class_install_action (widget_class, "remote.next", NULL, remote_player_action);
620 : 1 : gtk_widget_class_install_action (widget_class, "remote.pause", NULL, remote_player_action);
621 : 1 : gtk_widget_class_install_action (widget_class, "remote.play", NULL, remote_player_action);
622 : 1 : gtk_widget_class_install_action (widget_class, "remote.previous", NULL, remote_player_action);
623 : 1 : gtk_widget_class_install_action (widget_class, "remote.repeat", NULL, remote_player_action);
624 : 1 : gtk_widget_class_install_action (widget_class, "remote.seek", "d", remote_player_action);
625 : 1 : gtk_widget_class_install_action (widget_class, "remote.stop", NULL, remote_player_action);
626 : :
627 : 2 : properties [PROP_PLAYERS] =
628 : 1 : g_param_spec_object ("players", NULL, NULL,
629 : : G_TYPE_LIST_MODEL,
630 : : (G_PARAM_READWRITE |
631 : : G_PARAM_CONSTRUCT_ONLY |
632 : : G_PARAM_STATIC_STRINGS));
633 : :
634 : 2 : properties [PROP_SHUFFLE] =
635 : 1 : g_param_spec_boolean ("shuffle", NULL, NULL,
636 : : FALSE,
637 : : (G_PARAM_READWRITE |
638 : : G_PARAM_EXPLICIT_NOTIFY |
639 : : G_PARAM_STATIC_STRINGS));
640 : :
641 : 1 : g_object_class_install_properties (object_class, N_PROPERTIES, properties);
642 : :
643 : 1 : gtk_widget_class_install_property_action (widget_class, "remote.shuffle", "shuffle");
644 : 1 : }
645 : :
646 : : static void
647 : 1 : valent_media_remote_init (ValentMediaRemote *self)
648 : : {
649 : 1 : GtkWidget *child;
650 : :
651 : 1 : gtk_widget_init_template (GTK_WIDGET (self));
652 : :
653 : 1 : for (child = gtk_widget_get_first_child (GTK_WIDGET (self->volume_button));
654 [ + + ]: 3 : child != NULL;
655 : 2 : child = gtk_widget_get_next_sibling (child))
656 : : {
657 [ + - + + : 2 : if (!GTK_IS_TOGGLE_BUTTON (child))
+ - ]
658 : 1 : continue;
659 : :
660 : 1 : gtk_widget_set_css_classes (child, (const char *[]){
661 : : "circular",
662 : : "flat",
663 : : "toggle",
664 : : NULL
665 : : });
666 : : }
667 : 1 : }
|