Rewrote some legacy KotoCurrentPlaylist code that relied on communicating KotoPlaylist change via a property change. Changed to using a signal. Implemented new koto_album_create_playlist function so we can use our KotoPlaylist generation in the KotoActionBar. Prior to this change, clicking "Play" on a given track in a DiscView would only play that specific track and never give you the opportunity to go backwards or forwards. Now we will use our "continue on playlist" config to determine that behaviour and dynamically generate a playlist when the actionbar is relative to an Album. This functionality is leveraged with a change to koto_current_playlist_set_playlist that now requires a gboolean for determining if we should play immediately as well. Added type checks in our KotoPlayerbar progress / range usage. Prior to this change, you would get a (harmless) GLib Warning when closing the application. Fixed KotoPlayerbar info updating returning early when it failed to get an Album for a track, resulting in the artwork never being reset. Fixed multiple warnings when missing metadata and using g_variants in our koto_playback_set_track_by_uuid.
423 lines
No EOL
12 KiB
C
423 lines
No EOL
12 KiB
C
/* koto-action-bar.c
|
|
*
|
|
* Copyright 2021 Joshua Strobl
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <glib-2.0/glib.h>
|
|
#include <gtk-4.0/gtk/gtk.h>
|
|
#include "koto-action-bar.h"
|
|
#include "../config/config.h"
|
|
#include "../db/cartographer.h"
|
|
#include "../indexer/album-playlist-funcs.h"
|
|
#include "../pages/music/music-local.h"
|
|
#include "../playlist/add-remove-track-popover.h"
|
|
#include "../playlist/current.h"
|
|
#include "../playback/engine.h"
|
|
#include "../koto-button.h"
|
|
#include "../koto-utils.h"
|
|
#include "../koto-window.h"
|
|
|
|
extern KotoAddRemoveTrackPopover * koto_add_remove_track_popup;
|
|
extern KotoCartographer * koto_maps;
|
|
extern KotoConfig * config;
|
|
extern KotoCurrentPlaylist * current_playlist;
|
|
extern KotoPageMusicLocal * music_local_page;
|
|
extern KotoPlaybackEngine * playback_engine;
|
|
extern KotoWindow * main_window;
|
|
|
|
enum {
|
|
SIGNAL_CLOSED,
|
|
N_SIGNALS
|
|
};
|
|
|
|
static guint actionbar_signals[N_SIGNALS] = {
|
|
0
|
|
};
|
|
|
|
struct _KotoActionBar {
|
|
GObject parent_instance;
|
|
|
|
GtkActionBar * main;
|
|
|
|
GtkWidget * center_box_content;
|
|
GtkWidget * start_box_content;
|
|
GtkWidget * stop_box_content;
|
|
|
|
KotoButton * close_button;
|
|
GtkWidget * go_to_artist;
|
|
GtkWidget * playlists;
|
|
GtkWidget * play_track;
|
|
GtkWidget * remove_from_playlist;
|
|
|
|
GList * current_list;
|
|
gchar * current_album_uuid;
|
|
gchar * current_playlist_uuid;
|
|
KotoActionBarRelative relative;
|
|
};
|
|
|
|
struct _KotoActionBarClass {
|
|
GObjectClass parent_class;
|
|
|
|
void (* closed) (KotoActionBar * self);
|
|
};
|
|
|
|
G_DEFINE_TYPE(KotoActionBar, koto_action_bar, G_TYPE_OBJECT);
|
|
|
|
KotoActionBar * action_bar;
|
|
|
|
static void koto_action_bar_class_init(KotoActionBarClass * c) {
|
|
GObjectClass * gobject_class = G_OBJECT_CLASS(c);
|
|
|
|
actionbar_signals[SIGNAL_CLOSED] = g_signal_new(
|
|
"closed",
|
|
G_TYPE_FROM_CLASS(gobject_class),
|
|
G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
|
|
G_STRUCT_OFFSET(KotoActionBarClass, closed),
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
G_TYPE_NONE,
|
|
0
|
|
);
|
|
}
|
|
|
|
static void koto_action_bar_init(KotoActionBar * self) {
|
|
self->main = GTK_ACTION_BAR(gtk_action_bar_new()); // Create a new action bar
|
|
self->current_list = NULL;
|
|
|
|
self->close_button = koto_button_new_with_icon(NULL, "window-close-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_NORMAL);
|
|
self->go_to_artist = gtk_button_new_with_label("Go to Artist");
|
|
self->playlists = gtk_button_new_with_label("Playlists");
|
|
self->play_track = gtk_button_new_with_label("Play");
|
|
self->remove_from_playlist = gtk_button_new_with_label("Remove from Playlist");
|
|
|
|
gtk_widget_add_css_class(self->playlists, "suggested-action");
|
|
gtk_widget_add_css_class(self->play_track, "suggested-action");
|
|
gtk_widget_add_css_class(self->remove_from_playlist, "destructive-action");
|
|
|
|
gtk_widget_set_size_request(self->play_track, 160, -1);
|
|
|
|
self->center_box_content = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
|
|
self->start_box_content = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
|
|
self->stop_box_content = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 20);
|
|
|
|
gtk_box_prepend(GTK_BOX(self->start_box_content), GTK_WIDGET(self->go_to_artist));
|
|
|
|
gtk_box_prepend(GTK_BOX(self->center_box_content), GTK_WIDGET(self->play_track));
|
|
|
|
gtk_box_prepend(GTK_BOX(self->stop_box_content), GTK_WIDGET(self->playlists));
|
|
gtk_box_append(GTK_BOX(self->stop_box_content), GTK_WIDGET(self->remove_from_playlist));
|
|
gtk_box_append(GTK_BOX(self->stop_box_content), GTK_WIDGET(self->close_button));
|
|
|
|
gtk_action_bar_pack_start(self->main, self->start_box_content);
|
|
gtk_action_bar_pack_end(self->main, self->stop_box_content);
|
|
gtk_action_bar_set_center_widget(self->main, self->center_box_content);
|
|
|
|
// Hide all the widgets by default
|
|
gtk_widget_hide(GTK_WIDGET(self->go_to_artist));
|
|
gtk_widget_hide(GTK_WIDGET(self->playlists));
|
|
gtk_widget_hide(GTK_WIDGET(self->play_track));
|
|
gtk_widget_hide(GTK_WIDGET(self->remove_from_playlist));
|
|
|
|
// Set up bindings
|
|
|
|
koto_button_add_click_handler(self->close_button, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_action_bar_handle_close_button_clicked), self);
|
|
g_signal_connect(self->go_to_artist, "clicked", G_CALLBACK(koto_action_bar_handle_go_to_artist_button_clicked), self);
|
|
g_signal_connect(self->playlists, "clicked", G_CALLBACK(koto_action_bar_handle_playlists_button_clicked), self);
|
|
g_signal_connect(self->play_track, "clicked", G_CALLBACK(koto_action_bar_handle_play_track_button_clicked), self);
|
|
g_signal_connect(self->remove_from_playlist, "clicked", G_CALLBACK(koto_action_bar_handle_remove_from_playlist_button_clicked), self);
|
|
|
|
koto_action_bar_toggle_reveal(self, FALSE); // Hide by default
|
|
}
|
|
|
|
void koto_action_bar_close(KotoActionBar * self) {
|
|
if (!KOTO_IS_ACTION_BAR(self)) {
|
|
return;
|
|
}
|
|
|
|
gtk_widget_hide(GTK_WIDGET(koto_add_remove_track_popup)); // Hide the Add / Remove Track Playlists popover
|
|
koto_action_bar_toggle_reveal(self, FALSE); // Hide the action bar
|
|
|
|
g_signal_emit(
|
|
self,
|
|
actionbar_signals[SIGNAL_CLOSED],
|
|
0,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
GtkActionBar * koto_action_bar_get_main(KotoActionBar * self) {
|
|
if (!KOTO_IS_ACTION_BAR(self)) {
|
|
return NULL;
|
|
}
|
|
|
|
return self->main;
|
|
}
|
|
|
|
void koto_action_bar_handle_close_button_clicked(
|
|
GtkGestureClick * gesture,
|
|
int n_press,
|
|
double x,
|
|
double y,
|
|
gpointer data
|
|
) {
|
|
(void) gesture;
|
|
(void) n_press;
|
|
(void) x;
|
|
(void) y;
|
|
koto_action_bar_close(data);
|
|
}
|
|
|
|
void koto_action_bar_handle_go_to_artist_button_clicked(
|
|
GtkButton * button,
|
|
gpointer data
|
|
) {
|
|
(void) button;
|
|
KotoActionBar * self = data;
|
|
|
|
if (!KOTO_IS_ACTION_BAR(self)) {
|
|
return;
|
|
}
|
|
|
|
if (self->current_list == NULL || (g_list_length(self->current_list) != 1)) { // Not a list or not exactly one item
|
|
return;
|
|
}
|
|
|
|
KotoTrack * selected_track = g_list_nth_data(self->current_list, 0); // Get the first item
|
|
|
|
if (!KOTO_IS_TRACK(selected_track)) { // Not a track
|
|
return;
|
|
}
|
|
|
|
gchar * artist_uuid = NULL;
|
|
|
|
g_object_get(
|
|
selected_track,
|
|
"artist-uuid",
|
|
&artist_uuid,
|
|
NULL
|
|
);
|
|
|
|
koto_page_music_local_go_to_artist_by_uuid(music_local_page, artist_uuid); // Go to the artist
|
|
koto_window_go_to_page(main_window, "music.local"); // Navigate to the local music stack so we can see the substack page
|
|
koto_action_bar_close(self); // Close the action bar
|
|
}
|
|
void koto_action_bar_handle_playlists_button_clicked(
|
|
GtkButton * button,
|
|
gpointer data
|
|
) {
|
|
(void) button;
|
|
KotoActionBar * self = data;
|
|
|
|
if (!KOTO_IS_ACTION_BAR(self)) {
|
|
return;
|
|
}
|
|
|
|
if (self->current_list == NULL || (g_list_length(self->current_list) == 0)) { // Not a list or no content
|
|
return;
|
|
}
|
|
|
|
koto_add_remove_track_popover_set_tracks(koto_add_remove_track_popup, self->current_list); // Set the popover tracks
|
|
koto_add_remove_track_popover_set_pointing_to_widget(koto_add_remove_track_popup, GTK_WIDGET(self->playlists), GTK_POS_TOP); // Show the popover above the button
|
|
gtk_widget_show(GTK_WIDGET(koto_add_remove_track_popup));
|
|
}
|
|
|
|
void koto_action_bar_handle_play_track_button_clicked(
|
|
GtkButton * button,
|
|
gpointer data
|
|
) {
|
|
(void) button;
|
|
KotoActionBar * self = data;
|
|
|
|
if (!KOTO_IS_ACTION_BAR(self)) {
|
|
return;
|
|
}
|
|
|
|
if (self->current_list == NULL || (g_list_length(self->current_list) != 1)) { // Not a list or not exactly 1 item selected
|
|
goto doclose;
|
|
}
|
|
|
|
KotoTrack * track = g_list_nth_data(self->current_list, 0); // Get the first track
|
|
|
|
if (!KOTO_IS_TRACK(track)) { // Not a track
|
|
goto doclose;
|
|
}
|
|
|
|
KotoPlaylist * playlist = NULL;
|
|
|
|
if (self->relative == KOTO_ACTION_BAR_IS_PLAYLIST_RELATIVE) { // Relative to a playlist
|
|
playlist = koto_cartographer_get_playlist_by_uuid(koto_maps, self->current_playlist_uuid);
|
|
} else if (self->relative == KOTO_ACTION_BAR_IS_ALBUM_RELATIVE) { // Relative to an Album
|
|
KotoAlbum * album = koto_cartographer_get_album_by_uuid(koto_maps, self->current_album_uuid); // Get the Album
|
|
|
|
if (KOTO_IS_ALBUM(album)) { // Have an Album
|
|
playlist = koto_album_create_playlist(album); // Create our playlist dynamically for the Album
|
|
}
|
|
}
|
|
|
|
if (KOTO_IS_PLAYLIST(playlist)) { // Is a playlist
|
|
koto_current_playlist_set_playlist(current_playlist, playlist, FALSE); // Update our playlist to the one associated with the track we are playing
|
|
koto_playlist_set_track_as_current(playlist, koto_track_get_uuid(track)); // Get this track as the current track in the position
|
|
}
|
|
|
|
gboolean continue_playback = FALSE;
|
|
g_object_get(config, "playback-continue-on-playlist", &continue_playback, NULL);
|
|
koto_playback_engine_set_track_by_uuid(playback_engine, koto_track_get_uuid(track), continue_playback); // Set the track to play
|
|
|
|
doclose:
|
|
koto_action_bar_close(self);
|
|
}
|
|
|
|
void koto_action_bar_handle_remove_from_playlist_button_clicked(
|
|
GtkButton * button,
|
|
gpointer data
|
|
) {
|
|
(void) button;
|
|
KotoActionBar * self = data;
|
|
|
|
if (!KOTO_IS_ACTION_BAR(self)) {
|
|
return;
|
|
}
|
|
|
|
if (self->current_list == NULL || (g_list_length(self->current_list) == 0)) { // Not a list or no content
|
|
goto doclose;
|
|
}
|
|
|
|
if (!koto_utils_is_string_valid(self->current_playlist_uuid)) { // Not valid UUID
|
|
goto doclose;
|
|
}
|
|
|
|
KotoPlaylist * playlist = koto_cartographer_get_playlist_by_uuid(koto_maps, self->current_playlist_uuid);
|
|
|
|
if (!KOTO_IS_PLAYLIST(playlist)) { // Not a playlist
|
|
goto doclose;
|
|
}
|
|
|
|
GList * cur_list;
|
|
|
|
for (cur_list = self->current_list; cur_list != NULL; cur_list = cur_list->next) { // For each KotoTrack
|
|
KotoTrack * track = cur_list->data;
|
|
koto_playlist_remove_track_by_uuid(playlist, koto_track_get_uuid(track)); // Remove this track
|
|
}
|
|
|
|
doclose:
|
|
koto_action_bar_close(self);
|
|
}
|
|
|
|
void koto_action_bar_set_tracks_in_album_selection(
|
|
KotoActionBar * self,
|
|
gchar * album_uuid,
|
|
GList * tracks
|
|
) {
|
|
if (!KOTO_IS_ACTION_BAR(self)) {
|
|
return;
|
|
}
|
|
|
|
if (koto_utils_is_string_valid(self->current_album_uuid)) { // Album UUID currently set
|
|
g_free(self->current_album_uuid);
|
|
}
|
|
|
|
if (koto_utils_is_string_valid(self->current_playlist_uuid)) { // Playlist UUID currently set
|
|
g_free(self->current_playlist_uuid);
|
|
}
|
|
|
|
self->current_playlist_uuid = NULL;
|
|
self->current_album_uuid = g_strdup(album_uuid);
|
|
self->relative = KOTO_ACTION_BAR_IS_ALBUM_RELATIVE;
|
|
|
|
g_list_free(self->current_list);
|
|
self->current_list = g_list_copy(tracks);
|
|
|
|
koto_add_remove_track_popover_clear_tracks(koto_add_remove_track_popup); // Clear the current popover contents
|
|
koto_add_remove_track_popover_set_tracks(koto_add_remove_track_popup, self->current_list); // Set the associated tracks to remove
|
|
|
|
koto_action_bar_toggle_go_to_artist_visibility(self, FALSE);
|
|
koto_action_bar_toggle_play_button_visibility(self, g_list_length(self->current_list) == 1);
|
|
|
|
gtk_widget_show(GTK_WIDGET(self->playlists));
|
|
gtk_widget_hide(GTK_WIDGET(self->remove_from_playlist));
|
|
}
|
|
|
|
void koto_action_bar_set_tracks_in_playlist_selection(
|
|
KotoActionBar * self,
|
|
gchar * playlist_uuid,
|
|
GList * tracks
|
|
) {
|
|
if (!KOTO_IS_ACTION_BAR(self)) {
|
|
return;
|
|
}
|
|
|
|
if (koto_utils_is_string_valid(self->current_album_uuid)) { // Album UUID currently set
|
|
g_free(self->current_album_uuid);
|
|
}
|
|
|
|
if (koto_utils_is_string_valid(self->current_playlist_uuid)) { // Playlist UUID currently set
|
|
g_free(self->current_playlist_uuid);
|
|
}
|
|
|
|
self->current_album_uuid = NULL;
|
|
self->current_playlist_uuid = g_strdup(playlist_uuid);
|
|
self->relative = KOTO_ACTION_BAR_IS_PLAYLIST_RELATIVE;
|
|
|
|
g_list_free(self->current_list);
|
|
self->current_list = g_list_copy(tracks);
|
|
|
|
gboolean single_selected = g_list_length(tracks) == 1;
|
|
|
|
koto_action_bar_toggle_go_to_artist_visibility(self, single_selected);
|
|
koto_action_bar_toggle_play_button_visibility(self, single_selected);
|
|
gtk_widget_hide(GTK_WIDGET(self->playlists));
|
|
gtk_widget_show(GTK_WIDGET(self->remove_from_playlist));
|
|
}
|
|
|
|
void koto_action_bar_toggle_go_to_artist_visibility(
|
|
KotoActionBar * self,
|
|
gboolean visible
|
|
) {
|
|
if (!KOTO_IS_ACTION_BAR(self)) {
|
|
return;
|
|
}
|
|
|
|
(visible) ? gtk_widget_show(GTK_WIDGET(self->go_to_artist)) : gtk_widget_hide(GTK_WIDGET(self->go_to_artist));
|
|
}
|
|
|
|
void koto_action_bar_toggle_play_button_visibility(
|
|
KotoActionBar * self,
|
|
gboolean visible
|
|
) {
|
|
if (!KOTO_IS_ACTION_BAR(self)) {
|
|
return;
|
|
}
|
|
|
|
(visible) ? gtk_widget_show(GTK_WIDGET(self->play_track)) : gtk_widget_hide(GTK_WIDGET(self->play_track));
|
|
}
|
|
|
|
void koto_action_bar_toggle_reveal(
|
|
KotoActionBar * self,
|
|
gboolean state
|
|
) {
|
|
if (!KOTO_IS_ACTION_BAR(self)) {
|
|
return;
|
|
}
|
|
|
|
gtk_action_bar_set_revealed(self->main, state);
|
|
}
|
|
|
|
KotoActionBar * koto_action_bar_new() {
|
|
return g_object_new(
|
|
KOTO_TYPE_ACTION_BAR,
|
|
NULL
|
|
);
|
|
} |