Implemented no albums view and improved indexing of file content directly within an artist.
Every KotoArtist now has a dedicated KotoPlaylist that is dynamically generated during KotoArtist initialization and contains references to all KotoTracks. Fixed weird parsing error with some track names that had single quote (ew) and made changes so various parts of the UX would not freak out when we didn't have an album associated with a track.
This commit is contained in:
parent
2bf5aa9388
commit
34ca201121
19 changed files with 948 additions and 530 deletions
486
src/components/track-table.c
Normal file
486
src/components/track-table.c
Normal file
|
@ -0,0 +1,486 @@
|
||||||
|
/* track-table.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 "../db/cartographer.h"
|
||||||
|
#include "../playlist/playlist.h"
|
||||||
|
#include "../koto-button.h"
|
||||||
|
#include "../koto-utils.h"
|
||||||
|
#include "../koto-window.h"
|
||||||
|
#include "koto-action-bar.h"
|
||||||
|
#include "track-table.h"
|
||||||
|
|
||||||
|
extern KotoActionBar * action_bar;
|
||||||
|
extern KotoCartographer * koto_maps;
|
||||||
|
extern KotoWindow * main_window;
|
||||||
|
|
||||||
|
struct _KotoTrackTable {
|
||||||
|
GObject parent_instance;
|
||||||
|
gchar * uuid;
|
||||||
|
KotoPlaylist * playlist;
|
||||||
|
|
||||||
|
GtkWidget * main;
|
||||||
|
|
||||||
|
GtkListItemFactory * item_factory;
|
||||||
|
GListModel * model;
|
||||||
|
GtkSelectionModel * selection_model;
|
||||||
|
|
||||||
|
GtkWidget * track_list_content;
|
||||||
|
GtkWidget * track_list_header;
|
||||||
|
GtkWidget * track_list_view;
|
||||||
|
|
||||||
|
KotoButton * track_album_button;
|
||||||
|
KotoButton * track_artist_button;
|
||||||
|
KotoButton * track_num_button;
|
||||||
|
KotoButton * track_title_button;
|
||||||
|
|
||||||
|
GtkSizeGroup * track_pos_size_group;
|
||||||
|
GtkSizeGroup * track_name_size_group;
|
||||||
|
GtkSizeGroup * track_album_size_group;
|
||||||
|
GtkSizeGroup * track_artist_size_group;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _KotoTrackTableClass {
|
||||||
|
GObjectClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE(KotoTrackTable, koto_track_table, G_TYPE_OBJECT);
|
||||||
|
|
||||||
|
static void koto_track_table_class_init(KotoTrackTableClass * c) {
|
||||||
|
(void) c;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void koto_track_table_init(KotoTrackTable * self) {
|
||||||
|
self->main = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
||||||
|
|
||||||
|
self->track_name_size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
|
||||||
|
self->track_pos_size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
|
||||||
|
self->track_album_size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
|
||||||
|
self->track_artist_size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
|
||||||
|
|
||||||
|
self->item_factory = gtk_signal_list_item_factory_new(); // Create a new signal list item factory
|
||||||
|
g_signal_connect(self->item_factory, "setup", G_CALLBACK(koto_track_table_setup_track_item), self);
|
||||||
|
g_signal_connect(self->item_factory, "bind", G_CALLBACK(koto_track_table_bind_track_item), self);
|
||||||
|
|
||||||
|
self->track_list_content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
||||||
|
gtk_widget_add_css_class((self->track_list_content), "track-list-content");
|
||||||
|
gtk_widget_set_hexpand(self->track_list_content, TRUE); // Expand horizontally
|
||||||
|
gtk_widget_set_vexpand(self->track_list_content, TRUE); // Expand vertically
|
||||||
|
|
||||||
|
self->track_list_header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
|
||||||
|
gtk_widget_add_css_class(self->track_list_header, "track-list-header");
|
||||||
|
koto_track_table_create_tracks_header(self); // Create our tracks header content
|
||||||
|
|
||||||
|
self->track_list_view = gtk_list_view_new(NULL, self->item_factory); // Create our list view with no model yet
|
||||||
|
gtk_widget_add_css_class(self->track_list_view, "track-list-columned");
|
||||||
|
gtk_widget_set_hexpand(self->track_list_view, TRUE); // Expand horizontally
|
||||||
|
gtk_widget_set_vexpand(self->track_list_view, TRUE); // Expand vertically
|
||||||
|
|
||||||
|
gtk_box_append(GTK_BOX(self->track_list_content), self->track_list_header);
|
||||||
|
gtk_box_append(GTK_BOX(self->track_list_content), self->track_list_view);
|
||||||
|
|
||||||
|
gtk_box_append(GTK_BOX(self->main), self->track_list_content);
|
||||||
|
|
||||||
|
g_signal_connect(action_bar, "closed", G_CALLBACK(koto_track_table_handle_action_bar_closed), self); // Handle closed action bar
|
||||||
|
}
|
||||||
|
|
||||||
|
void koto_track_table_bind_track_item(
|
||||||
|
GtkListItemFactory * factory,
|
||||||
|
GtkListItem * item,
|
||||||
|
KotoTrackTable * self
|
||||||
|
) {
|
||||||
|
(void) factory;
|
||||||
|
|
||||||
|
GtkWidget * track_position_label = gtk_widget_get_first_child(gtk_list_item_get_child(item));
|
||||||
|
GtkWidget * track_name_label = gtk_widget_get_next_sibling(track_position_label);
|
||||||
|
GtkWidget * track_album_label = gtk_widget_get_next_sibling(track_name_label);
|
||||||
|
GtkWidget * track_artist_label = gtk_widget_get_next_sibling(track_album_label);
|
||||||
|
|
||||||
|
KotoTrack * track = gtk_list_item_get_item(item); // Get the track UUID from our model
|
||||||
|
|
||||||
|
if (!KOTO_IS_TRACK(track)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gchar * track_name = NULL;
|
||||||
|
gchar * album_uuid = NULL;
|
||||||
|
gchar * artist_uuid = NULL;
|
||||||
|
|
||||||
|
g_object_get(
|
||||||
|
track,
|
||||||
|
"parsed-name",
|
||||||
|
&track_name,
|
||||||
|
"album-uuid",
|
||||||
|
&album_uuid,
|
||||||
|
"artist-uuid",
|
||||||
|
&artist_uuid,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
guint track_position = koto_playlist_get_position_of_track(self->playlist, track) + 1;
|
||||||
|
|
||||||
|
gtk_label_set_label(GTK_LABEL(track_position_label), g_strdup_printf("%u", track_position)); // Set the track position
|
||||||
|
gtk_label_set_label(GTK_LABEL(track_name_label), track_name); // Set our track name
|
||||||
|
|
||||||
|
if (koto_utils_is_string_valid(album_uuid)) { // Is associated with an album
|
||||||
|
KotoAlbum * album = koto_cartographer_get_album_by_uuid(koto_maps, album_uuid);
|
||||||
|
|
||||||
|
if (KOTO_IS_ALBUM(album)) {
|
||||||
|
gtk_label_set_label(GTK_LABEL(track_album_label), koto_album_get_name(album)); // Get the name of the album and set it to the label
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
KotoArtist * artist = koto_cartographer_get_artist_by_uuid(koto_maps, artist_uuid);
|
||||||
|
|
||||||
|
if (KOTO_IS_ARTIST(artist)) {
|
||||||
|
gtk_label_set_label(GTK_LABEL(track_artist_label), koto_artist_get_name(artist)); // Get the name of the artist and set it to the label
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void koto_track_table_create_tracks_header(KotoTrackTable * self) {
|
||||||
|
self->track_num_button = koto_button_new_with_icon("#", "pan-down-symbolic", "pan-up-symbolic", KOTO_BUTTON_PIXBUF_SIZE_SMALL);
|
||||||
|
koto_button_add_click_handler(self->track_num_button, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_track_table_handle_track_num_clicked), self);
|
||||||
|
koto_button_set_image_position(self->track_num_button, KOTO_BUTTON_IMAGE_POS_RIGHT); // Move the image to the right
|
||||||
|
gtk_size_group_add_widget(self->track_pos_size_group, GTK_WIDGET(self->track_num_button));
|
||||||
|
|
||||||
|
self->track_title_button = koto_button_new_plain("Title");
|
||||||
|
koto_button_add_click_handler(self->track_title_button, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_track_table_handle_track_name_clicked), self);
|
||||||
|
gtk_size_group_add_widget(self->track_name_size_group, GTK_WIDGET(self->track_title_button));
|
||||||
|
|
||||||
|
self->track_album_button = koto_button_new_plain("Album");
|
||||||
|
|
||||||
|
gtk_widget_set_margin_start(GTK_WIDGET(self->track_album_button), 50);
|
||||||
|
koto_button_add_click_handler(self->track_album_button, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_track_table_handle_track_album_clicked), self);
|
||||||
|
gtk_size_group_add_widget(self->track_album_size_group, GTK_WIDGET(self->track_album_button));
|
||||||
|
|
||||||
|
self->track_artist_button = koto_button_new_plain("Artist");
|
||||||
|
|
||||||
|
gtk_widget_set_margin_start(GTK_WIDGET(self->track_artist_button), 50);
|
||||||
|
koto_button_add_click_handler(self->track_artist_button, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_track_table_handle_track_artist_clicked), self);
|
||||||
|
gtk_size_group_add_widget(self->track_artist_size_group, GTK_WIDGET(self->track_artist_button));
|
||||||
|
|
||||||
|
gtk_box_append(GTK_BOX(self->track_list_header), GTK_WIDGET(self->track_num_button));
|
||||||
|
gtk_box_append(GTK_BOX(self->track_list_header), GTK_WIDGET(self->track_title_button));
|
||||||
|
gtk_box_append(GTK_BOX(self->track_list_header), GTK_WIDGET(self->track_album_button));
|
||||||
|
gtk_box_append(GTK_BOX(self->track_list_header), GTK_WIDGET(self->track_artist_button));
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkWidget * koto_track_table_get_main(KotoTrackTable * self) {
|
||||||
|
return self->main;
|
||||||
|
}
|
||||||
|
|
||||||
|
void koto_track_table_handle_action_bar_closed (
|
||||||
|
KotoActionBar * bar,
|
||||||
|
gpointer data
|
||||||
|
) {
|
||||||
|
(void) bar;
|
||||||
|
KotoTrackTable * self = data;
|
||||||
|
|
||||||
|
if (!KOTO_IS_TRACK_TABLE(self)) { // Self is not a track table
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!KOTO_IS_PLAYLIST(self->playlist)) { // No playlist set
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_selection_model_unselect_all(self->selection_model);
|
||||||
|
gtk_widget_grab_focus(GTK_WIDGET(main_window)); // Focus on the window
|
||||||
|
}
|
||||||
|
|
||||||
|
void koto_track_table_handle_track_album_clicked (
|
||||||
|
GtkGestureClick * gesture,
|
||||||
|
int n_press,
|
||||||
|
double x,
|
||||||
|
double y,
|
||||||
|
gpointer user_data
|
||||||
|
) {
|
||||||
|
(void) gesture;
|
||||||
|
(void) n_press;
|
||||||
|
(void) x;
|
||||||
|
(void) y;
|
||||||
|
KotoTrackTable * self = user_data;
|
||||||
|
|
||||||
|
gtk_widget_add_css_class(GTK_WIDGET(self->track_album_button), "active");
|
||||||
|
koto_button_hide_image(self->track_num_button); // Go back to hiding the image
|
||||||
|
koto_track_table_set_playlist_model(self, KOTO_PREFERRED_MODEL_TYPE_SORT_BY_ALBUM);
|
||||||
|
}
|
||||||
|
|
||||||
|
void koto_track_table_handle_track_artist_clicked(
|
||||||
|
GtkGestureClick * gesture,
|
||||||
|
int n_press,
|
||||||
|
double x,
|
||||||
|
double y,
|
||||||
|
gpointer user_data
|
||||||
|
) {
|
||||||
|
(void) gesture;
|
||||||
|
(void) n_press;
|
||||||
|
(void) x;
|
||||||
|
(void) y;
|
||||||
|
KotoTrackTable * self = user_data;
|
||||||
|
|
||||||
|
gtk_widget_add_css_class(GTK_WIDGET(self->track_artist_button), "active");
|
||||||
|
koto_button_hide_image(self->track_num_button); // Go back to hiding the image
|
||||||
|
koto_track_table_set_playlist_model(self, KOTO_PREFERRED_MODEL_TYPE_SORT_BY_ARTIST);
|
||||||
|
}
|
||||||
|
|
||||||
|
void koto_track_table_handle_track_name_clicked(
|
||||||
|
GtkGestureClick * gesture,
|
||||||
|
int n_press,
|
||||||
|
double x,
|
||||||
|
double y,
|
||||||
|
gpointer user_data
|
||||||
|
) {
|
||||||
|
(void) gesture;
|
||||||
|
(void) n_press;
|
||||||
|
(void) x;
|
||||||
|
(void) y;
|
||||||
|
KotoTrackTable * self = user_data;
|
||||||
|
|
||||||
|
gtk_widget_add_css_class(GTK_WIDGET(self->track_title_button), "active");
|
||||||
|
koto_button_hide_image(self->track_num_button); // Go back to hiding the image
|
||||||
|
koto_track_table_set_playlist_model(self, KOTO_PREFERRED_MODEL_TYPE_SORT_BY_TRACK_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
void koto_track_table_handle_track_num_clicked(
|
||||||
|
GtkGestureClick * gesture,
|
||||||
|
int n_press,
|
||||||
|
double x,
|
||||||
|
double y,
|
||||||
|
gpointer user_data
|
||||||
|
) {
|
||||||
|
(void) gesture;
|
||||||
|
(void) n_press;
|
||||||
|
(void) x;
|
||||||
|
(void) y;
|
||||||
|
KotoTrackTable * self = user_data;
|
||||||
|
|
||||||
|
KotoPreferredModelType current_model = koto_playlist_get_current_model(self->playlist);
|
||||||
|
|
||||||
|
if (current_model == KOTO_PREFERRED_MODEL_TYPE_DEFAULT) { // Set to newest currently
|
||||||
|
koto_track_table_set_playlist_model(self, KOTO_PREFERRED_MODEL_TYPE_OLDEST_FIRST); // Sort reversed (oldest)
|
||||||
|
koto_button_show_image(self->track_num_button, TRUE); // Use inverted value (pan-up-symbolic)
|
||||||
|
} else {
|
||||||
|
koto_track_table_set_playlist_model(self, KOTO_PREFERRED_MODEL_TYPE_DEFAULT); // Sort newest
|
||||||
|
koto_button_show_image(self->track_num_button, FALSE); // Use pan down default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void koto_track_table_handle_tracks_selected(
|
||||||
|
GtkSelectionModel * model,
|
||||||
|
guint position,
|
||||||
|
guint n_items,
|
||||||
|
gpointer user_data
|
||||||
|
) {
|
||||||
|
(void) position;
|
||||||
|
KotoTrackTable * self = user_data;
|
||||||
|
|
||||||
|
if (!KOTO_IS_TRACK_TABLE(self)) { // Not a track table
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n_items == 0) { // No items selected
|
||||||
|
koto_action_bar_toggle_reveal(action_bar, FALSE); // Hide the action bar
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkBitset * selected_items_bitset = gtk_selection_model_get_selection(model); // Get the selected items as a GtkBitSet
|
||||||
|
GtkBitsetIter iter;
|
||||||
|
GList * selected_tracks = NULL;
|
||||||
|
GList * selected_tracks_pos = NULL;
|
||||||
|
|
||||||
|
guint first_track_pos;
|
||||||
|
|
||||||
|
if (!gtk_bitset_iter_init_first(&iter, selected_items_bitset, &first_track_pos)) { // Failed to get the first item
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selected_tracks_pos = g_list_append(selected_tracks_pos, GUINT_TO_POINTER(first_track_pos));
|
||||||
|
|
||||||
|
gboolean have_more_items = TRUE;
|
||||||
|
|
||||||
|
while (have_more_items) { // While we are able to get selected items
|
||||||
|
guint track_pos;
|
||||||
|
have_more_items = gtk_bitset_iter_next(&iter, &track_pos);
|
||||||
|
if (have_more_items) { // Got the next track
|
||||||
|
selected_tracks_pos = g_list_append(selected_tracks_pos, GUINT_TO_POINTER(track_pos));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GList * cur_pos_list;
|
||||||
|
|
||||||
|
for (cur_pos_list = selected_tracks_pos; cur_pos_list != NULL; cur_pos_list = cur_pos_list->next) { // Iterate over every position that we accumulated
|
||||||
|
KotoTrack * selected_track = g_list_model_get_item(self->model, GPOINTER_TO_UINT(cur_pos_list->data)); // Get the KotoTrack in the GListModel for this current position
|
||||||
|
selected_tracks = g_list_append(selected_tracks, selected_track); // Add to selected tracks
|
||||||
|
}
|
||||||
|
|
||||||
|
koto_action_bar_set_tracks_in_playlist_selection(action_bar, koto_playlist_get_uuid(self->playlist), selected_tracks); // Set the tracks for the playlist selection
|
||||||
|
koto_action_bar_toggle_reveal(action_bar, TRUE); // Show the items
|
||||||
|
}
|
||||||
|
|
||||||
|
void koto_track_table_set_model(
|
||||||
|
KotoTrackTable * self,
|
||||||
|
KotoPreferredModelType model
|
||||||
|
) {
|
||||||
|
if (!KOTO_IS_TRACK_TABLE(self)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
koto_playlist_apply_model(self->playlist, model); // Apply our new model
|
||||||
|
self->model = G_LIST_MODEL(koto_playlist_get_store(self->playlist)); // Get the latest generated model / store and cast it as a GListModel
|
||||||
|
|
||||||
|
if (model != KOTO_PREFERRED_MODEL_TYPE_SORT_BY_ALBUM) { // Not sorting by album currently
|
||||||
|
gtk_widget_remove_css_class(GTK_WIDGET(self->track_album_button), "active");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (model != KOTO_PREFERRED_MODEL_TYPE_SORT_BY_ARTIST) { // Not sorting by artist currently
|
||||||
|
gtk_widget_remove_css_class(GTK_WIDGET(self->track_artist_button), "active");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (model != KOTO_PREFERRED_MODEL_TYPE_SORT_BY_TRACK_NAME) { // Not sorting by track name
|
||||||
|
gtk_widget_remove_css_class(GTK_WIDGET(self->track_title_button), "active");
|
||||||
|
}
|
||||||
|
|
||||||
|
self->selection_model = GTK_SELECTION_MODEL(gtk_multi_selection_new(self->model));
|
||||||
|
g_signal_connect(self->selection_model, "selection-changed", G_CALLBACK(koto_track_table_handle_tracks_selected), self); // Bind to our selection changed
|
||||||
|
|
||||||
|
gtk_list_view_set_model(GTK_LIST_VIEW(self->track_list_view), self->selection_model); // Set our multi selection model to our provided model
|
||||||
|
}
|
||||||
|
|
||||||
|
void koto_track_table_set_playlist_model (
|
||||||
|
KotoTrackTable * self,
|
||||||
|
KotoPreferredModelType model
|
||||||
|
) {
|
||||||
|
if (!KOTO_IS_TRACK_TABLE(self)) { // Not a track table
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!KOTO_IS_PLAYLIST(self->playlist)) { // Don't have a playlist yet
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
koto_playlist_apply_model(self->playlist, model); // Apply our new model
|
||||||
|
self->model = G_LIST_MODEL(koto_playlist_get_store(self->playlist)); // Get the latest generated model / store and cast it as a GListModel
|
||||||
|
|
||||||
|
if (model != KOTO_PREFERRED_MODEL_TYPE_SORT_BY_ALBUM) { // Not sorting by album currently
|
||||||
|
gtk_widget_remove_css_class(GTK_WIDGET(self->track_album_button), "active");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (model != KOTO_PREFERRED_MODEL_TYPE_SORT_BY_ARTIST) { // Not sorting by artist currently
|
||||||
|
gtk_widget_remove_css_class(GTK_WIDGET(self->track_artist_button), "active");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (model != KOTO_PREFERRED_MODEL_TYPE_SORT_BY_TRACK_NAME) { // Not sorting by track name
|
||||||
|
gtk_widget_remove_css_class(GTK_WIDGET(self->track_title_button), "active");
|
||||||
|
}
|
||||||
|
|
||||||
|
self->selection_model = GTK_SELECTION_MODEL(gtk_multi_selection_new(self->model));
|
||||||
|
g_signal_connect(self->selection_model, "selection-changed", G_CALLBACK(koto_track_table_handle_tracks_selected), self); // Bind to our selection changed
|
||||||
|
|
||||||
|
gtk_list_view_set_model(GTK_LIST_VIEW(self->track_list_view), self->selection_model); // Set our multi selection model to our provided model
|
||||||
|
|
||||||
|
KotoPreferredModelType current_model = koto_playlist_get_current_model(self->playlist); // Get the current model
|
||||||
|
|
||||||
|
if (current_model == KOTO_PREFERRED_MODEL_TYPE_OLDEST_FIRST) {
|
||||||
|
koto_button_show_image(self->track_num_button, TRUE); // Immediately use pan-up-symbolic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void koto_track_table_set_playlist(
|
||||||
|
KotoTrackTable * self,
|
||||||
|
KotoPlaylist * playlist
|
||||||
|
) {
|
||||||
|
if (!KOTO_IS_TRACK_TABLE(self)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!KOTO_IS_PLAYLIST(playlist)) { // Not a playlist
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->playlist = playlist;
|
||||||
|
koto_track_table_set_playlist_model(self, KOTO_PREFERRED_MODEL_TYPE_DEFAULT); // TODO: Enable this to be changed
|
||||||
|
}
|
||||||
|
|
||||||
|
void koto_track_table_setup_track_item(
|
||||||
|
GtkListItemFactory * factory,
|
||||||
|
GtkListItem * item,
|
||||||
|
gpointer user_data
|
||||||
|
) {
|
||||||
|
(void) factory;
|
||||||
|
KotoTrackTable * self = user_data;
|
||||||
|
|
||||||
|
if (!KOTO_IS_TRACK_TABLE(self)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkWidget * item_content = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); // Have a horizontal box for our content
|
||||||
|
|
||||||
|
gtk_widget_add_css_class(item_content, "track-list-columned-item");
|
||||||
|
|
||||||
|
GtkWidget * track_number = gtk_label_new(NULL); // Our track number
|
||||||
|
|
||||||
|
gtk_label_set_xalign(GTK_LABEL(track_number), 0);
|
||||||
|
gtk_widget_add_css_class(track_number, "track-column-number");
|
||||||
|
gtk_widget_set_halign(track_number, GTK_ALIGN_END);
|
||||||
|
gtk_widget_set_hexpand(track_number, FALSE);
|
||||||
|
gtk_widget_set_size_request(track_number, 150, -1);
|
||||||
|
gtk_box_append(GTK_BOX(item_content), track_number);
|
||||||
|
gtk_size_group_add_widget(self->track_pos_size_group, track_number);
|
||||||
|
|
||||||
|
GtkWidget * track_name = gtk_label_new(NULL); // Our track name
|
||||||
|
|
||||||
|
gtk_label_set_xalign(GTK_LABEL(track_name), 0);
|
||||||
|
gtk_widget_add_css_class(track_name, "track-column-name");
|
||||||
|
gtk_widget_set_halign(track_name, GTK_ALIGN_START);
|
||||||
|
gtk_widget_set_hexpand(track_name, FALSE);
|
||||||
|
gtk_widget_set_size_request(track_name, 350, -1);
|
||||||
|
gtk_box_append(GTK_BOX(item_content), track_name);
|
||||||
|
gtk_size_group_add_widget(self->track_name_size_group, track_name);
|
||||||
|
|
||||||
|
GtkWidget * track_album = gtk_label_new(NULL); // Our track album
|
||||||
|
|
||||||
|
gtk_label_set_xalign(GTK_LABEL(track_album), 0);
|
||||||
|
gtk_widget_add_css_class(track_album, "track-column-album");
|
||||||
|
gtk_widget_set_halign(track_album, GTK_ALIGN_START);
|
||||||
|
gtk_widget_set_hexpand(track_album, FALSE);
|
||||||
|
gtk_widget_set_margin_start(track_album, 50);
|
||||||
|
gtk_widget_set_size_request(track_album, 350, -1);
|
||||||
|
gtk_box_append(GTK_BOX(item_content), track_album);
|
||||||
|
gtk_size_group_add_widget(self->track_album_size_group, track_album);
|
||||||
|
|
||||||
|
GtkWidget * track_artist = gtk_label_new(NULL); // Our track artist
|
||||||
|
|
||||||
|
gtk_label_set_xalign(GTK_LABEL(track_artist), 0);
|
||||||
|
gtk_widget_add_css_class(track_artist, "track-column-artist");
|
||||||
|
gtk_widget_set_halign(track_artist, GTK_ALIGN_START);
|
||||||
|
gtk_widget_set_hexpand(track_artist, TRUE);
|
||||||
|
gtk_widget_set_margin_start(track_artist, 50);
|
||||||
|
gtk_widget_set_size_request(track_artist, 350, -1);
|
||||||
|
gtk_box_append(GTK_BOX(item_content), track_artist);
|
||||||
|
gtk_size_group_add_widget(self->track_artist_size_group, track_artist);
|
||||||
|
|
||||||
|
gtk_list_item_set_child(item, item_content);
|
||||||
|
}
|
||||||
|
|
||||||
|
KotoTrackTable * koto_track_table_new() {
|
||||||
|
return g_object_new(
|
||||||
|
KOTO_TYPE_TRACK_TABLE,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
}
|
114
src/components/track-table.h
Normal file
114
src/components/track-table.h
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
/* track-table.h
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glib-2.0/glib-object.h>
|
||||||
|
#include <gtk-4.0/gtk/gtk.h>
|
||||||
|
#include "../playlist/playlist.h"
|
||||||
|
#include "koto-action-bar.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
#define KOTO_TYPE_TRACK_TABLE koto_track_table_get_type()
|
||||||
|
#define KOTO_TRACK_TABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), KOTO_TYPE_TRACK_TABLE, KotoTrackTable))
|
||||||
|
typedef struct _KotoTrackTable KotoTrackTable;
|
||||||
|
typedef struct _KotoTrackTableClass KotoTrackTableClass;
|
||||||
|
|
||||||
|
GLIB_AVAILABLE_IN_ALL
|
||||||
|
GType koto_track_type_get_type(void) G_GNUC_CONST;
|
||||||
|
|
||||||
|
#define KOTO_IS_TRACK_TABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KOTO_TYPE_TRACK_TABLE))
|
||||||
|
|
||||||
|
void koto_track_table_bind_track_item(
|
||||||
|
GtkListItemFactory * factory,
|
||||||
|
GtkListItem * item,
|
||||||
|
KotoTrackTable * self
|
||||||
|
);
|
||||||
|
|
||||||
|
void koto_track_table_create_tracks_header(KotoTrackTable * self);
|
||||||
|
|
||||||
|
GtkWidget * koto_track_table_get_main(KotoTrackTable * self);
|
||||||
|
|
||||||
|
void koto_track_table_handle_action_bar_closed(
|
||||||
|
KotoActionBar * bar,
|
||||||
|
gpointer data
|
||||||
|
);
|
||||||
|
|
||||||
|
void koto_track_table_handle_track_album_clicked(
|
||||||
|
GtkGestureClick * gesture,
|
||||||
|
int n_press,
|
||||||
|
double x,
|
||||||
|
double y,
|
||||||
|
gpointer user_data
|
||||||
|
);
|
||||||
|
|
||||||
|
void koto_track_table_handle_track_artist_clicked(
|
||||||
|
GtkGestureClick * gesture,
|
||||||
|
int n_press,
|
||||||
|
double x,
|
||||||
|
double y,
|
||||||
|
gpointer user_data
|
||||||
|
);
|
||||||
|
|
||||||
|
void koto_track_table_handle_track_name_clicked(
|
||||||
|
GtkGestureClick * gesture,
|
||||||
|
int n_press,
|
||||||
|
double x,
|
||||||
|
double y,
|
||||||
|
gpointer user_data
|
||||||
|
);
|
||||||
|
|
||||||
|
void koto_track_table_handle_track_num_clicked(
|
||||||
|
GtkGestureClick * gesture,
|
||||||
|
int n_press,
|
||||||
|
double x,
|
||||||
|
double y,
|
||||||
|
gpointer user_data
|
||||||
|
);
|
||||||
|
|
||||||
|
void koto_track_table_handle_tracks_selected(
|
||||||
|
GtkSelectionModel * model,
|
||||||
|
guint position,
|
||||||
|
guint n_items,
|
||||||
|
gpointer user_data
|
||||||
|
);
|
||||||
|
|
||||||
|
void koto_track_table_set_model(
|
||||||
|
KotoTrackTable * self,
|
||||||
|
KotoPreferredModelType model
|
||||||
|
);
|
||||||
|
|
||||||
|
void koto_track_table_set_playlist_model(
|
||||||
|
KotoTrackTable * self,
|
||||||
|
KotoPreferredModelType model
|
||||||
|
);
|
||||||
|
|
||||||
|
void koto_track_table_set_playlist(
|
||||||
|
KotoTrackTable * self,
|
||||||
|
KotoPlaylist * playlist
|
||||||
|
);
|
||||||
|
|
||||||
|
void koto_track_table_setup_track_item(
|
||||||
|
GtkListItemFactory * factory,
|
||||||
|
GtkListItem * item,
|
||||||
|
gpointer user_data
|
||||||
|
);
|
||||||
|
|
||||||
|
KotoTrackTable * koto_track_table_new();
|
||||||
|
|
||||||
|
G_END_DECLS
|
|
@ -61,6 +61,8 @@ int process_artists(
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
koto_artist_set_as_finalized(artist); // Indicate it is finalized
|
||||||
|
|
||||||
int tracks_rc = sqlite3_exec(koto_db, g_strdup_printf("SELECT * FROM tracks WHERE artist_id=\"%s\"", artist_uuid), process_tracks, NULL, NULL); // Process our tracks by artist uuid
|
int tracks_rc = sqlite3_exec(koto_db, g_strdup_printf("SELECT * FROM tracks WHERE artist_id=\"%s\"", artist_uuid), process_tracks, NULL, NULL); // Process our tracks by artist uuid
|
||||||
|
|
||||||
if (tracks_rc != SQLITE_OK) { // Failed to get our tracks
|
if (tracks_rc != SQLITE_OK) { // Failed to get our tracks
|
||||||
|
|
25
src/indexer/artist-playlist-funcs.h
Normal file
25
src/indexer/artist-playlist-funcs.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/* artist-playlist-funcs.h
|
||||||
|
*
|
||||||
|
* 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 "../playlist/playlist.h"
|
||||||
|
#include "structs.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
KotoPlaylist * koto_artist_get_playlist(KotoArtist * self);
|
||||||
|
|
||||||
|
G_END_DECLS
|
|
@ -19,7 +19,9 @@
|
||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
#include "../db/db.h"
|
#include "../db/db.h"
|
||||||
#include "../db/cartographer.h"
|
#include "../db/cartographer.h"
|
||||||
|
#include "../playlist/playlist.h"
|
||||||
#include "../koto-utils.h"
|
#include "../koto-utils.h"
|
||||||
|
#include "artist-playlist-funcs.h"
|
||||||
#include "structs.h"
|
#include "structs.h"
|
||||||
#include "track-helpers.h"
|
#include "track-helpers.h"
|
||||||
|
|
||||||
|
@ -39,6 +41,7 @@ static GParamSpec * props[N_PROPERTIES] = {
|
||||||
enum {
|
enum {
|
||||||
SIGNAL_ALBUM_ADDED,
|
SIGNAL_ALBUM_ADDED,
|
||||||
SIGNAL_ALBUM_REMOVED,
|
SIGNAL_ALBUM_REMOVED,
|
||||||
|
SIGNAL_HAS_NO_ALBUMS,
|
||||||
SIGNAL_TRACK_ADDED,
|
SIGNAL_TRACK_ADDED,
|
||||||
SIGNAL_TRACK_REMOVED,
|
SIGNAL_TRACK_REMOVED,
|
||||||
N_SIGNALS
|
N_SIGNALS
|
||||||
|
@ -52,6 +55,9 @@ struct _KotoArtist {
|
||||||
GObject parent_instance;
|
GObject parent_instance;
|
||||||
gchar * uuid;
|
gchar * uuid;
|
||||||
|
|
||||||
|
KotoPlaylist * content_playlist;
|
||||||
|
|
||||||
|
gboolean finalized;
|
||||||
gboolean has_artist_art;
|
gboolean has_artist_art;
|
||||||
gchar * artist_name;
|
gchar * artist_name;
|
||||||
GList * albums;
|
GList * albums;
|
||||||
|
@ -71,6 +77,7 @@ struct _KotoArtistClass {
|
||||||
KotoArtist * artist,
|
KotoArtist * artist,
|
||||||
KotoAlbum * album
|
KotoAlbum * album
|
||||||
);
|
);
|
||||||
|
void (* has_no_albums) (KotoArtist * artist);
|
||||||
void (* track_added) (
|
void (* track_added) (
|
||||||
KotoArtist * artist,
|
KotoArtist * artist,
|
||||||
KotoTrack * track
|
KotoTrack * track
|
||||||
|
@ -130,6 +137,18 @@ static void koto_artist_class_init(KotoArtistClass * c) {
|
||||||
KOTO_TYPE_ALBUM
|
KOTO_TYPE_ALBUM
|
||||||
);
|
);
|
||||||
|
|
||||||
|
artist_signals[SIGNAL_HAS_NO_ALBUMS] = g_signal_new(
|
||||||
|
"has-no-albums",
|
||||||
|
G_TYPE_FROM_CLASS(gobject_class),
|
||||||
|
G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
|
||||||
|
G_STRUCT_OFFSET(KotoArtistClass, has_no_albums),
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
G_TYPE_NONE,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
artist_signals[SIGNAL_TRACK_ADDED] = g_signal_new(
|
artist_signals[SIGNAL_TRACK_ADDED] = g_signal_new(
|
||||||
"track-added",
|
"track-added",
|
||||||
G_TYPE_FROM_CLASS(gobject_class),
|
G_TYPE_FROM_CLASS(gobject_class),
|
||||||
|
@ -175,6 +194,24 @@ static void koto_artist_class_init(KotoArtistClass * c) {
|
||||||
g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
|
g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void koto_artist_init(KotoArtist * self) {
|
||||||
|
self->albums = NULL; // Create a new GList
|
||||||
|
|
||||||
|
self->content_playlist = koto_playlist_new(); // Create our playlist
|
||||||
|
g_object_set(
|
||||||
|
self->content_playlist,
|
||||||
|
"ephemeral", // Indicate that it is temporary
|
||||||
|
TRUE,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
self->finalized = FALSE; // Indicate we not finalized
|
||||||
|
self->has_artist_art = FALSE;
|
||||||
|
self->paths = g_hash_table_new(g_str_hash, g_str_equal);
|
||||||
|
self->tracks = NULL;
|
||||||
|
self->type = KOTO_LIBRARY_TYPE_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
void koto_artist_commit(KotoArtist * self) {
|
void koto_artist_commit(KotoArtist * self) {
|
||||||
if ((self->uuid == NULL) || strcmp(self->uuid, "")) { // UUID not set
|
if ((self->uuid == NULL) || strcmp(self->uuid, "")) { // UUID not set
|
||||||
self->uuid = g_strdup(g_uuid_string_random());
|
self->uuid = g_strdup(g_uuid_string_random());
|
||||||
|
@ -211,12 +248,12 @@ void koto_artist_commit(KotoArtist * self) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void koto_artist_init(KotoArtist * self) {
|
KotoPlaylist * koto_artist_get_playlist(KotoArtist * self) {
|
||||||
self->albums = NULL; // Create a new GList
|
if (!KOTO_IS_ARTIST(self)) {
|
||||||
self->has_artist_art = FALSE;
|
return NULL;
|
||||||
self->paths = g_hash_table_new(g_str_hash, g_str_equal);
|
}
|
||||||
self->tracks = NULL;
|
|
||||||
self->type = KOTO_LIBRARY_TYPE_UNKNOWN;
|
return self->content_playlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void koto_artist_get_property(
|
static void koto_artist_get_property(
|
||||||
|
@ -311,6 +348,8 @@ void koto_artist_add_track(
|
||||||
koto_cartographer_add_track(koto_maps, track); // Add the track to cartographer if necessary
|
koto_cartographer_add_track(koto_maps, track); // Add the track to cartographer if necessary
|
||||||
self->tracks = g_list_insert_sorted_with_data(self->tracks, track_uuid, koto_track_helpers_sort_tracks_by_uuid, NULL);
|
self->tracks = g_list_insert_sorted_with_data(self->tracks, track_uuid, koto_track_helpers_sort_tracks_by_uuid, NULL);
|
||||||
|
|
||||||
|
koto_playlist_add_track(self->content_playlist, track, FALSE, FALSE); // Add this new track for the artist to its playlist
|
||||||
|
|
||||||
g_signal_emit(
|
g_signal_emit(
|
||||||
self,
|
self,
|
||||||
artist_signals[SIGNAL_TRACK_ADDED],
|
artist_signals[SIGNAL_TRACK_ADDED],
|
||||||
|
@ -408,8 +447,11 @@ void koto_artist_remove_track(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gchar * track_uuid = koto_track_get_uuid(track);
|
||||||
self->tracks = g_list_remove(self->tracks, koto_track_get_uuid(track));
|
self->tracks = g_list_remove(self->tracks, koto_track_get_uuid(track));
|
||||||
|
|
||||||
|
koto_playlist_remove_track_by_uuid(self->content_playlist, track_uuid); // Remove the track from our playlist
|
||||||
|
|
||||||
g_signal_emit(
|
g_signal_emit(
|
||||||
self,
|
self,
|
||||||
artist_signals[SIGNAL_TRACK_ADDED],
|
artist_signals[SIGNAL_TRACK_ADDED],
|
||||||
|
@ -438,6 +480,18 @@ void koto_artist_set_artist_name(
|
||||||
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_ARTIST_NAME]);
|
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_ARTIST_NAME]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void koto_artist_set_as_finalized(KotoArtist * self) {
|
||||||
|
if (!KOTO_IS_ARTIST(self)) { // Not an artist
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->finalized = TRUE;
|
||||||
|
|
||||||
|
if (g_list_length(self->albums) == 0) { // Have no albums
|
||||||
|
g_signal_emit_by_name(self, "has-no-albums");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void koto_artist_set_path(
|
void koto_artist_set_path(
|
||||||
KotoArtist * self,
|
KotoArtist * self,
|
||||||
KotoLibrary * lib,
|
KotoLibrary * lib,
|
||||||
|
|
|
@ -57,6 +57,7 @@ void index_folder(
|
||||||
koto_artist_set_path(artist, self, full_path, TRUE); // Add the path for this library on this Artist and commit immediately
|
koto_artist_set_path(artist, self, full_path, TRUE); // Add the path for this library on this Artist and commit immediately
|
||||||
koto_cartographer_add_artist(koto_maps, artist); // Add the artist to cartographer
|
koto_cartographer_add_artist(koto_maps, artist); // Add the artist to cartographer
|
||||||
index_folder(self, full_path, depth); // Index this directory
|
index_folder(self, full_path, depth); // Index this directory
|
||||||
|
koto_artist_set_as_finalized(artist); // Indicate it is finalized
|
||||||
}
|
}
|
||||||
} else if (depth == 2) { // If we are following FOLDER/ARTIST/ALBUM then this would be album
|
} else if (depth == 2) { // If we are following FOLDER/ARTIST/ALBUM then this would be album
|
||||||
gchar * artist_name = g_path_get_basename(path); // Get the last entry from our path which is probably the artist
|
gchar * artist_name = g_path_get_basename(path); // Get the last entry from our path which is probably the artist
|
||||||
|
@ -197,10 +198,6 @@ void index_file(
|
||||||
album = KOTO_IS_ALBUM(possible_album) ? possible_album : NULL;
|
album = KOTO_IS_ALBUM(possible_album) ? possible_album : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!KOTO_IS_ALBUM(album)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
gchar * album_uuid = KOTO_IS_ALBUM(album) ? koto_album_get_uuid(album) : NULL;
|
gchar * album_uuid = KOTO_IS_ALBUM(album) ? koto_album_get_uuid(album) : NULL;
|
||||||
|
|
||||||
track = koto_track_new(koto_artist_get_uuid(artist), album_uuid, file_name, cd);
|
track = koto_track_new(koto_artist_get_uuid(artist), album_uuid, file_name, cd);
|
||||||
|
|
|
@ -180,6 +180,8 @@ void koto_artist_set_artist_name(
|
||||||
gchar * artist_name
|
gchar * artist_name
|
||||||
);
|
);
|
||||||
|
|
||||||
|
void koto_artist_set_as_finalized(KotoArtist * self);
|
||||||
|
|
||||||
void koto_artist_set_path(
|
void koto_artist_set_path(
|
||||||
KotoArtist * self,
|
KotoArtist * self,
|
||||||
KotoLibrary * lib,
|
KotoLibrary * lib,
|
||||||
|
@ -282,6 +284,11 @@ void koto_track_remove_from_playlist(
|
||||||
gchar * playlist_uuid
|
gchar * playlist_uuid
|
||||||
);
|
);
|
||||||
|
|
||||||
|
void koto_track_set_album_uuid(
|
||||||
|
KotoTrack * self,
|
||||||
|
const gchar * album_uuid
|
||||||
|
);
|
||||||
|
|
||||||
void koto_track_save_to_playlist(
|
void koto_track_save_to_playlist(
|
||||||
KotoTrack * self,
|
KotoTrack * self,
|
||||||
gchar * playlist_uuid,
|
gchar * playlist_uuid,
|
||||||
|
|
|
@ -210,8 +210,7 @@ static void koto_track_set_property(
|
||||||
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_ARTIST_UUID]);
|
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_ARTIST_UUID]);
|
||||||
break;
|
break;
|
||||||
case PROP_ALBUM_UUID:
|
case PROP_ALBUM_UUID:
|
||||||
self->album_uuid = g_strdup(g_value_get_string(val));
|
koto_track_set_album_uuid(self, g_value_get_string(val));
|
||||||
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_ALBUM_UUID]);
|
|
||||||
break;
|
break;
|
||||||
case PROP_UUID:
|
case PROP_UUID:
|
||||||
self->uuid = g_strdup(g_value_get_string(val));
|
self->uuid = g_strdup(g_value_get_string(val));
|
||||||
|
@ -260,12 +259,14 @@ void koto_track_commit(KotoTrack * self) {
|
||||||
self->uuid,
|
self->uuid,
|
||||||
self->artist_uuid,
|
self->artist_uuid,
|
||||||
self->album_uuid,
|
self->album_uuid,
|
||||||
self->parsed_name,
|
g_strescape(self->parsed_name, NULL),
|
||||||
(int) self->cd,
|
(int) self->cd,
|
||||||
(int) self->position
|
(int) self->position
|
||||||
);
|
);
|
||||||
|
|
||||||
new_transaction(commit_op, "Failed to write our file to the database", FALSE);
|
if (new_transaction(commit_op, "Failed to write our file to the database", FALSE) != SQLITE_OK) {
|
||||||
|
g_message("Failed with song: %s", self->parsed_name);
|
||||||
|
}
|
||||||
|
|
||||||
GHashTableIter paths_iter;
|
GHashTableIter paths_iter;
|
||||||
g_hash_table_iter_init(&paths_iter, self->paths); // Create an iterator for our paths
|
g_hash_table_iter_init(&paths_iter, self->paths); // Create an iterator for our paths
|
||||||
|
@ -426,6 +427,28 @@ void koto_track_remove_from_playlist(
|
||||||
new_transaction(commit_op, "Failed to remove track from playlist", FALSE);
|
new_transaction(commit_op, "Failed to remove track from playlist", FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void koto_track_set_album_uuid(
|
||||||
|
KotoTrack * self,
|
||||||
|
const gchar * album_uuid
|
||||||
|
) {
|
||||||
|
if (!KOTO_IS_TRACK(self)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (album_uuid == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gchar * uuid = g_strdup(album_uuid);
|
||||||
|
|
||||||
|
if (!koto_utils_is_string_valid(uuid)) { // If this is not a valid string
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->album_uuid = uuid;
|
||||||
|
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_ALBUM_UUID]);
|
||||||
|
}
|
||||||
|
|
||||||
void koto_track_save_to_playlist(
|
void koto_track_save_to_playlist(
|
||||||
KotoTrack * self,
|
KotoTrack * self,
|
||||||
gchar * playlist_uuid,
|
gchar * playlist_uuid,
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "koto-button.h"
|
#include "koto-button.h"
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "koto-playerbar.h"
|
#include "koto-playerbar.h"
|
||||||
|
#include "koto-utils.h"
|
||||||
|
|
||||||
extern KotoAddRemoveTrackPopover * koto_add_remove_track_popup;
|
extern KotoAddRemoveTrackPopover * koto_add_remove_track_popup;
|
||||||
extern KotoCartographer * koto_maps;
|
extern KotoCartographer * koto_maps;
|
||||||
|
@ -616,10 +617,8 @@ void koto_playerbar_update_track_info(
|
||||||
g_object_get(current_track, "parsed-name", &track_name, "artist-uuid", &artist_uuid, "album-uuid", &album_uuid, NULL);
|
g_object_get(current_track, "parsed-name", &track_name, "artist-uuid", &artist_uuid, "album-uuid", &album_uuid, NULL);
|
||||||
|
|
||||||
KotoArtist * artist = koto_cartographer_get_artist_by_uuid(koto_maps, artist_uuid);
|
KotoArtist * artist = koto_cartographer_get_artist_by_uuid(koto_maps, artist_uuid);
|
||||||
KotoAlbum * album = koto_cartographer_get_album_by_uuid(koto_maps, album_uuid);
|
|
||||||
|
|
||||||
g_free(artist_uuid);
|
g_free(artist_uuid);
|
||||||
g_free(album_uuid);
|
|
||||||
|
|
||||||
if ((track_name != NULL) && (strcmp(track_name, "") != 0)) { // Have a track name
|
if ((track_name != NULL) && (strcmp(track_name, "") != 0)) { // Have a track name
|
||||||
gtk_label_set_text(GTK_LABEL(bar->playback_title), track_name); // Set the label
|
gtk_label_set_text(GTK_LABEL(bar->playback_title), track_name); // Set the label
|
||||||
|
@ -637,6 +636,13 @@ void koto_playerbar_update_track_info(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!koto_utils_is_string_valid(album_uuid)) { // Do not have a valid album UUID
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
KotoAlbum * album = koto_cartographer_get_album_by_uuid(koto_maps, album_uuid);
|
||||||
|
g_free(album_uuid);
|
||||||
|
|
||||||
if (KOTO_IS_ALBUM(album)) {
|
if (KOTO_IS_ALBUM(album)) {
|
||||||
gchar * album_name = NULL;
|
gchar * album_name = NULL;
|
||||||
gchar * art_path = NULL;
|
gchar * art_path = NULL;
|
||||||
|
|
|
@ -46,6 +46,9 @@ extern GList * supported_mimes;
|
||||||
|
|
||||||
extern gchar * koto_path_to_conf;
|
extern gchar * koto_path_to_conf;
|
||||||
extern gchar * koto_rev_dns;
|
extern gchar * koto_rev_dns;
|
||||||
|
|
||||||
|
extern gboolean created_new_db;
|
||||||
|
|
||||||
GVolumeMonitor * volume_monitor = NULL;
|
GVolumeMonitor * volume_monitor = NULL;
|
||||||
GtkApplication * app = NULL;
|
GtkApplication * app = NULL;
|
||||||
GtkWindow * main_window;
|
GtkWindow * main_window;
|
||||||
|
@ -59,8 +62,11 @@ static void on_activate (GtkApplication * app) {
|
||||||
main_window = g_object_new(KOTO_TYPE_WINDOW, "application", app, "default-width", 1200, "default-height", 675, NULL);
|
main_window = g_object_new(KOTO_TYPE_WINDOW, "application", app, "default-width", 1200, "default-height", 675, NULL);
|
||||||
setup_mpris_interfaces(); // Set up our MPRIS interfaces
|
setup_mpris_interfaces(); // Set up our MPRIS interfaces
|
||||||
setup_mediakeys_interface(); // Set up our media key support
|
setup_mediakeys_interface(); // Set up our media key support
|
||||||
|
|
||||||
|
if (!created_new_db) {
|
||||||
read_from_db(); // Read the database, allowing us to propagate the UI with various data such as artists and playlist navigation elements
|
read_from_db(); // Read the database, allowing us to propagate the UI with various data such as artists and playlist navigation elements
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
gtk_window_present(main_window);
|
gtk_window_present(main_window);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ add_global_arguments([
|
||||||
koto_sources = [
|
koto_sources = [
|
||||||
'components/koto-action-bar.c',
|
'components/koto-action-bar.c',
|
||||||
'components/koto-cover-art-button.c',
|
'components/koto-cover-art-button.c',
|
||||||
|
'components/track-table.c',
|
||||||
'config/config.c',
|
'config/config.c',
|
||||||
'db/cartographer.c',
|
'db/cartographer.c',
|
||||||
'db/db.c',
|
'db/db.c',
|
||||||
|
|
|
@ -17,13 +17,18 @@
|
||||||
|
|
||||||
#include <glib-2.0/glib.h>
|
#include <glib-2.0/glib.h>
|
||||||
#include <gtk-4.0/gtk/gtk.h>
|
#include <gtk-4.0/gtk/gtk.h>
|
||||||
|
#include "../../components/koto-cover-art-button.h"
|
||||||
|
#include "../../components/track-table.h"
|
||||||
#include "../../db/cartographer.h"
|
#include "../../db/cartographer.h"
|
||||||
|
#include "../../indexer/artist-playlist-funcs.h"
|
||||||
#include "../../indexer/structs.h"
|
#include "../../indexer/structs.h"
|
||||||
|
#include "../../playlist/current.h"
|
||||||
#include "album-view.h"
|
#include "album-view.h"
|
||||||
#include "artist-view.h"
|
#include "artist-view.h"
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "koto-utils.h"
|
#include "koto-utils.h"
|
||||||
|
|
||||||
|
extern KotoCurrentPlaylist * current_playlist;
|
||||||
extern KotoCartographer * koto_maps;
|
extern KotoCartographer * koto_maps;
|
||||||
|
|
||||||
struct _KotoArtistView {
|
struct _KotoArtistView {
|
||||||
|
@ -33,6 +38,12 @@ struct _KotoArtistView {
|
||||||
GtkWidget * content;
|
GtkWidget * content;
|
||||||
GtkWidget * album_list;
|
GtkWidget * album_list;
|
||||||
|
|
||||||
|
GtkWidget * no_albums_view;
|
||||||
|
GtkWidget * no_albums_artist_header;
|
||||||
|
KotoCoverArtButton * no_albums_artist_button;
|
||||||
|
GtkWidget * no_albums_artist_label;
|
||||||
|
|
||||||
|
KotoTrackTable * table;
|
||||||
GHashTable * albums_to_component;
|
GHashTable * albums_to_component;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -47,7 +58,6 @@ enum {
|
||||||
static GParamSpec * props[N_PROPERTIES] = {
|
static GParamSpec * props[N_PROPERTIES] = {
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
static void koto_artist_view_constructed(GObject * obj);
|
|
||||||
|
|
||||||
static void koto_artist_view_get_property(
|
static void koto_artist_view_get_property(
|
||||||
GObject * obj,
|
GObject * obj,
|
||||||
|
@ -67,7 +77,6 @@ static void koto_artist_view_class_init(KotoArtistViewClass * c) {
|
||||||
GObjectClass * gobject_class;
|
GObjectClass * gobject_class;
|
||||||
|
|
||||||
gobject_class = G_OBJECT_CLASS(c);
|
gobject_class = G_OBJECT_CLASS(c);
|
||||||
gobject_class->constructed = koto_artist_view_constructed;
|
|
||||||
gobject_class->set_property = koto_artist_view_set_property;
|
gobject_class->set_property = koto_artist_view_set_property;
|
||||||
gobject_class->get_property = koto_artist_view_get_property;
|
gobject_class->get_property = koto_artist_view_get_property;
|
||||||
|
|
||||||
|
@ -82,6 +91,50 @@ static void koto_artist_view_class_init(KotoArtistViewClass * c) {
|
||||||
g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
|
g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void koto_artist_view_init(KotoArtistView * self) {
|
||||||
|
self->artist = NULL;
|
||||||
|
self->albums_to_component = g_hash_table_new(g_str_hash, g_str_equal);
|
||||||
|
|
||||||
|
self->scrolled_window = gtk_scrolled_window_new(); // Create our scrolled window
|
||||||
|
self->content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); // Create our content as a GtkBox
|
||||||
|
|
||||||
|
gtk_scrolled_window_set_propagate_natural_height(GTK_SCROLLED_WINDOW(self->scrolled_window), TRUE);
|
||||||
|
gtk_scrolled_window_set_propagate_natural_width(GTK_SCROLLED_WINDOW(self->scrolled_window), TRUE);
|
||||||
|
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(self->scrolled_window), self->content); // Add the content as the widget for the scrolled window
|
||||||
|
gtk_widget_add_css_class(GTK_WIDGET(self->scrolled_window), "artist-view");
|
||||||
|
gtk_widget_add_css_class(GTK_WIDGET(self->content), "artist-view-content");
|
||||||
|
|
||||||
|
self->album_list = gtk_flow_box_new(); // Create our list of our albums
|
||||||
|
gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(self->album_list), GTK_SELECTION_NONE);
|
||||||
|
gtk_widget_add_css_class(self->album_list, "album-list");
|
||||||
|
|
||||||
|
self->no_albums_view = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
||||||
|
gtk_widget_add_css_class(self->no_albums_view, "no-albums-view");
|
||||||
|
|
||||||
|
self->no_albums_artist_header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
|
||||||
|
gtk_widget_add_css_class(self->no_albums_artist_header, "no-albums-view-header"),
|
||||||
|
|
||||||
|
self->no_albums_artist_button = koto_cover_art_button_new(220, 220, NULL);
|
||||||
|
KotoButton * cover_art_button = koto_cover_art_button_get_button(self->no_albums_artist_button); // Get the button for the KotoCoverArt
|
||||||
|
koto_button_add_click_handler(cover_art_button, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_artist_view_toggle_playback), self); // Handle clicking the button
|
||||||
|
|
||||||
|
self->no_albums_artist_label = gtk_label_new(NULL); // Create a label without any artist name
|
||||||
|
|
||||||
|
gtk_box_append(GTK_BOX(self->no_albums_artist_header), koto_cover_art_button_get_main(self->no_albums_artist_button)); // Add our button to the header
|
||||||
|
gtk_box_append(GTK_BOX(self->no_albums_artist_header), self->no_albums_artist_label);
|
||||||
|
|
||||||
|
gtk_box_append(GTK_BOX(self->no_albums_view), self->no_albums_artist_header); // Add the header to the no albums view
|
||||||
|
|
||||||
|
self->table = koto_track_table_new(); //Create our track table
|
||||||
|
gtk_box_append(GTK_BOX(self->no_albums_view), koto_track_table_get_main(self->table)); // Add the table to the no albums view
|
||||||
|
|
||||||
|
gtk_box_append(GTK_BOX(self->content), self->album_list); // Add the album flowbox
|
||||||
|
gtk_box_append(GTK_BOX(self->content), self->no_albums_view); // Add the no albums view just in case we do not have any albums
|
||||||
|
|
||||||
|
gtk_widget_set_hexpand(GTK_WIDGET(self->album_list), TRUE);
|
||||||
|
gtk_widget_set_hexpand(GTK_WIDGET(self->no_albums_view), TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
static void koto_artist_view_get_property(
|
static void koto_artist_view_get_property(
|
||||||
GObject * obj,
|
GObject * obj,
|
||||||
guint prop_id,
|
guint prop_id,
|
||||||
|
@ -118,34 +171,6 @@ static void koto_artist_view_set_property(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void koto_artist_view_init(KotoArtistView * self) {
|
|
||||||
self->artist = NULL;
|
|
||||||
self->albums_to_component = g_hash_table_new(g_str_hash, g_str_equal);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void koto_artist_view_constructed(GObject * obj) {
|
|
||||||
KotoArtistView * self = KOTO_ARTIST_VIEW(obj);
|
|
||||||
|
|
||||||
self->scrolled_window = gtk_scrolled_window_new(); // Create our scrolled window
|
|
||||||
self->content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); // Create our content as a GtkBox
|
|
||||||
|
|
||||||
gtk_scrolled_window_set_propagate_natural_height(GTK_SCROLLED_WINDOW(self->scrolled_window), TRUE);
|
|
||||||
gtk_scrolled_window_set_propagate_natural_width(GTK_SCROLLED_WINDOW(self->scrolled_window), TRUE);
|
|
||||||
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(self->scrolled_window), self->content); // Add the content as the widget for the scrolled window
|
|
||||||
gtk_widget_add_css_class(GTK_WIDGET(self->scrolled_window), "artist-view");
|
|
||||||
gtk_widget_add_css_class(GTK_WIDGET(self->content), "artist-view-content");
|
|
||||||
|
|
||||||
self->album_list = gtk_flow_box_new(); // Create our list of our albums
|
|
||||||
gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(self->album_list), GTK_SELECTION_NONE);
|
|
||||||
gtk_widget_add_css_class(self->album_list, "album-list");
|
|
||||||
|
|
||||||
gtk_box_append(GTK_BOX(self->content), self->album_list); // Add the list
|
|
||||||
|
|
||||||
gtk_widget_set_hexpand(GTK_WIDGET(self->album_list), TRUE);
|
|
||||||
|
|
||||||
G_OBJECT_CLASS(koto_artist_view_parent_class)->constructed(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
void koto_artist_view_add_album(
|
void koto_artist_view_add_album(
|
||||||
KotoArtistView * self,
|
KotoArtistView * self,
|
||||||
KotoAlbum * album
|
KotoAlbum * album
|
||||||
|
@ -169,6 +194,13 @@ void koto_artist_view_add_album(
|
||||||
|
|
||||||
gtk_flow_box_insert(GTK_FLOW_BOX(self->album_list), album_view_main, -1); // Append the album view to the album list
|
gtk_flow_box_insert(GTK_FLOW_BOX(self->album_list), album_view_main, -1); // Append the album view to the album list
|
||||||
g_hash_table_replace(self->albums_to_component, album_uuid, album_view_main);
|
g_hash_table_replace(self->albums_to_component, album_uuid, album_view_main);
|
||||||
|
|
||||||
|
gtk_widget_hide(self->no_albums_view); // Hide the no album view
|
||||||
|
gtk_widget_show(self->album_list); // Show the album list now that it has albums
|
||||||
|
}
|
||||||
|
|
||||||
|
GtkWidget * koto_artist_view_get_main(KotoArtistView * self) {
|
||||||
|
return self->scrolled_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
void koto_artist_view_handle_album_added(
|
void koto_artist_view_handle_album_added(
|
||||||
|
@ -222,6 +254,30 @@ void koto_artist_view_handle_album_removed(
|
||||||
g_hash_table_remove(self->albums_to_component, album_uuid); // Remove the album from our hash table
|
g_hash_table_remove(self->albums_to_component, album_uuid); // Remove the album from our hash table
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void koto_artist_view_handle_artist_name_changed(
|
||||||
|
KotoArtist * artist,
|
||||||
|
guint prop_id,
|
||||||
|
KotoArtistView * self
|
||||||
|
) {
|
||||||
|
(void) prop_id;
|
||||||
|
gtk_label_set_text(GTK_LABEL(self->no_albums_artist_label), koto_artist_get_name(artist)); // Update the label for the artist now that it has changed
|
||||||
|
}
|
||||||
|
|
||||||
|
void koto_artist_view_handle_has_no_albums(
|
||||||
|
KotoArtist * artist,
|
||||||
|
gpointer user_data
|
||||||
|
) {
|
||||||
|
(void) artist;
|
||||||
|
KotoArtistView * self = user_data;
|
||||||
|
|
||||||
|
if (!KOTO_IS_ARTIST_VIEW(self)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_widget_hide(self->album_list); // Hide our album list flowbox
|
||||||
|
gtk_widget_show(self->no_albums_view); // Show the no albums view
|
||||||
|
}
|
||||||
|
|
||||||
void koto_artist_view_set_artist(
|
void koto_artist_view_set_artist(
|
||||||
KotoArtistView * self,
|
KotoArtistView * self,
|
||||||
KotoArtist * artist
|
KotoArtist * artist
|
||||||
|
@ -235,12 +291,39 @@ void koto_artist_view_set_artist(
|
||||||
}
|
}
|
||||||
|
|
||||||
self->artist = artist;
|
self->artist = artist;
|
||||||
|
koto_track_table_set_playlist(self->table, koto_artist_get_playlist(self->artist)); // Set our track table to the artist's playlist
|
||||||
|
gtk_label_set_text(GTK_LABEL(self->no_albums_artist_label), koto_artist_get_name(self->artist)); // Update our label with the name of the artist
|
||||||
|
|
||||||
g_signal_connect(artist, "album-added", G_CALLBACK(koto_artist_view_handle_album_added), self);
|
g_signal_connect(artist, "album-added", G_CALLBACK(koto_artist_view_handle_album_added), self);
|
||||||
g_signal_connect(artist, "album-removed", G_CALLBACK(koto_artist_view_handle_album_removed), self);
|
g_signal_connect(artist, "album-removed", G_CALLBACK(koto_artist_view_handle_album_removed), self);
|
||||||
|
g_signal_connect(artist, "has-no-albums", G_CALLBACK(koto_artist_view_handle_has_no_albums), self);
|
||||||
|
g_signal_connect(artist, "notify::name", G_CALLBACK(koto_artist_view_handle_artist_name_changed), self);
|
||||||
}
|
}
|
||||||
|
|
||||||
GtkWidget * koto_artist_view_get_main(KotoArtistView * self) {
|
void koto_artist_view_toggle_playback(
|
||||||
return self->scrolled_window;
|
GtkGestureClick * gesture,
|
||||||
|
int n_press,
|
||||||
|
double x,
|
||||||
|
double y,
|
||||||
|
gpointer data
|
||||||
|
) {
|
||||||
|
(void) gesture;
|
||||||
|
(void) n_press;
|
||||||
|
(void) x;
|
||||||
|
(void) y;
|
||||||
|
KotoArtistView * self = data;
|
||||||
|
|
||||||
|
if (!KOTO_IS_ARTIST_VIEW(self)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
KotoPlaylist * artist_playlist = koto_artist_get_playlist(self->artist);
|
||||||
|
|
||||||
|
if (!KOTO_IS_PLAYLIST(artist_playlist)) { // Failed our is playlist check for the artist playlist
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
koto_current_playlist_set_playlist(current_playlist, artist_playlist); // Set our playlist to the one associated with the Artist
|
||||||
}
|
}
|
||||||
|
|
||||||
KotoArtistView * koto_artist_view_new(KotoArtist * artist) {
|
KotoArtistView * koto_artist_view_new(KotoArtist * artist) {
|
||||||
|
|
|
@ -34,6 +34,8 @@ void koto_artist_view_add_album(
|
||||||
KotoAlbum * album
|
KotoAlbum * album
|
||||||
);
|
);
|
||||||
|
|
||||||
|
GtkWidget * koto_artist_view_get_main(KotoArtistView * self);
|
||||||
|
|
||||||
void koto_artist_view_handle_album_added(
|
void koto_artist_view_handle_album_added(
|
||||||
KotoArtist * artist,
|
KotoArtist * artist,
|
||||||
KotoAlbum * album,
|
KotoAlbum * album,
|
||||||
|
@ -46,11 +48,28 @@ void koto_artist_view_handle_album_removed(
|
||||||
gpointer user_data
|
gpointer user_data
|
||||||
);
|
);
|
||||||
|
|
||||||
|
void koto_artist_view_handle_artist_name_changed(
|
||||||
|
KotoArtist * artist,
|
||||||
|
guint prop_id,
|
||||||
|
KotoArtistView * self
|
||||||
|
);
|
||||||
|
|
||||||
|
void koto_artist_view_handle_has_no_albums(
|
||||||
|
KotoArtist * artist,
|
||||||
|
gpointer user_data
|
||||||
|
);
|
||||||
|
|
||||||
void koto_artist_view_set_artist(
|
void koto_artist_view_set_artist(
|
||||||
KotoArtistView * self,
|
KotoArtistView * self,
|
||||||
KotoArtist * artist
|
KotoArtist * artist
|
||||||
);
|
);
|
||||||
|
|
||||||
GtkWidget * koto_artist_view_get_main(KotoArtistView * self);
|
void koto_artist_view_toggle_playback(
|
||||||
|
GtkGestureClick * gesture,
|
||||||
|
int n_press,
|
||||||
|
double x,
|
||||||
|
double y,
|
||||||
|
gpointer data
|
||||||
|
);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
|
@ -19,7 +19,9 @@
|
||||||
#include <gtk-4.0/gtk/gtk.h>
|
#include <gtk-4.0/gtk/gtk.h>
|
||||||
#include "../../components/koto-action-bar.h"
|
#include "../../components/koto-action-bar.h"
|
||||||
#include "../../components/koto-cover-art-button.h"
|
#include "../../components/koto-cover-art-button.h"
|
||||||
|
#include "../../components/track-table.h"
|
||||||
#include "../../db/cartographer.h"
|
#include "../../db/cartographer.h"
|
||||||
|
#include "../../indexer/structs.h"
|
||||||
#include "../../playlist/current.h"
|
#include "../../playlist/current.h"
|
||||||
#include "../../playlist/playlist.h"
|
#include "../../playlist/playlist.h"
|
||||||
#include "../../koto-button.h"
|
#include "../../koto-button.h"
|
||||||
|
@ -59,23 +61,7 @@ struct _KotoPlaylistPage {
|
||||||
KotoButton * favorite_button;
|
KotoButton * favorite_button;
|
||||||
KotoButton * edit_button;
|
KotoButton * edit_button;
|
||||||
|
|
||||||
GtkListItemFactory * item_factory;
|
KotoTrackTable * table;
|
||||||
GListModel * model;
|
|
||||||
GtkSelectionModel * selection_model;
|
|
||||||
|
|
||||||
GtkWidget * track_list_content;
|
|
||||||
GtkWidget * track_list_header;
|
|
||||||
GtkWidget * track_list_view;
|
|
||||||
|
|
||||||
KotoButton * track_num_button;
|
|
||||||
KotoButton * track_title_button;
|
|
||||||
KotoButton * track_album_button;
|
|
||||||
KotoButton * track_artist_button;
|
|
||||||
|
|
||||||
GtkSizeGroup * track_pos_size_group;
|
|
||||||
GtkSizeGroup * track_name_size_group;
|
|
||||||
GtkSizeGroup * track_album_size_group;
|
|
||||||
GtkSizeGroup * track_artist_size_group;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _KotoPlaylistPageClass {
|
struct _KotoPlaylistPageClass {
|
||||||
|
@ -117,11 +103,6 @@ static void koto_playlist_page_class_init(KotoPlaylistPageClass * c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void koto_playlist_page_init(KotoPlaylistPage * self) {
|
static void koto_playlist_page_init(KotoPlaylistPage * self) {
|
||||||
self->track_name_size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
|
|
||||||
self->track_pos_size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
|
|
||||||
self->track_album_size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
|
|
||||||
self->track_artist_size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
|
|
||||||
|
|
||||||
self->main = gtk_scrolled_window_new(); // Create our scrolled window
|
self->main = gtk_scrolled_window_new(); // Create our scrolled window
|
||||||
self->content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
self->content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
||||||
gtk_widget_add_css_class(self->content, "playlist-page");
|
gtk_widget_add_css_class(self->content, "playlist-page");
|
||||||
|
@ -166,30 +147,9 @@ static void koto_playlist_page_init(KotoPlaylistPage * self) {
|
||||||
gtk_box_append(GTK_BOX(self->header), GTK_WIDGET(self->favorite_button)); // Add the favorite button
|
gtk_box_append(GTK_BOX(self->header), GTK_WIDGET(self->favorite_button)); // Add the favorite button
|
||||||
gtk_box_append(GTK_BOX(self->header), GTK_WIDGET(self->edit_button)); // Add the edit button
|
gtk_box_append(GTK_BOX(self->header), GTK_WIDGET(self->edit_button)); // Add the edit button
|
||||||
|
|
||||||
self->item_factory = gtk_signal_list_item_factory_new(); // Create a new signal list item factory
|
self->table = koto_track_table_new(); // Create our new KotoTrackTable
|
||||||
g_signal_connect(self->item_factory, "setup", G_CALLBACK(koto_playlist_page_setup_track_item), self);
|
GtkWidget * track_main = koto_track_table_get_main(self->table);
|
||||||
g_signal_connect(self->item_factory, "bind", G_CALLBACK(koto_playlist_page_bind_track_item), self);
|
gtk_box_append(GTK_BOX(self->content), track_main);
|
||||||
|
|
||||||
self->track_list_content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
|
||||||
gtk_widget_add_css_class((self->track_list_content), "track-list-content");
|
|
||||||
gtk_widget_set_hexpand(self->track_list_content, TRUE); // Expand horizontally
|
|
||||||
gtk_widget_set_vexpand(self->track_list_content, TRUE); // Expand vertically
|
|
||||||
|
|
||||||
self->track_list_header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
|
|
||||||
gtk_widget_add_css_class(self->track_list_header, "track-list-header");
|
|
||||||
koto_playlist_page_create_tracks_header(self); // Create our tracks header content
|
|
||||||
|
|
||||||
self->track_list_view = gtk_list_view_new(NULL, self->item_factory); // Create our list view with no model yet
|
|
||||||
gtk_widget_add_css_class(self->track_list_view, "track-list-columned");
|
|
||||||
gtk_widget_set_hexpand(self->track_list_view, TRUE); // Expand horizontally
|
|
||||||
gtk_widget_set_vexpand(self->track_list_view, TRUE); // Expand vertically
|
|
||||||
|
|
||||||
gtk_box_append(GTK_BOX(self->track_list_content), self->track_list_header);
|
|
||||||
gtk_box_append(GTK_BOX(self->track_list_content), self->track_list_view);
|
|
||||||
|
|
||||||
gtk_box_append(GTK_BOX(self->content), self->track_list_content);
|
|
||||||
|
|
||||||
g_signal_connect(action_bar, "closed", G_CALLBACK(koto_playlist_page_handle_action_bar_closed), self); // Handle closed action bar
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void koto_playlist_page_get_property(
|
static void koto_playlist_page_get_property(
|
||||||
|
@ -228,104 +188,10 @@ static void koto_playlist_page_set_property(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void koto_playlist_page_bind_track_item(
|
|
||||||
GtkListItemFactory * factory,
|
|
||||||
GtkListItem * item,
|
|
||||||
KotoPlaylistPage * self
|
|
||||||
) {
|
|
||||||
(void) factory;
|
|
||||||
|
|
||||||
GtkWidget * track_position_label = gtk_widget_get_first_child(gtk_list_item_get_child(item));
|
|
||||||
GtkWidget * track_name_label = gtk_widget_get_next_sibling(track_position_label);
|
|
||||||
GtkWidget * track_album_label = gtk_widget_get_next_sibling(track_name_label);
|
|
||||||
GtkWidget * track_artist_label = gtk_widget_get_next_sibling(track_album_label);
|
|
||||||
|
|
||||||
KotoTrack * track = gtk_list_item_get_item(item); // Get the track UUID from our model
|
|
||||||
|
|
||||||
if (!KOTO_IS_TRACK(track)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
gchar * track_name = NULL;
|
|
||||||
gchar * album_uuid = NULL;
|
|
||||||
gchar * artist_uuid = NULL;
|
|
||||||
|
|
||||||
g_object_get(
|
|
||||||
track,
|
|
||||||
"parsed-name",
|
|
||||||
&track_name,
|
|
||||||
"album-uuid",
|
|
||||||
&album_uuid,
|
|
||||||
"artist-uuid",
|
|
||||||
&artist_uuid,
|
|
||||||
NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
guint track_position = koto_playlist_get_position_of_track(self->playlist, track) + 1;
|
|
||||||
|
|
||||||
gtk_label_set_label(GTK_LABEL(track_position_label), g_strdup_printf("%u", track_position)); // Set the track position
|
|
||||||
gtk_label_set_label(GTK_LABEL(track_name_label), track_name); // Set our track name
|
|
||||||
|
|
||||||
KotoAlbum * album = koto_cartographer_get_album_by_uuid(koto_maps, album_uuid);
|
|
||||||
|
|
||||||
if (KOTO_IS_ALBUM(album)) {
|
|
||||||
gtk_label_set_label(GTK_LABEL(track_album_label), koto_album_get_name(album)); // Get the name of the album and set it to the label
|
|
||||||
}
|
|
||||||
|
|
||||||
KotoArtist * artist = koto_cartographer_get_artist_by_uuid(koto_maps, artist_uuid);
|
|
||||||
|
|
||||||
if (KOTO_IS_ARTIST(artist)) {
|
|
||||||
gtk_label_set_label(GTK_LABEL(track_artist_label), koto_artist_get_name(artist)); // Get the name of the artist and set it to the label
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void koto_playlist_page_create_tracks_header(KotoPlaylistPage * self) {
|
|
||||||
self->track_num_button = koto_button_new_with_icon("#", "pan-down-symbolic", "pan-up-symbolic", KOTO_BUTTON_PIXBUF_SIZE_SMALL);
|
|
||||||
koto_button_add_click_handler(self->track_num_button, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_playlist_page_handle_track_num_clicked), self);
|
|
||||||
koto_button_set_image_position(self->track_num_button, KOTO_BUTTON_IMAGE_POS_RIGHT); // Move the image to the right
|
|
||||||
gtk_size_group_add_widget(self->track_pos_size_group, GTK_WIDGET(self->track_num_button));
|
|
||||||
|
|
||||||
self->track_title_button = koto_button_new_plain("Title");
|
|
||||||
koto_button_add_click_handler(self->track_title_button, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_playlist_page_handle_track_name_clicked), self);
|
|
||||||
gtk_size_group_add_widget(self->track_name_size_group, GTK_WIDGET(self->track_title_button));
|
|
||||||
|
|
||||||
self->track_album_button = koto_button_new_plain("Album");
|
|
||||||
|
|
||||||
gtk_widget_set_margin_start(GTK_WIDGET(self->track_album_button), 50);
|
|
||||||
koto_button_add_click_handler(self->track_album_button, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_playlist_page_handle_track_album_clicked), self);
|
|
||||||
gtk_size_group_add_widget(self->track_album_size_group, GTK_WIDGET(self->track_album_button));
|
|
||||||
|
|
||||||
self->track_artist_button = koto_button_new_plain("Artist");
|
|
||||||
|
|
||||||
gtk_widget_set_margin_start(GTK_WIDGET(self->track_artist_button), 50);
|
|
||||||
koto_button_add_click_handler(self->track_artist_button, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_playlist_page_handle_track_artist_clicked), self);
|
|
||||||
gtk_size_group_add_widget(self->track_artist_size_group, GTK_WIDGET(self->track_artist_button));
|
|
||||||
|
|
||||||
gtk_box_append(GTK_BOX(self->track_list_header), GTK_WIDGET(self->track_num_button));
|
|
||||||
gtk_box_append(GTK_BOX(self->track_list_header), GTK_WIDGET(self->track_title_button));
|
|
||||||
gtk_box_append(GTK_BOX(self->track_list_header), GTK_WIDGET(self->track_album_button));
|
|
||||||
gtk_box_append(GTK_BOX(self->track_list_header), GTK_WIDGET(self->track_artist_button));
|
|
||||||
}
|
|
||||||
|
|
||||||
GtkWidget * koto_playlist_page_get_main(KotoPlaylistPage * self) {
|
GtkWidget * koto_playlist_page_get_main(KotoPlaylistPage * self) {
|
||||||
return self->main;
|
return self->main;
|
||||||
}
|
}
|
||||||
|
|
||||||
void koto_playlist_page_handle_action_bar_closed(
|
|
||||||
KotoActionBar * bar,
|
|
||||||
gpointer data
|
|
||||||
) {
|
|
||||||
(void) bar;
|
|
||||||
KotoPlaylistPage * self = data;
|
|
||||||
|
|
||||||
if (!KOTO_IS_PLAYLIST(self->playlist)) { // No playlist set
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
gtk_selection_model_unselect_all(self->selection_model);
|
|
||||||
gtk_widget_grab_focus(GTK_WIDGET(main_window)); // Focus on the window
|
|
||||||
}
|
|
||||||
|
|
||||||
void koto_playlist_page_handle_cover_art_clicked(
|
void koto_playlist_page_handle_cover_art_clicked(
|
||||||
GtkGestureClick * gesture,
|
GtkGestureClick * gesture,
|
||||||
int n_press,
|
int n_press,
|
||||||
|
@ -390,132 +256,6 @@ void koto_playlist_page_handle_playlist_modified(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void koto_playlist_page_handle_track_album_clicked(
|
|
||||||
GtkGestureClick * gesture,
|
|
||||||
int n_press,
|
|
||||||
double x,
|
|
||||||
double y,
|
|
||||||
gpointer user_data
|
|
||||||
) {
|
|
||||||
(void) gesture;
|
|
||||||
(void) n_press;
|
|
||||||
(void) x;
|
|
||||||
(void) y;
|
|
||||||
KotoPlaylistPage * self = user_data;
|
|
||||||
|
|
||||||
gtk_widget_add_css_class(GTK_WIDGET(self->track_album_button), "active");
|
|
||||||
koto_button_hide_image(self->track_num_button); // Go back to hiding the image
|
|
||||||
koto_playlist_page_set_playlist_model(self, KOTO_PREFERRED_MODEL_TYPE_SORT_BY_ALBUM);
|
|
||||||
}
|
|
||||||
|
|
||||||
void koto_playlist_page_handle_track_artist_clicked(
|
|
||||||
GtkGestureClick * gesture,
|
|
||||||
int n_press,
|
|
||||||
double x,
|
|
||||||
double y,
|
|
||||||
gpointer user_data
|
|
||||||
) {
|
|
||||||
(void) gesture;
|
|
||||||
(void) n_press;
|
|
||||||
(void) x;
|
|
||||||
(void) y;
|
|
||||||
KotoPlaylistPage * self = user_data;
|
|
||||||
|
|
||||||
gtk_widget_add_css_class(GTK_WIDGET(self->track_artist_button), "active");
|
|
||||||
koto_button_hide_image(self->track_num_button); // Go back to hiding the image
|
|
||||||
koto_playlist_page_set_playlist_model(self, KOTO_PREFERRED_MODEL_TYPE_SORT_BY_ARTIST);
|
|
||||||
}
|
|
||||||
|
|
||||||
void koto_playlist_page_handle_track_name_clicked(
|
|
||||||
GtkGestureClick * gesture,
|
|
||||||
int n_press,
|
|
||||||
double x,
|
|
||||||
double y,
|
|
||||||
gpointer user_data
|
|
||||||
) {
|
|
||||||
(void) gesture;
|
|
||||||
(void) n_press;
|
|
||||||
(void) x;
|
|
||||||
(void) y;
|
|
||||||
KotoPlaylistPage * self = user_data;
|
|
||||||
|
|
||||||
gtk_widget_add_css_class(GTK_WIDGET(self->track_title_button), "active");
|
|
||||||
koto_button_hide_image(self->track_num_button); // Go back to hiding the image
|
|
||||||
koto_playlist_page_set_playlist_model(self, KOTO_PREFERRED_MODEL_TYPE_SORT_BY_TRACK_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
void koto_playlist_page_handle_track_num_clicked(
|
|
||||||
GtkGestureClick * gesture,
|
|
||||||
int n_press,
|
|
||||||
double x,
|
|
||||||
double y,
|
|
||||||
gpointer user_data
|
|
||||||
) {
|
|
||||||
(void) gesture;
|
|
||||||
(void) n_press;
|
|
||||||
(void) x;
|
|
||||||
(void) y;
|
|
||||||
KotoPlaylistPage * self = user_data;
|
|
||||||
|
|
||||||
KotoPreferredModelType current_model = koto_playlist_get_current_model(self->playlist);
|
|
||||||
|
|
||||||
if (current_model == KOTO_PREFERRED_MODEL_TYPE_DEFAULT) { // Set to newest currently
|
|
||||||
koto_playlist_page_set_playlist_model(self, KOTO_PREFERRED_MODEL_TYPE_OLDEST_FIRST); // Sort reversed (oldest)
|
|
||||||
koto_button_show_image(self->track_num_button, TRUE); // Use inverted value (pan-up-symbolic)
|
|
||||||
} else {
|
|
||||||
koto_playlist_page_set_playlist_model(self, KOTO_PREFERRED_MODEL_TYPE_DEFAULT); // Sort newest
|
|
||||||
koto_button_show_image(self->track_num_button, FALSE); // Use pan down default
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void koto_playlist_page_handle_tracks_selected(
|
|
||||||
GtkSelectionModel * model,
|
|
||||||
guint position,
|
|
||||||
guint n_items,
|
|
||||||
gpointer user_data
|
|
||||||
) {
|
|
||||||
(void) position;
|
|
||||||
KotoPlaylistPage * self = user_data;
|
|
||||||
|
|
||||||
if (n_items == 0) { // No items selected
|
|
||||||
koto_action_bar_toggle_reveal(action_bar, FALSE); // Hide the action bar
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GtkBitset * selected_items_bitset = gtk_selection_model_get_selection(model); // Get the selected items as a GtkBitSet
|
|
||||||
GtkBitsetIter iter;
|
|
||||||
GList * selected_tracks = NULL;
|
|
||||||
GList * selected_tracks_pos = NULL;
|
|
||||||
|
|
||||||
guint first_track_pos;
|
|
||||||
|
|
||||||
if (!gtk_bitset_iter_init_first(&iter, selected_items_bitset, &first_track_pos)) { // Failed to get the first item
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
selected_tracks_pos = g_list_append(selected_tracks_pos, GUINT_TO_POINTER(first_track_pos));
|
|
||||||
|
|
||||||
gboolean have_more_items = TRUE;
|
|
||||||
|
|
||||||
while (have_more_items) { // While we are able to get selected items
|
|
||||||
guint track_pos;
|
|
||||||
have_more_items = gtk_bitset_iter_next(&iter, &track_pos);
|
|
||||||
if (have_more_items) { // Got the next track
|
|
||||||
selected_tracks_pos = g_list_append(selected_tracks_pos, GUINT_TO_POINTER(track_pos));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GList * cur_pos_list;
|
|
||||||
|
|
||||||
for (cur_pos_list = selected_tracks_pos; cur_pos_list != NULL; cur_pos_list = cur_pos_list->next) { // Iterate over every position that we accumulated
|
|
||||||
KotoTrack * selected_track = g_list_model_get_item(self->model, GPOINTER_TO_UINT(cur_pos_list->data)); // Get the KotoTrack in the GListModel for this current position
|
|
||||||
selected_tracks = g_list_append(selected_tracks, selected_track); // Add to selected tracks
|
|
||||||
}
|
|
||||||
|
|
||||||
koto_action_bar_set_tracks_in_playlist_selection(action_bar, self->uuid, selected_tracks); // Set the tracks for the playlist selection
|
|
||||||
koto_action_bar_toggle_reveal(action_bar, TRUE); // Show the items
|
|
||||||
}
|
|
||||||
|
|
||||||
void koto_playlist_page_set_playlist_uuid(
|
void koto_playlist_page_set_playlist_uuid(
|
||||||
KotoPlaylistPage * self,
|
KotoPlaylistPage * self,
|
||||||
gchar * playlist_uuid
|
gchar * playlist_uuid
|
||||||
|
@ -524,10 +264,6 @@ void koto_playlist_page_set_playlist_uuid(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!KOTO_IS_PLAYLIST_PAGE(self)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!koto_utils_is_string_valid(playlist_uuid)) { // Provided UUID string is not valid
|
if (!koto_utils_is_string_valid(playlist_uuid)) { // Provided UUID string is not valid
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -540,100 +276,10 @@ void koto_playlist_page_set_playlist_uuid(
|
||||||
KotoPlaylist * playlist = koto_cartographer_get_playlist_by_uuid(koto_maps, self->uuid);
|
KotoPlaylist * playlist = koto_cartographer_get_playlist_by_uuid(koto_maps, self->uuid);
|
||||||
|
|
||||||
self->playlist = playlist;
|
self->playlist = playlist;
|
||||||
koto_playlist_page_set_playlist_model(self, KOTO_PREFERRED_MODEL_TYPE_DEFAULT); // TODO: Enable this to be changed
|
|
||||||
koto_playlist_page_update_header(self); // Update our header
|
koto_playlist_page_update_header(self); // Update our header
|
||||||
|
|
||||||
g_signal_connect(playlist, "modified", G_CALLBACK(koto_playlist_page_handle_playlist_modified), self); // Handle playlist modification
|
g_signal_connect(playlist, "modified", G_CALLBACK(koto_playlist_page_handle_playlist_modified), self); // Handle playlist modification
|
||||||
}
|
koto_track_table_set_playlist(self->table, playlist); // Set our track table
|
||||||
|
|
||||||
void koto_playlist_page_set_playlist_model(
|
|
||||||
KotoPlaylistPage * self,
|
|
||||||
KotoPreferredModelType model
|
|
||||||
) {
|
|
||||||
if (!KOTO_IS_PLAYLIST_PAGE(self)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
koto_playlist_apply_model(self->playlist, model); // Apply our new model
|
|
||||||
self->model = G_LIST_MODEL(koto_playlist_get_store(self->playlist)); // Get the latest generated model / store and cast it as a GListModel
|
|
||||||
|
|
||||||
if (model != KOTO_PREFERRED_MODEL_TYPE_SORT_BY_ALBUM) { // Not sorting by album currently
|
|
||||||
gtk_widget_remove_css_class(GTK_WIDGET(self->track_album_button), "active");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (model != KOTO_PREFERRED_MODEL_TYPE_SORT_BY_ARTIST) { // Not sorting by artist currently
|
|
||||||
gtk_widget_remove_css_class(GTK_WIDGET(self->track_artist_button), "active");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (model != KOTO_PREFERRED_MODEL_TYPE_SORT_BY_TRACK_NAME) { // Not sorting by track name
|
|
||||||
gtk_widget_remove_css_class(GTK_WIDGET(self->track_title_button), "active");
|
|
||||||
}
|
|
||||||
|
|
||||||
self->selection_model = GTK_SELECTION_MODEL(gtk_multi_selection_new(self->model));
|
|
||||||
g_signal_connect(self->selection_model, "selection-changed", G_CALLBACK(koto_playlist_page_handle_tracks_selected), self); // Bind to our selection changed
|
|
||||||
|
|
||||||
gtk_list_view_set_model(GTK_LIST_VIEW(self->track_list_view), self->selection_model); // Set our multi selection model to our provided model
|
|
||||||
}
|
|
||||||
|
|
||||||
void koto_playlist_page_setup_track_item(
|
|
||||||
GtkListItemFactory * factory,
|
|
||||||
GtkListItem * item,
|
|
||||||
gpointer user_data
|
|
||||||
) {
|
|
||||||
(void) factory;
|
|
||||||
KotoPlaylistPage * self = user_data;
|
|
||||||
|
|
||||||
if (!KOTO_IS_PLAYLIST_PAGE(self)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GtkWidget * item_content = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); // Have a horizontal box for our content
|
|
||||||
|
|
||||||
gtk_widget_add_css_class(item_content, "track-list-columned-item");
|
|
||||||
|
|
||||||
GtkWidget * track_number = gtk_label_new(NULL); // Our track number
|
|
||||||
|
|
||||||
gtk_label_set_xalign(GTK_LABEL(track_number), 0);
|
|
||||||
gtk_widget_add_css_class(track_number, "track-column-number");
|
|
||||||
gtk_widget_set_halign(track_number, GTK_ALIGN_END);
|
|
||||||
gtk_widget_set_hexpand(track_number, FALSE);
|
|
||||||
gtk_widget_set_size_request(track_number, 150, -1);
|
|
||||||
gtk_box_append(GTK_BOX(item_content), track_number);
|
|
||||||
gtk_size_group_add_widget(self->track_pos_size_group, track_number);
|
|
||||||
|
|
||||||
GtkWidget * track_name = gtk_label_new(NULL); // Our track name
|
|
||||||
|
|
||||||
gtk_label_set_xalign(GTK_LABEL(track_name), 0);
|
|
||||||
gtk_widget_add_css_class(track_name, "track-column-name");
|
|
||||||
gtk_widget_set_halign(track_name, GTK_ALIGN_START);
|
|
||||||
gtk_widget_set_hexpand(track_name, FALSE);
|
|
||||||
gtk_widget_set_size_request(track_name, 350, -1);
|
|
||||||
gtk_box_append(GTK_BOX(item_content), track_name);
|
|
||||||
gtk_size_group_add_widget(self->track_name_size_group, track_name);
|
|
||||||
|
|
||||||
GtkWidget * track_album = gtk_label_new(NULL); // Our track album
|
|
||||||
|
|
||||||
gtk_label_set_xalign(GTK_LABEL(track_album), 0);
|
|
||||||
gtk_widget_add_css_class(track_album, "track-column-album");
|
|
||||||
gtk_widget_set_halign(track_album, GTK_ALIGN_START);
|
|
||||||
gtk_widget_set_hexpand(track_album, FALSE);
|
|
||||||
gtk_widget_set_margin_start(track_album, 50);
|
|
||||||
gtk_widget_set_size_request(track_album, 350, -1);
|
|
||||||
gtk_box_append(GTK_BOX(item_content), track_album);
|
|
||||||
gtk_size_group_add_widget(self->track_album_size_group, track_album);
|
|
||||||
|
|
||||||
GtkWidget * track_artist = gtk_label_new(NULL); // Our track artist
|
|
||||||
|
|
||||||
gtk_label_set_xalign(GTK_LABEL(track_artist), 0);
|
|
||||||
gtk_widget_add_css_class(track_artist, "track-column-artist");
|
|
||||||
gtk_widget_set_halign(track_artist, GTK_ALIGN_START);
|
|
||||||
gtk_widget_set_hexpand(track_artist, TRUE);
|
|
||||||
gtk_widget_set_margin_start(track_artist, 50);
|
|
||||||
gtk_widget_set_size_request(track_artist, 350, -1);
|
|
||||||
gtk_box_append(GTK_BOX(item_content), track_artist);
|
|
||||||
gtk_size_group_add_widget(self->track_artist_size_group, track_artist);
|
|
||||||
|
|
||||||
gtk_list_item_set_child(item, item_content);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void koto_playlist_page_update_header(KotoPlaylistPage * self) {
|
void koto_playlist_page_update_header(KotoPlaylistPage * self) {
|
||||||
|
@ -666,12 +312,6 @@ void koto_playlist_page_update_header(KotoPlaylistPage * self) {
|
||||||
if (koto_utils_is_string_valid(artwork)) { // Have artwork
|
if (koto_utils_is_string_valid(artwork)) { // Have artwork
|
||||||
koto_cover_art_button_set_art_path(self->playlist_image, artwork); // Update our artwork
|
koto_cover_art_button_set_art_path(self->playlist_image, artwork); // Update our artwork
|
||||||
}
|
}
|
||||||
|
|
||||||
KotoPreferredModelType current_model = koto_playlist_get_current_model(self->playlist); // Get the current model
|
|
||||||
|
|
||||||
if (current_model == KOTO_PREFERRED_MODEL_TYPE_OLDEST_FIRST) {
|
|
||||||
koto_button_show_image(self->track_num_button, TRUE); // Immediately use pan-up-symbolic
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
KotoPlaylistPage * koto_playlist_page_new(gchar * playlist_uuid) {
|
KotoPlaylistPage * koto_playlist_page_new(gchar * playlist_uuid) {
|
||||||
|
|
|
@ -51,10 +51,6 @@ void koto_playlist_page_remove_track(
|
||||||
|
|
||||||
GtkWidget * koto_playlist_page_get_main(KotoPlaylistPage * self);
|
GtkWidget * koto_playlist_page_get_main(KotoPlaylistPage * self);
|
||||||
|
|
||||||
void koto_playlist_page_handle_action_bar_closed(
|
|
||||||
KotoActionBar * bar,
|
|
||||||
gpointer data
|
|
||||||
);
|
|
||||||
|
|
||||||
void koto_playlist_page_handle_cover_art_clicked(
|
void koto_playlist_page_handle_cover_art_clicked(
|
||||||
GtkGestureClick * gesture,
|
GtkGestureClick * gesture,
|
||||||
|
@ -77,71 +73,15 @@ void koto_playlist_page_handle_playlist_modified(
|
||||||
gpointer user_data
|
gpointer user_data
|
||||||
);
|
);
|
||||||
|
|
||||||
void koto_playlist_page_handle_track_album_clicked(
|
|
||||||
GtkGestureClick * gesture,
|
|
||||||
int n_press,
|
|
||||||
double x,
|
|
||||||
double y,
|
|
||||||
gpointer user_data
|
|
||||||
);
|
|
||||||
|
|
||||||
void koto_playlist_page_handle_track_artist_clicked(
|
|
||||||
GtkGestureClick * gesture,
|
|
||||||
int n_press,
|
|
||||||
double x,
|
|
||||||
double y,
|
|
||||||
gpointer user_data
|
|
||||||
);
|
|
||||||
|
|
||||||
void koto_playlist_page_handle_track_name_clicked(
|
|
||||||
GtkGestureClick * gesture,
|
|
||||||
int n_press,
|
|
||||||
double x,
|
|
||||||
double y,
|
|
||||||
gpointer user_data
|
|
||||||
);
|
|
||||||
|
|
||||||
void koto_playlist_page_handle_track_num_clicked(
|
|
||||||
GtkGestureClick * gesture,
|
|
||||||
int n_press,
|
|
||||||
double x,
|
|
||||||
double y,
|
|
||||||
gpointer user_data
|
|
||||||
);
|
|
||||||
|
|
||||||
void koto_playlist_page_handle_tracks_selected(
|
|
||||||
GtkSelectionModel * model,
|
|
||||||
guint position,
|
|
||||||
guint n_items,
|
|
||||||
gpointer user_data
|
|
||||||
);
|
|
||||||
|
|
||||||
void koto_playlist_page_hide_popover(KotoPlaylistPage * self);
|
void koto_playlist_page_hide_popover(KotoPlaylistPage * self);
|
||||||
|
|
||||||
void koto_playlist_page_setup_track_item(
|
|
||||||
GtkListItemFactory * factory,
|
|
||||||
GtkListItem * item,
|
|
||||||
gpointer user_data
|
|
||||||
);
|
|
||||||
|
|
||||||
void koto_playlist_page_set_playlist_uuid(
|
void koto_playlist_page_set_playlist_uuid(
|
||||||
KotoPlaylistPage * self,
|
KotoPlaylistPage * self,
|
||||||
gchar * playlist_uuid
|
gchar * playlist_uuid
|
||||||
);
|
);
|
||||||
|
|
||||||
void koto_playlist_page_set_playlist_model(
|
|
||||||
KotoPlaylistPage * self,
|
|
||||||
KotoPreferredModelType model
|
|
||||||
);
|
|
||||||
|
|
||||||
void koto_playlist_page_show_popover(KotoPlaylistPage * self);
|
void koto_playlist_page_show_popover(KotoPlaylistPage * self);
|
||||||
|
|
||||||
void koto_playlist_page_update_header(KotoPlaylistPage * self);
|
void koto_playlist_page_update_header(KotoPlaylistPage * self);
|
||||||
|
|
||||||
void koto_playlist_page_handle_listitem_activate(
|
|
||||||
GtkListView * list,
|
|
||||||
guint position,
|
|
||||||
gpointer user_data
|
|
||||||
);
|
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
@import 'components/cover-art-button';
|
@import 'components/cover-art-button';
|
||||||
@import 'components/gtk-overrides';
|
@import 'components/gtk-overrides';
|
||||||
|
@import 'components/track-table';
|
||||||
|
@import 'pages/artist-view';
|
||||||
@import 'pages/music-local';
|
@import 'pages/music-local';
|
||||||
@import 'pages/playlist-page';
|
@import 'pages/playlist-page';
|
||||||
|
|
||||||
|
|
49
theme/components/_track-table.scss
Normal file
49
theme/components/_track-table.scss
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
.track-list-content { // Our Track List
|
||||||
|
& > .track-list-header,
|
||||||
|
.track-list-columned-item {
|
||||||
|
font-size: x-large;
|
||||||
|
padding: 3ex 2ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .track-list-header { // Headers
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
.koto-button { // All Koto buttons in our header
|
||||||
|
&.active { // Is active
|
||||||
|
color: $green;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .track-list-columned { // Column content
|
||||||
|
& > row {
|
||||||
|
&:not(:selected) {
|
||||||
|
color: $text-color-bright;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:nth-child(odd):not(:selected) {
|
||||||
|
background-color: $bg-secondary;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .track-list-columned-item { // Track rows
|
||||||
|
font-size: x-large;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.track-column-number { // Column section within header and track items
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.track-column-name { // Name section within header and track items
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.track-column-album { // Album section within headers and track items
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.track-column-artist { // Artist section within headers and track items
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
14
theme/pages/_artist-view.scss
Normal file
14
theme/pages/_artist-view.scss
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
.artist-view {
|
||||||
|
.no-albums-view { // No Albums
|
||||||
|
.no-albums-view-header { // Header for artist when we have no albums
|
||||||
|
.cover-art-button { // Button to play all artist tracks
|
||||||
|
margin-right: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > label { // Artist Name
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: 10ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,54 +35,4 @@
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.track-list-content { // Our Track List
|
|
||||||
& > .track-list-header,
|
|
||||||
.track-list-columned-item {
|
|
||||||
font-size: x-large;
|
|
||||||
padding: 3ex 2ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > .track-list-header { // Headers
|
|
||||||
font-weight: bold;
|
|
||||||
|
|
||||||
.koto-button { // All Koto buttons in our header
|
|
||||||
&.active { // Is active
|
|
||||||
color: $green;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& > .track-list-columned { // Column content
|
|
||||||
& > row {
|
|
||||||
&:not(:selected) {
|
|
||||||
color: $text-color-bright;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:nth-child(odd):not(:selected) {
|
|
||||||
background-color: $bg-secondary;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > .track-list-columned-item { // Track rows
|
|
||||||
font-size: x-large;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.track-column-number { // Column section within header and track items
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.track-column-name { // Name section within header and track items
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.track-column-album { // Album section within headers and track items
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.track-column-artist { // Artist section within headers and track items
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue