Port to GTK4, start implementation of Local Music view, Artist and Album Views.

This commit is contained in:
Joshua Strobl 2021-02-24 20:17:18 +02:00
parent 8948a8ec9f
commit 588a68b2cc
28 changed files with 1103 additions and 270 deletions

View file

@ -0,0 +1,169 @@
/* album-view.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 "../../indexer/album.h"
#include "../../indexer/artist.h"
#include "../../koto-track-item.h"
#include "album-view.h"
#include "koto-config.h"
#include "koto-utils.h"
struct _KotoAlbumView {
GObject parent_instance;
KotoIndexedAlbum *album;
GtkWidget *main;
GtkWidget *album_tracks_box;
GtkWidget *tracks;
GtkWidget *album_label;
};
G_DEFINE_TYPE(KotoAlbumView, koto_album_view, G_TYPE_OBJECT);
enum {
PROP_0,
PROP_ALBUM,
N_PROPERTIES
};
static GParamSpec *props[N_PROPERTIES] = { NULL, };
static void koto_album_view_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec);
static void koto_album_view_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec);
static void koto_album_view_class_init(KotoAlbumViewClass *c) {
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS(c);
gobject_class->set_property = koto_album_view_set_property;
gobject_class->get_property = koto_album_view_get_property;
props[PROP_ALBUM] = g_param_spec_object(
"album",
"Album",
"Album",
KOTO_TYPE_INDEXED_ALBUM,
G_PARAM_CONSTRUCT_ONLY|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
);
g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
}
static void koto_album_view_init(KotoAlbumView *self) {
self->main = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
gtk_widget_set_can_focus(self->main, FALSE);
self->album_tracks_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
self->tracks = gtk_list_box_new(); // Create our list of our tracks
gtk_list_box_set_sort_func(GTK_LIST_BOX(self->tracks), koto_album_view_sort_tracks, NULL, NULL); // Ensure we can sort our tracks
gtk_widget_set_size_request(self->tracks, 600, -1);
gtk_box_append(GTK_BOX(self->main), self->album_tracks_box); // Add the tracks box to the art info combo box
gtk_box_append(GTK_BOX(self->album_tracks_box), self->tracks); // Add the tracks list box to the albums tracks box
}
GtkWidget* koto_album_view_get_main(KotoAlbumView *self) {
return self->main;
}
static void koto_album_view_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) {
KotoAlbumView *self = KOTO_ALBUM_VIEW(obj);
switch (prop_id) {
case PROP_ALBUM:
g_value_set_object(val, self->album);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
break;
}
}
static void koto_album_view_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec) {
KotoAlbumView *self = KOTO_ALBUM_VIEW(obj);
switch (prop_id) {
case PROP_ALBUM:
koto_album_view_set_album(self, (KotoIndexedAlbum*) g_value_get_object(val));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
break;
}
}
void koto_album_view_set_album(KotoAlbumView *self, KotoIndexedAlbum *album) {
if (album == NULL) {
return;
}
self->album = album;
gchar *album_art = koto_indexed_album_get_album_art(self->album); // Get the art for the album
GtkWidget *art_image = koto_utils_create_image_from_filepath(album_art, "audio-x-generic-symbolic", 220, 220);
gtk_box_prepend(GTK_BOX(self->main), art_image); // Prepend the image to the art info box
gchar *album_name;
g_object_get(album, "name", &album_name, NULL); // Get the album name
self->album_label = gtk_label_new(album_name);
gtk_box_prepend(GTK_BOX(self->album_tracks_box), self->album_label); // Prepend our new label to the album + tracks box
GList *t;
for (t = koto_indexed_album_get_files(self->album); t != NULL; t = t->next) { // For each file / track
KotoIndexedFile *file = (KotoIndexedFile*) t->data;
KotoTrackItem *track_item = koto_track_item_new(file); // Create our new track item
gtk_list_box_prepend(GTK_LIST_BOX(self->tracks), GTK_WIDGET(track_item)); // Add to our tracks list box
}
}
int koto_album_view_sort_tracks(GtkListBoxRow *track1, GtkListBoxRow *track2, gpointer user_data) {
(void) user_data;
KotoTrackItem *track1_item = KOTO_TRACK_ITEM(gtk_list_box_row_get_child(track1));
KotoTrackItem *track2_item = KOTO_TRACK_ITEM(gtk_list_box_row_get_child(track2));
KotoIndexedFile *track1_file;
KotoIndexedFile *track2_file;
g_object_get(track1_item, "track", &track1_file, NULL);
g_object_get(track2_item, "track", &track2_file, NULL);
guint16 *track1_pos;
guint16 *track2_pos;
g_object_get(track1_file, "position", &track1_pos, NULL);
g_object_get(track2_file, "position", &track2_pos, NULL);
if (track1_pos == track2_pos) { // Identical positions (like reported as 0)
gchar *track1_name;
gchar *track2_name;
g_object_get(track1_file, "parsed-name", &track1_name, NULL);
g_object_get(track2_file, "parsed-name", &track2_name, NULL);
return g_utf8_collate(track1_name, track2_name);
} else if (track1_pos < track2_pos) {
return -1;
} else {
return 1;
}
}
KotoAlbumView* koto_album_view_new(KotoIndexedAlbum *album) {
return g_object_new(KOTO_TYPE_ALBUM_VIEW, "album", album, NULL);
}

View file

@ -0,0 +1,36 @@
/* album-view.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 "../../indexer/album.h"
#include "../../indexer/artist.h"
G_BEGIN_DECLS
#define KOTO_TYPE_ALBUM_VIEW (koto_album_view_get_type())
G_DECLARE_FINAL_TYPE(KotoAlbumView, koto_album_view, KOTO, ALBUM_VIEW, GObject)
KotoAlbumView* koto_album_view_new(KotoIndexedAlbum *album);
GtkWidget* koto_album_view_get_main(KotoAlbumView *self);
void koto_album_view_set_album(KotoAlbumView *self, KotoIndexedAlbum *album);
int koto_album_view_sort_tracks(GtkListBoxRow *track1, GtkListBoxRow *track2, gpointer user_data);
G_END_DECLS

View file

@ -0,0 +1,167 @@
/* artist-view.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 "../../indexer/album.h"
#include "../../indexer/artist.h"
#include "album-view.h"
#include "artist-view.h"
#include "koto-config.h"
#include "koto-utils.h"
struct _KotoArtistView {
GObject parent_instance;
KotoIndexedArtist *artist;
GtkWidget *scrolled_window;
GtkWidget *content;
GtkWidget *albums_strip;
GtkWidget *album_list;
GHashTable *albums_to_component;
gboolean constructed;
};
G_DEFINE_TYPE(KotoArtistView, koto_artist_view, G_TYPE_OBJECT);
enum {
PROP_0,
PROP_ARTIST,
N_PROPERTIES
};
static GParamSpec *props[N_PROPERTIES] = { NULL, };
static void koto_artist_view_constructed(GObject *obj);
static void koto_artist_view_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec);
static void koto_artist_view_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec);
static void koto_artist_view_class_init(KotoArtistViewClass *c) {
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS(c);
gobject_class->constructed = koto_artist_view_constructed;
gobject_class->set_property = koto_artist_view_set_property;
gobject_class->get_property = koto_artist_view_get_property;
props[PROP_ARTIST] = g_param_spec_object(
"artist",
"Artist",
"Artist",
KOTO_TYPE_INDEXED_ARTIST,
G_PARAM_CONSTRUCT_ONLY|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
);
g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
}
static void koto_artist_view_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) {
KotoArtistView *self = KOTO_ARTIST_VIEW(obj);
switch (prop_id) {
case PROP_ARTIST:
g_value_set_object(val, self->artist);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
break;
}
}
static void koto_artist_view_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec) {
KotoArtistView *self = KOTO_ARTIST_VIEW(obj);
switch (prop_id) {
case PROP_ARTIST:
koto_artist_view_add_artist(self, (KotoIndexedArtist*) g_value_get_object(val));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
break;
}
}
static void koto_artist_view_init(KotoArtistView *self) {
self->artist = NULL;
self->constructed = FALSE;
}
static void koto_artist_view_constructed(GObject *obj) {
KotoArtistView *self = KOTO_ARTIST_VIEW(obj);
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
self->albums_strip = gtk_flow_box_new(); // Create our album strip
gtk_flow_box_set_activate_on_single_click(GTK_FLOW_BOX(self->albums_strip), TRUE);
gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(self->albums_strip), GTK_SELECTION_NONE);
self->album_list = gtk_flow_box_new(); // Create our list of albums as a flow box
gtk_flow_box_set_activate_on_single_click(GTK_FLOW_BOX(self->album_list), FALSE);
gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(self->album_list), GTK_SELECTION_NONE);
gtk_box_prepend(GTK_BOX(self->content), self->albums_strip); // Add the strip
gtk_box_append(GTK_BOX(self->content), self->album_list); // Add the list
gtk_widget_set_hexpand(GTK_WIDGET(self->albums_strip), TRUE);
gtk_widget_set_hexpand(GTK_WIDGET(self->album_list), TRUE);
G_OBJECT_CLASS (koto_artist_view_parent_class)->constructed (obj);
self->constructed = TRUE;
}
void koto_artist_view_add_album(KotoArtistView *self, KotoIndexedAlbum *album) {
gchar *album_art = koto_indexed_album_get_album_art(album); // Get the art for the album
GtkWidget *art_image = koto_utils_create_image_from_filepath(album_art, "audio-x-generic-symbolic", 220, 220);
gtk_flow_box_insert(GTK_FLOW_BOX(self->albums_strip), art_image, -1); // Append the album art
KotoAlbumView* album_view = koto_album_view_new(album); // Create our new album view
GtkWidget* album_view_main = koto_album_view_get_main(album_view);
gtk_flow_box_insert(GTK_FLOW_BOX(self->album_list), album_view_main, -1); // Append the album view to the album list
}
void koto_artist_view_add_artist(KotoArtistView *self, KotoIndexedArtist *artist) {
if (artist == NULL) {
return;
}
self->artist = artist;
if (!self->constructed) {
return;
}
GList *albums = koto_indexed_artist_get_albums(self->artist); // Get the albums
GList *a;
for (a = albums; a != NULL; a = a->next) {
KotoIndexedAlbum *album = (KotoIndexedAlbum*) a->data;
koto_artist_view_add_album(self, album); // Add the album
}
}
GtkWidget* koto_artist_view_get_main(KotoArtistView *self) {
return self->scrolled_window;
}
KotoArtistView* koto_artist_view_new() {
return g_object_new(KOTO_TYPE_ARTIST_VIEW, NULL);
}

View file

@ -0,0 +1,36 @@
/* artist-view.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 "../../indexer/album.h"
#include "../../indexer/artist.h"
G_BEGIN_DECLS
#define KOTO_TYPE_ARTIST_VIEW (koto_artist_view_get_type())
G_DECLARE_FINAL_TYPE (KotoArtistView, koto_artist_view, KOTO, ARTIST_VIEW, GObject)
KotoArtistView* koto_artist_view_new();
void koto_artist_view_add_album(KotoArtistView *self, KotoIndexedAlbum *album);
void koto_artist_view_add_artist(KotoArtistView *self, KotoIndexedArtist *artist);
GtkWidget* koto_artist_view_get_main(KotoArtistView *self);
G_END_DECLS

View file

@ -0,0 +1,227 @@
/* music-local.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 "../../indexer/file-indexer.h"
#include "koto-button.h"
#include "koto-config.h"
#include "music-local.h"
enum {
PROP_0,
PROP_LIB,
N_PROPERTIES
};
static GParamSpec *props[N_PROPERTIES] = { NULL, };
struct _KotoPageMusicLocal {
GtkBox parent_instance;
GtkWidget *scrolled_window;
GtkWidget *artist_list;
GtkWidget *stack;
KotoIndexedLibrary *lib;
gboolean constructed;
};
struct _KotoPageMusicLocalClass {
GtkBoxClass parent_class;
};
G_DEFINE_TYPE(KotoPageMusicLocal, koto_page_music_local, GTK_TYPE_BOX);
static void koto_page_music_local_constructed(GObject *obj);
static void koto_page_music_local_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec);
static void koto_page_music_local_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec);
static void koto_page_music_local_class_init(KotoPageMusicLocalClass *c) {
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS(c);
gobject_class->constructed = koto_page_music_local_constructed;
gobject_class->set_property = koto_page_music_local_set_property;
gobject_class->get_property = koto_page_music_local_get_property;
props[PROP_LIB] = g_param_spec_object(
"lib",
"Library",
"Library",
KOTO_TYPE_INDEXED_LIBRARY,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
);
g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
}
static void koto_page_music_local_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) {
KotoPageMusicLocal *self = KOTO_PAGE_MUSIC_LOCAL(obj);
switch (prop_id) {
case PROP_LIB:
g_value_set_object(val, self->lib);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
break;
}
}
static void koto_page_music_local_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec) {
KotoPageMusicLocal *self = KOTO_PAGE_MUSIC_LOCAL(obj);
switch (prop_id) {
case PROP_LIB:
koto_page_music_local_set_library(self, (KotoIndexedLibrary*) g_value_get_object(val));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
break;
}
}
static void koto_page_music_local_init(KotoPageMusicLocal *self) {
self->lib = NULL;
self->constructed = FALSE;
}
static void koto_page_music_local_constructed(GObject *obj) {
KotoPageMusicLocal *self = KOTO_PAGE_MUSIC_LOCAL(obj);
gtk_widget_add_css_class(GTK_WIDGET(self), "page-music-local");
gtk_widget_set_hexpand(GTK_WIDGET(self), TRUE);
G_OBJECT_CLASS (koto_page_music_local_parent_class)->constructed (obj);
self->constructed = TRUE;
}
void koto_page_music_local_add_artist(KotoPageMusicLocal *self, KotoIndexedArtist *artist) {
gchar *artist_name;
g_object_get(artist, "name", &artist_name, NULL);
KotoButton *artist_button = koto_button_new_plain(artist_name);
gtk_list_box_prepend(GTK_LIST_BOX(self->artist_list), GTK_WIDGET(artist_button));
KotoArtistView *artist_view = koto_artist_view_new(); // Create our new artist view
koto_artist_view_add_artist(artist_view, artist); // Add the artist
gtk_stack_add_named(GTK_STACK(self->stack), koto_artist_view_get_main(artist_view), artist_name);
GtkGesture *controller = gtk_gesture_click_new(); // Create a new GtkGestureClick
g_signal_connect(controller, "pressed", G_CALLBACK(koto_page_music_local_handle_artist_click), self);
gtk_widget_add_controller(GTK_WIDGET(artist_button), GTK_EVENT_CONTROLLER(controller));
}
void koto_page_music_local_handle_artist_click(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data) {
(void) n_press; (void) x; (void) y;
KotoPageMusicLocal *self = (KotoPageMusicLocal*) data;
GtkWidget *btn_widget = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(gesture)); // Get the widget that applied to this gesture
KotoButton *btn = KOTO_BUTTON(btn_widget);
gchar *artist_name;
g_object_get(btn, "button-text", &artist_name, NULL);
gtk_stack_set_visible_child_name(GTK_STACK(self->stack), artist_name);
}
void koto_page_music_local_set_library(KotoPageMusicLocal *self, KotoIndexedLibrary *lib) {
if (lib == NULL) {
return;
}
if (self->lib != NULL) { // If lib is already set
g_free(self->lib);
}
self->lib = lib;
if (!self->constructed) {
return;
}
if (!GTK_IS_SCROLLED_WINDOW(self->scrolled_window)) {
self->scrolled_window = gtk_scrolled_window_new();
gtk_widget_add_css_class(self->scrolled_window, "artist-list");
gtk_box_prepend(GTK_BOX(self), self->scrolled_window);
}
if (GTK_IS_LIST_BOX(self->artist_list)) { // artist list is a list box
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(self->scrolled_window), NULL); // Set to null to maybe clear?
g_object_unref(self->artist_list); // Unref
}
if (GTK_IS_STACK(self->stack)) { // Stack is a Stack
gtk_box_remove(GTK_BOX(self), GTK_WIDGET(self->stack)); // Destroy to free references
}
self->artist_list = gtk_list_box_new(); // Create our artist list
gboolean list_created = GTK_IS_LIST_BOX(self->artist_list);
if (list_created) { // Successfully created our list
gtk_list_box_set_activate_on_single_click(GTK_LIST_BOX(self->artist_list), TRUE);
gtk_list_box_set_selection_mode(GTK_LIST_BOX(self->artist_list), GTK_SELECTION_BROWSE);
gtk_list_box_set_sort_func(GTK_LIST_BOX(self->artist_list), koto_page_music_local_sort_artists, NULL, NULL); // Add our sort function
gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(self->scrolled_window), self->artist_list);
gtk_widget_show(GTK_WIDGET(self->artist_list));
}
gtk_scrolled_window_set_min_content_width(GTK_SCROLLED_WINDOW(self->scrolled_window), 300);
gtk_scrolled_window_set_propagate_natural_height(GTK_SCROLLED_WINDOW(self->scrolled_window), FALSE);
gtk_widget_set_size_request(GTK_WIDGET(self->artist_list), 300, -1);
self->stack = gtk_stack_new(); // Create a new stack
gtk_widget_set_hexpand(self->stack, TRUE);
gboolean stack_created = GTK_IS_STACK(self->stack);
if (list_created && stack_created) {
GHashTableIter artist_list_iter;
gpointer artist_key;
gpointer artist_data;
GHashTable *artists = koto_indexed_library_get_artists(self->lib); // Get the artists
g_hash_table_iter_init(&artist_list_iter, artists);
while (g_hash_table_iter_next(&artist_list_iter, &artist_key, &artist_data)) { // For each of the music artists
KotoIndexedArtist *artist = (KotoIndexedArtist*) artist_data; // Cast our data as a KotoIndexedArtist
koto_page_music_local_add_artist(self, artist);
}
}
if (stack_created) { // Successfully created our stack
gtk_box_append(GTK_BOX(self), self->stack);
}
gtk_widget_show(GTK_WIDGET(self));
}
int koto_page_music_local_sort_artists(GtkListBoxRow *artist1, GtkListBoxRow *artist2, gpointer user_data) {
(void) user_data;
KotoButton *artist1_btn = KOTO_BUTTON(gtk_list_box_row_get_child(artist1));
KotoButton *artist2_btn = KOTO_BUTTON(gtk_list_box_row_get_child(artist2));
gchar *artist1_text;
gchar *artist2_text;
g_object_get(artist1_btn, "button-text", &artist1_text, NULL);
g_object_get(artist2_btn, "button-text", &artist2_text, NULL);
return g_utf8_collate(artist1_text, artist2_text);
}
KotoPageMusicLocal* koto_page_music_local_new() {
return g_object_new(KOTO_TYPE_PAGE_MUSIC_LOCAL,
"orientation", GTK_ORIENTATION_HORIZONTAL,
NULL
);
}

View file

@ -0,0 +1,38 @@
/* music-local.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 "../../indexer/artist.h"
#include "../../indexer/file-indexer.h"
#include "artist-view.h"
G_BEGIN_DECLS
#define KOTO_TYPE_PAGE_MUSIC_LOCAL (koto_page_music_local_get_type())
G_DECLARE_FINAL_TYPE (KotoPageMusicLocal, koto_page_music_local, KOTO, PAGE_MUSIC_LOCAL, GtkBox)
KotoPageMusicLocal* koto_page_music_local_new();
void koto_page_music_local_add_artist(KotoPageMusicLocal *self, KotoIndexedArtist *artist);
void koto_page_music_local_handle_artist_click(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data);
void koto_page_music_local_set_library(KotoPageMusicLocal *self, KotoIndexedLibrary *lib);
int koto_page_music_local_sort_artists(GtkListBoxRow *artist1, GtkListBoxRow *artist2, gpointer user_data);
G_END_DECLS