Implement Playlist functionality. My god...
Too many changes to summarize. - Fixes #2. - Fixes #3. - Fixes #5. - Fixes #7. Start work on uncrustify config.
This commit is contained in:
parent
ddf1987b50
commit
0e2244ba90
62 changed files with 6280 additions and 374 deletions
249
src/playlist/add-remove-track-popover.c
Normal file
249
src/playlist/add-remove-track-popover.c
Normal file
|
@ -0,0 +1,249 @@
|
|||
/* add-remove-track-popover.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 "../playback/engine.h"
|
||||
#include "../playlist/playlist.h"
|
||||
#include "add-remove-track-popover.h"
|
||||
|
||||
extern KotoCartographer *koto_maps;
|
||||
|
||||
struct _KotoAddRemoveTrackPopover {
|
||||
GtkPopover parent_instance;
|
||||
GtkWidget *list_box;
|
||||
GHashTable *checkbox_to_playlist_uuid;
|
||||
GHashTable *playlist_uuid_to_checkbox;
|
||||
GList *tracks;
|
||||
|
||||
GHashTable *checkbox_to_signal_ids;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(KotoAddRemoveTrackPopover, koto_add_remove_track_popover, GTK_TYPE_POPOVER);
|
||||
|
||||
KotoAddRemoveTrackPopover *koto_add_remove_track_popup = NULL;
|
||||
|
||||
static void koto_add_remove_track_popover_class_init(KotoAddRemoveTrackPopoverClass *c) {
|
||||
(void) c;
|
||||
}
|
||||
|
||||
static void koto_add_remove_track_popover_init(KotoAddRemoveTrackPopover *self) {
|
||||
self->list_box = gtk_list_box_new(); // Create our new GtkListBox
|
||||
gtk_list_box_set_selection_mode(GTK_LIST_BOX(self->list_box), GTK_SELECTION_NONE);
|
||||
|
||||
self->checkbox_to_playlist_uuid = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
self->playlist_uuid_to_checkbox = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
self->checkbox_to_signal_ids = g_hash_table_new(g_str_hash, g_str_equal),
|
||||
self->tracks = NULL; // Initialize our tracks
|
||||
|
||||
gtk_popover_set_autohide(GTK_POPOVER(self), TRUE); // Ensure autohide is enabled
|
||||
gtk_popover_set_child(GTK_POPOVER(self), self->list_box);
|
||||
|
||||
g_signal_connect(koto_maps, "playlist-added", G_CALLBACK(koto_add_remove_track_popover_handle_playlist_added), self);
|
||||
g_signal_connect(koto_maps, "playlist-removed", G_CALLBACK(koto_add_remove_track_popover_handle_playlist_removed), self);
|
||||
}
|
||||
|
||||
void koto_add_remove_track_popover_add_playlist(KotoAddRemoveTrackPopover *self, KotoPlaylist *playlist) {
|
||||
if (!KOTO_JS_ADD_REMOVE_TRACK_POPOVER(self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!KOTO_IS_PLAYLIST(playlist)) { // Not a playlist
|
||||
return;
|
||||
}
|
||||
|
||||
gchar *playlist_uuid = koto_playlist_get_uuid(playlist); // Get the UUID of the playlist
|
||||
|
||||
if (GTK_IS_CHECK_BUTTON(g_hash_table_lookup(self->playlist_uuid_to_checkbox, playlist_uuid))) { // Already have a check button for this
|
||||
g_free(playlist_uuid);
|
||||
return;
|
||||
}
|
||||
|
||||
GtkWidget *playlist_button = gtk_check_button_new_with_label(koto_playlist_get_name(playlist)); // Create our GtkCheckButton
|
||||
g_hash_table_insert(self->checkbox_to_playlist_uuid, playlist_button, playlist_uuid);
|
||||
g_hash_table_insert(self->playlist_uuid_to_checkbox, playlist_uuid, playlist_button);
|
||||
|
||||
gulong playlist_sig_id = g_signal_connect(playlist_button, "toggled", G_CALLBACK(koto_add_remove_track_popover_handle_checkbutton_toggle), self);
|
||||
g_hash_table_insert(self->checkbox_to_signal_ids, playlist_button, GUINT_TO_POINTER(playlist_sig_id)); // Add our GSignal handler ID
|
||||
|
||||
gtk_list_box_append(GTK_LIST_BOX(self->list_box), playlist_button); // Add the playlist to the list box
|
||||
}
|
||||
|
||||
void koto_add_remove_track_popover_clear_tracks(KotoAddRemoveTrackPopover *self) {
|
||||
if (!KOTO_JS_ADD_REMOVE_TRACK_POPOVER(self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->tracks != NULL) { // Is a list
|
||||
g_list_free(self->tracks);
|
||||
self->tracks = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void koto_add_remove_track_popover_remove_playlist(KotoAddRemoveTrackPopover *self, gchar *playlist_uuid) {
|
||||
if (!KOTO_JS_ADD_REMOVE_TRACK_POPOVER(self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_hash_table_contains(self->playlist_uuid_to_checkbox, playlist_uuid)) { // Playlist not added
|
||||
return;
|
||||
}
|
||||
|
||||
GtkCheckButton *btn = GTK_CHECK_BUTTON(g_hash_table_lookup(self->playlist_uuid_to_checkbox, playlist_uuid)); // Get the check button
|
||||
|
||||
if (GTK_IS_CHECK_BUTTON(btn)) { // Is a check button
|
||||
g_hash_table_remove(self->checkbox_to_playlist_uuid, btn); // Remove uuid based on btn
|
||||
gtk_list_box_remove(GTK_LIST_BOX(self->list_box), GTK_WIDGET(btn)); // Remove the button from the list box
|
||||
}
|
||||
|
||||
g_hash_table_remove(self->playlist_uuid_to_checkbox, playlist_uuid);
|
||||
}
|
||||
|
||||
void koto_add_remove_track_popover_handle_checkbutton_toggle(GtkCheckButton *btn, gpointer user_data) {
|
||||
KotoAddRemoveTrackPopover *self = user_data;
|
||||
|
||||
if (!KOTO_JS_ADD_REMOVE_TRACK_POPOVER(self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
gboolean should_add = gtk_check_button_get_active(btn); // Get the now active state
|
||||
gchar *playlist_uuid = g_hash_table_lookup(self->checkbox_to_playlist_uuid, btn); // Get the playlist UUID for this button
|
||||
|
||||
KotoPlaylist *playlist = koto_cartographer_get_playlist_by_uuid(koto_maps, playlist_uuid); // Get the playlist
|
||||
|
||||
if (!KOTO_IS_PLAYLIST(playlist)) { // Failed to get the playlist
|
||||
return;
|
||||
}
|
||||
|
||||
GList *pos;
|
||||
for (pos = self->tracks; pos != NULL; pos = pos->next) { // Iterate over our KotoIndexedTracks
|
||||
KotoIndexedTrack *track = pos->data;
|
||||
|
||||
if (!KOTO_INDEXED_TRACK(track)) { // Not a track
|
||||
continue; // Skip this
|
||||
}
|
||||
|
||||
gchar *track_uuid = koto_indexed_track_get_uuid(track); // Get the track
|
||||
|
||||
if (should_add) { // Should be adding
|
||||
koto_playlist_add_track_by_uuid(playlist, track_uuid, FALSE, TRUE); // Add the track to the playlist
|
||||
} else { // Should be removing
|
||||
koto_playlist_remove_track_by_uuid(playlist, track_uuid); // Remove the track from the playlist
|
||||
}
|
||||
}
|
||||
|
||||
gtk_popover_popdown(GTK_POPOVER(self)); // Temporary to hopefully prevent a bork
|
||||
}
|
||||
|
||||
void koto_add_remove_track_popover_handle_playlist_added(KotoCartographer *carto, KotoPlaylist *playlist, gpointer user_data) {
|
||||
(void) carto;
|
||||
KotoAddRemoveTrackPopover *self = user_data;
|
||||
|
||||
if (!KOTO_JS_ADD_REMOVE_TRACK_POPOVER(self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
koto_add_remove_track_popover_add_playlist(self, playlist);
|
||||
}
|
||||
|
||||
void koto_add_remove_track_popover_handle_playlist_removed(KotoCartographer *carto, gchar *playlist_uuid, gpointer user_data) {
|
||||
(void) carto;
|
||||
KotoAddRemoveTrackPopover *self = user_data;
|
||||
|
||||
if (!KOTO_JS_ADD_REMOVE_TRACK_POPOVER(self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
koto_add_remove_track_popover_remove_playlist(self, playlist_uuid);
|
||||
}
|
||||
|
||||
void koto_add_remove_track_popover_set_pointing_to_widget(KotoAddRemoveTrackPopover *self, GtkWidget *widget, GtkPositionType pos) {
|
||||
if (!KOTO_JS_ADD_REMOVE_TRACK_POPOVER(self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GTK_IS_WIDGET(widget)) { // Not a widget
|
||||
return;
|
||||
}
|
||||
|
||||
GtkWidget* existing_parent = gtk_widget_get_parent(GTK_WIDGET(self));
|
||||
|
||||
if (existing_parent != NULL) {
|
||||
g_object_ref(GTK_WIDGET(self)); // Increment widget ref since unparent will do an unref
|
||||
gtk_widget_unparent(GTK_WIDGET(self)); // Unparent the popup
|
||||
}
|
||||
|
||||
gtk_widget_insert_after(GTK_WIDGET(self), widget, gtk_widget_get_last_child(widget));
|
||||
gtk_popover_set_position(GTK_POPOVER(self), pos);
|
||||
}
|
||||
|
||||
void koto_add_remove_track_popover_set_tracks(KotoAddRemoveTrackPopover *self, GList *tracks) {
|
||||
if (!KOTO_JS_ADD_REMOVE_TRACK_POPOVER(self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
gint tracks_len = g_list_length(tracks);
|
||||
|
||||
if (tracks_len == 0) { // No tracks
|
||||
return;
|
||||
}
|
||||
|
||||
self->tracks = g_list_copy(tracks);
|
||||
GHashTable *playlists = koto_cartographer_get_playlists(koto_maps); // Get our playlists
|
||||
GHashTableIter playlists_iter;
|
||||
gpointer uuid, playlist_ptr;
|
||||
|
||||
g_hash_table_iter_init(&playlists_iter, playlists); // Init our HashTable iterator
|
||||
|
||||
while (g_hash_table_iter_next(&playlists_iter, &uuid, &playlist_ptr)) { // While we are iterating through our playlists
|
||||
KotoPlaylist *playlist = playlist_ptr;
|
||||
gboolean should_be_checked = FALSE;
|
||||
|
||||
if (tracks_len > 1) { // More than one track
|
||||
GList *pos;
|
||||
for (pos = self->tracks; pos != NULL; pos = pos->next) { // Iterate over our tracks
|
||||
should_be_checked = (koto_playlist_get_position_of_track(playlist, pos->data) != -1);
|
||||
|
||||
if (!should_be_checked) { // Should not be checked
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
KotoIndexedTrack *track = g_list_nth_data(self->tracks, 0); // Get the first track
|
||||
|
||||
if (KOTO_IS_INDEXED_TRACK(track)) {
|
||||
gint pos = koto_playlist_get_position_of_track(playlist, track);
|
||||
should_be_checked = (pos != -1);
|
||||
}
|
||||
}
|
||||
|
||||
GtkCheckButton *playlist_check = g_hash_table_lookup(self->playlist_uuid_to_checkbox, uuid); // Get the GtkCheckButton for this playlist
|
||||
|
||||
if (GTK_IS_CHECK_BUTTON(playlist_check)) { // Is a checkbox
|
||||
gpointer sig_id_ptr = g_hash_table_lookup(self->checkbox_to_signal_ids, playlist_check);
|
||||
gulong check_button_sig_id = GPOINTER_TO_UINT(sig_id_ptr);
|
||||
g_signal_handler_block(playlist_check, check_button_sig_id); // Temporary ignore toggled signal, since set_active calls toggled
|
||||
gtk_check_button_set_active(playlist_check, should_be_checked); // Set active to our should_be_checked bool
|
||||
g_signal_handler_unblock(playlist_check, check_button_sig_id); // Unblock the signal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KotoAddRemoveTrackPopover* koto_add_remove_track_popover_new() {
|
||||
return g_object_new(KOTO_TYPE_ADD_REMOVE_TRACK_POPOVER, NULL);
|
||||
}
|
48
src/playlist/add-remove-track-popover.h
Normal file
48
src/playlist/add-remove-track-popover.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
/* add-remove-track-popover.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 "../db/cartographer.h"
|
||||
#include "playlist.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* Type Definition
|
||||
**/
|
||||
|
||||
#define KOTO_TYPE_ADD_REMOVE_TRACK_POPOVER koto_add_remove_track_popover_get_type()
|
||||
G_DECLARE_FINAL_TYPE(KotoAddRemoveTrackPopover, koto_add_remove_track_popover, KOTO, ADD_REMOVE_TRACK_POPOVER, GtkPopover);
|
||||
#define KOTO_JS_ADD_REMOVE_TRACK_POPOVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KOTO_TYPE_ADD_REMOVE_TRACK_POPOVER))
|
||||
|
||||
/**
|
||||
* Functions
|
||||
**/
|
||||
|
||||
KotoAddRemoveTrackPopover* koto_add_remove_track_popover_new();
|
||||
void koto_add_remove_track_popover_add_playlist(KotoAddRemoveTrackPopover *self, KotoPlaylist *playlist);
|
||||
void koto_add_remove_track_popover_clear_tracks(KotoAddRemoveTrackPopover *self);
|
||||
void koto_add_remove_track_popover_remove_playlist(KotoAddRemoveTrackPopover *self, gchar *playlist_uuid);
|
||||
void koto_add_remove_track_popover_handle_checkbutton_toggle(GtkCheckButton *btn, gpointer user_data);
|
||||
void koto_add_remove_track_popover_handle_playlist_added(KotoCartographer *carto, KotoPlaylist *playlist, gpointer user_data);
|
||||
void koto_add_remove_track_popover_handle_playlist_removed(KotoCartographer *carto, gchar *playlist_uuid, gpointer user_data);
|
||||
void koto_add_remove_track_popover_set_pointing_to_widget(KotoAddRemoveTrackPopover *self, GtkWidget *widget, GtkPositionType pos);
|
||||
void koto_add_remove_track_popover_set_tracks(KotoAddRemoveTrackPopover *self, GList *tracks);
|
||||
|
||||
G_END_DECLS
|
|
@ -1,99 +0,0 @@
|
|||
/* create-dialog.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 <glib-2.0/glib-object.h>
|
||||
#include <gtk-4.0/gtk/gtk.h>
|
||||
#include "../db/cartographer.h"
|
||||
#include "../db/db.h"
|
||||
#include "create-dialog.h"
|
||||
|
||||
extern GtkWindow *main_window;
|
||||
|
||||
struct _KotoCreatePlaylistDialog {
|
||||
GObject parent_instance;
|
||||
GtkWidget *content;
|
||||
GtkWidget *playlist_image;
|
||||
GtkFileChooserNative *playlist_file_chooser;
|
||||
GtkWidget *name_entry;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(KotoCreatePlaylistDialog, koto_create_playlist_dialog, G_TYPE_OBJECT);
|
||||
|
||||
static void koto_create_playlist_dialog_class_init(KotoCreatePlaylistDialogClass *c) {
|
||||
(void) c;
|
||||
}
|
||||
|
||||
static void koto_create_playlist_dialog_init(KotoCreatePlaylistDialog *self) {
|
||||
self->content = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
||||
gtk_widget_set_halign(self->content, GTK_ALIGN_CENTER);
|
||||
gtk_widget_set_valign(self->content, GTK_ALIGN_CENTER);
|
||||
|
||||
GtkIconTheme *default_icon_theme = gtk_icon_theme_get_for_display(gdk_display_get_default()); // Get the icon theme for this display
|
||||
|
||||
if (default_icon_theme != NULL) {
|
||||
gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self->content));
|
||||
GtkIconPaintable* audio_paintable = gtk_icon_theme_lookup_icon(default_icon_theme, "insert-image-symbolic", NULL, 96, scale_factor, GTK_TEXT_DIR_NONE, GTK_ICON_LOOKUP_PRELOAD);
|
||||
|
||||
if (GTK_IS_ICON_PAINTABLE(audio_paintable)) {
|
||||
self->playlist_image = gtk_image_new_from_paintable(GDK_PAINTABLE(audio_paintable));
|
||||
gtk_widget_set_size_request(self->playlist_image, 96, 96);
|
||||
gtk_box_append(GTK_BOX(self->content), self->playlist_image); // Add our image to our content
|
||||
|
||||
GtkGesture *image_click_controller = gtk_gesture_click_new(); // Create a click gesture for the image clicking
|
||||
gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(image_click_controller), 1); // Only allow left click
|
||||
g_signal_connect(GTK_EVENT_CONTROLLER(image_click_controller), "pressed", G_CALLBACK(koto_create_playlist_dialog_handle_image_click), self);
|
||||
|
||||
gtk_widget_add_controller(self->playlist_image, GTK_EVENT_CONTROLLER(image_click_controller));
|
||||
}
|
||||
}
|
||||
|
||||
self->playlist_file_chooser = gtk_file_chooser_native_new(
|
||||
"Choose playlist image",
|
||||
main_window,
|
||||
GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||
"Choose",
|
||||
"Cancel"
|
||||
);
|
||||
|
||||
GtkFileFilter *image_filter = gtk_file_filter_new(); // Create our file filter
|
||||
gtk_file_filter_add_pattern(image_filter, "image/*"); // Only allow for images
|
||||
gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(self->playlist_file_chooser), image_filter); // Only allow picking images
|
||||
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(self->playlist_file_chooser), FALSE);
|
||||
|
||||
self->name_entry = gtk_entry_new(); // Create our text entry for the name of the playlist
|
||||
gtk_entry_set_placeholder_text(GTK_ENTRY(self->name_entry), "Name of playlist");
|
||||
gtk_entry_set_input_purpose(GTK_ENTRY(self->name_entry), GTK_INPUT_PURPOSE_NAME);
|
||||
gtk_entry_set_input_hints(GTK_ENTRY(self->name_entry), GTK_INPUT_HINT_SPELLCHECK & GTK_INPUT_HINT_NO_EMOJI & GTK_INPUT_HINT_PRIVATE);
|
||||
|
||||
gtk_box_append(GTK_BOX(self->content), self->name_entry);
|
||||
}
|
||||
|
||||
GtkWidget* koto_create_playlist_dialog_get_content(KotoCreatePlaylistDialog *self) {
|
||||
return self->content;
|
||||
}
|
||||
|
||||
void koto_create_playlist_dialog_handle_image_click(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data) {
|
||||
(void) gesture; (void) n_press; (void) x; (void) y;
|
||||
|
||||
KotoCreatePlaylistDialog *self = user_data;
|
||||
|
||||
gtk_native_dialog_show(GTK_NATIVE_DIALOG(self->playlist_file_chooser)); // Show our file chooser
|
||||
}
|
||||
|
||||
KotoCreatePlaylistDialog* koto_create_playlist_dialog_new() {
|
||||
return g_object_new(KOTO_TYPE_CREATE_PLAYLIST_DIALOG, NULL);
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
/* create-dialog.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>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* Type Definition
|
||||
**/
|
||||
|
||||
#define KOTO_TYPE_CREATE_PLAYLIST_DIALOG koto_create_playlist_dialog_get_type()
|
||||
G_DECLARE_FINAL_TYPE(KotoCreatePlaylistDialog, koto_create_playlist_dialog, KOTO, CREATE_PLAYLIST_DIALOG, GObject);
|
||||
|
||||
/**
|
||||
* Create Dialog Functions
|
||||
**/
|
||||
|
||||
KotoCreatePlaylistDialog* koto_create_playlist_dialog_new();
|
||||
GtkWidget* koto_create_playlist_dialog_get_content(KotoCreatePlaylistDialog *self);
|
||||
void koto_create_playlist_dialog_handle_close(KotoCreatePlaylistDialog *self);
|
||||
void koto_create_playlist_dialog_handle_create(KotoCreatePlaylistDialog *self);
|
||||
void koto_create_playlist_dialog_handle_image_click(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data);
|
||||
|
||||
G_END_DECLS
|
301
src/playlist/create-modify-dialog.c
Normal file
301
src/playlist/create-modify-dialog.c
Normal file
|
@ -0,0 +1,301 @@
|
|||
/* create-modify-dialog.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-object.h>
|
||||
#include <gtk-4.0/gtk/gtk.h>
|
||||
#include <magic.h>
|
||||
#include "../db/cartographer.h"
|
||||
#include "../playlist/playlist.h"
|
||||
#include "../koto-utils.h"
|
||||
#include "../koto-window.h"
|
||||
#include "create-modify-dialog.h"
|
||||
|
||||
extern KotoCartographer *koto_maps;
|
||||
extern KotoWindow *main_window;
|
||||
|
||||
enum {
|
||||
PROP_DIALOG_0,
|
||||
PROP_PLAYLIST_UUID,
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
static GParamSpec *dialog_props[N_PROPS] = { NULL, };
|
||||
|
||||
|
||||
struct _KotoCreateModifyPlaylistDialog {
|
||||
GtkBox parent_instance;
|
||||
GtkWidget *playlist_image;
|
||||
GtkWidget *name_entry;
|
||||
|
||||
GtkWidget *create_button;
|
||||
|
||||
gchar *playlist_image_path;
|
||||
gchar *playlist_uuid;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(KotoCreateModifyPlaylistDialog, koto_create_modify_playlist_dialog, GTK_TYPE_BOX);
|
||||
|
||||
KotoCreateModifyPlaylistDialog *playlist_create_modify_dialog;
|
||||
|
||||
static void koto_create_modify_playlist_dialog_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec);
|
||||
static void koto_create_modify_playlist_dialog_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec);
|
||||
|
||||
static void koto_create_modify_playlist_dialog_class_init(KotoCreateModifyPlaylistDialogClass *c) {
|
||||
GObjectClass *gobject_class;
|
||||
gobject_class = G_OBJECT_CLASS(c);
|
||||
gobject_class->set_property = koto_create_modify_playlist_dialog_set_property;
|
||||
gobject_class->get_property = koto_create_modify_playlist_dialog_get_property;
|
||||
|
||||
dialog_props[PROP_PLAYLIST_UUID] = g_param_spec_string(
|
||||
"playlist-uuid",
|
||||
"Playlist UUID",
|
||||
"Playlist UUID",
|
||||
NULL,
|
||||
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
|
||||
);
|
||||
|
||||
g_object_class_install_properties(gobject_class, N_PROPS, dialog_props);
|
||||
}
|
||||
|
||||
static void koto_create_modify_playlist_dialog_init(KotoCreateModifyPlaylistDialog *self) {
|
||||
self->playlist_image_path = NULL;
|
||||
|
||||
gtk_widget_set_halign(GTK_WIDGET(self), GTK_ALIGN_CENTER);
|
||||
gtk_widget_set_valign(GTK_WIDGET(self), GTK_ALIGN_CENTER);
|
||||
|
||||
self->playlist_image = gtk_image_new_from_icon_name("insert-image-symbolic");
|
||||
gtk_image_set_pixel_size(GTK_IMAGE(self->playlist_image), 220);
|
||||
gtk_widget_set_size_request(self->playlist_image, 220, 220);
|
||||
gtk_box_append(GTK_BOX(self), self->playlist_image); // Add our image
|
||||
|
||||
GtkDropTarget *target = gtk_drop_target_new(G_TYPE_FILE, GDK_ACTION_COPY);
|
||||
g_signal_connect(GTK_EVENT_CONTROLLER(target), "drop", G_CALLBACK(koto_create_modify_playlist_dialog_handle_drop), self);
|
||||
gtk_widget_add_controller(self->playlist_image, GTK_EVENT_CONTROLLER(target));
|
||||
|
||||
GtkGesture *image_click_controller = gtk_gesture_click_new(); // Create a click gesture for the image clicking
|
||||
gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(image_click_controller), 1); // Only allow left click
|
||||
g_signal_connect(GTK_EVENT_CONTROLLER(image_click_controller), "pressed", G_CALLBACK(koto_create_modify_playlist_dialog_handle_image_click), self);
|
||||
|
||||
gtk_widget_add_controller(self->playlist_image, GTK_EVENT_CONTROLLER(image_click_controller));
|
||||
|
||||
self->name_entry = gtk_entry_new(); // Create our text entry for the name of the playlist
|
||||
gtk_entry_set_placeholder_text(GTK_ENTRY(self->name_entry), "Name of playlist");
|
||||
gtk_entry_set_input_purpose(GTK_ENTRY(self->name_entry), GTK_INPUT_PURPOSE_NAME);
|
||||
gtk_entry_set_input_hints(GTK_ENTRY(self->name_entry), GTK_INPUT_HINT_SPELLCHECK & GTK_INPUT_HINT_NO_EMOJI & GTK_INPUT_HINT_PRIVATE);
|
||||
|
||||
gtk_box_append(GTK_BOX(self), self->name_entry);
|
||||
|
||||
self->create_button = gtk_button_new_with_label("Create");
|
||||
gtk_widget_add_css_class(self->create_button, "suggested-action");
|
||||
g_signal_connect(self->create_button, "clicked", G_CALLBACK(koto_create_modify_playlist_dialog_handle_create_click), self);
|
||||
gtk_box_append(GTK_BOX(self), self->create_button); // Add the create button
|
||||
}
|
||||
|
||||
static void koto_create_modify_playlist_dialog_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec){
|
||||
KotoCreateModifyPlaylistDialog *self = KOTO_CREATE_MODIFY_PLAYLIST_DIALOG(obj);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_PLAYLIST_UUID:
|
||||
g_value_set_string(val, (self->playlist_uuid != NULL) ? g_strdup(self->playlist_uuid) : NULL);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void koto_create_modify_playlist_dialog_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec) {
|
||||
KotoCreateModifyPlaylistDialog *self = KOTO_CREATE_MODIFY_PLAYLIST_DIALOG(obj);
|
||||
(void) self; (void) val;
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_PLAYLIST_UUID:
|
||||
// TODO: Implement
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void koto_create_modify_playlist_dialog_handle_chooser_response(GtkNativeDialog *native, int response, gpointer user_data) {
|
||||
if (response != GTK_RESPONSE_ACCEPT) { // Not accept
|
||||
g_object_unref(native);
|
||||
return;
|
||||
}
|
||||
|
||||
KotoCreateModifyPlaylistDialog *self = user_data;
|
||||
if (!KOTO_IS_CURRENT_MODIFY_PLAYLIST(self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
GFile *file = gtk_file_chooser_get_file(GTK_FILE_CHOOSER(native));
|
||||
gchar *file_path = g_file_get_path(file); // Get the absolute path
|
||||
|
||||
if (file_path != NULL) {
|
||||
self->playlist_image_path = g_strdup(file_path);
|
||||
gtk_image_set_from_file(GTK_IMAGE(self->playlist_image), self->playlist_image_path); // Set the file path
|
||||
g_free(file_path);
|
||||
}
|
||||
|
||||
g_object_unref(file);
|
||||
g_object_unref(native);
|
||||
}
|
||||
|
||||
void koto_create_modify_playlist_dialog_handle_create_click(GtkButton *button, gpointer user_data) {
|
||||
(void) button;
|
||||
|
||||
KotoCreateModifyPlaylistDialog *self = user_data;
|
||||
|
||||
if (!KOTO_IS_CURRENT_MODIFY_PLAYLIST(self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gtk_entry_get_text_length(GTK_ENTRY(self->name_entry)) == 0) { // No text
|
||||
gtk_widget_grab_focus(GTK_WIDGET(self->name_entry)); // Select the name entry
|
||||
return;
|
||||
}
|
||||
|
||||
KotoPlaylist *playlist = NULL;
|
||||
gboolean modify_existing_playlist = ((self->playlist_uuid != NULL) && (g_strcmp0(self->playlist_uuid, "") != 0));
|
||||
|
||||
if (modify_existing_playlist) { // Modifying an existing playlist
|
||||
playlist = koto_cartographer_get_playlist_by_uuid(koto_maps, self->playlist_uuid);
|
||||
} else { // Creating a new playlist
|
||||
playlist = koto_playlist_new(); // Create a new playlist with a new UUID
|
||||
}
|
||||
|
||||
if (!KOTO_IS_PLAYLIST(playlist)) { // If this isn't a playlist
|
||||
return;
|
||||
}
|
||||
|
||||
koto_playlist_set_name(playlist, gtk_entry_buffer_get_text(gtk_entry_get_buffer(GTK_ENTRY(self->name_entry)))); // Set the name to the text we get from the entry buffer
|
||||
koto_playlist_set_artwork(playlist, self->playlist_image_path); // Add the playlist path if any
|
||||
koto_playlist_commit(playlist); // Save the playlist to the database
|
||||
|
||||
if (!modify_existing_playlist) { // Not a new playlist
|
||||
koto_cartographer_add_playlist(koto_maps, playlist); // Add to cartographer
|
||||
koto_playlist_mark_as_finalized(playlist); // Ensure our tracks loaded finalized signal is emitted for the new playlist
|
||||
}
|
||||
|
||||
koto_create_modify_playlist_dialog_reset(self);
|
||||
koto_window_hide_dialogs(main_window); // Hide the dialogs
|
||||
}
|
||||
|
||||
gboolean koto_create_modify_playlist_dialog_handle_drop(GtkDropTarget *target, const GValue *val, double x, double y, gpointer user_data) {
|
||||
(void) target; (void) x; (void) y;
|
||||
|
||||
if (!G_VALUE_HOLDS(val, G_TYPE_FILE)) { // Not a file
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
KotoCreateModifyPlaylistDialog *self = user_data;
|
||||
|
||||
if (!KOTO_IS_CURRENT_MODIFY_PLAYLIST(self)) { // No dialog
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GFile *dropped_file = g_value_get_object(val); // Get the GValue
|
||||
gchar *file_path = g_file_get_path(dropped_file); // Get the absolute path
|
||||
g_object_unref(dropped_file); // Unref the file
|
||||
|
||||
if (file_path == NULL) {
|
||||
return FALSE; // Failed to get the path so immediately return false
|
||||
}
|
||||
|
||||
magic_t magic_cookie = magic_open(MAGIC_MIME);
|
||||
|
||||
if (magic_cookie == NULL) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (magic_load(magic_cookie, NULL) != 0) {
|
||||
goto cookie_closure;
|
||||
}
|
||||
|
||||
const char *mime_type = magic_file(magic_cookie, file_path);
|
||||
|
||||
if ((mime_type != NULL) && g_str_has_prefix(mime_type, "image/")) { // Is an image
|
||||
self->playlist_image_path = g_strdup(file_path);
|
||||
gtk_image_set_from_file(GTK_IMAGE(self->playlist_image), self->playlist_image_path); // Set the file path
|
||||
g_free(file_path);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
cookie_closure:
|
||||
magic_close(magic_cookie);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void koto_create_modify_playlist_dialog_handle_image_click(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data) {
|
||||
(void) gesture; (void) n_press; (void) x; (void) y;
|
||||
|
||||
KotoCreateModifyPlaylistDialog *self = user_data;
|
||||
|
||||
GtkFileChooserNative* chooser = koto_utils_create_image_file_chooser("Choose playlist image");
|
||||
g_signal_connect(chooser, "response", G_CALLBACK(koto_create_modify_playlist_dialog_handle_chooser_response), self);
|
||||
gtk_native_dialog_show(GTK_NATIVE_DIALOG(chooser)); // Show our file chooser
|
||||
}
|
||||
|
||||
void koto_create_modify_playlist_dialog_reset(KotoCreateModifyPlaylistDialog *self) {
|
||||
if (!KOTO_IS_CURRENT_MODIFY_PLAYLIST(self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
gtk_entry_buffer_set_text(gtk_entry_get_buffer(GTK_ENTRY(self->name_entry)), "", -1); // Reset existing buffer to empty string
|
||||
gtk_entry_set_placeholder_text(GTK_ENTRY(self->name_entry), "Name of playlist"); // Reset placeholder
|
||||
gtk_image_set_from_icon_name(GTK_IMAGE(self->playlist_image), "insert-image-symbolic"); // Reset the image
|
||||
gtk_button_set_label(GTK_BUTTON(self->create_button), "Create");
|
||||
}
|
||||
|
||||
void koto_create_modify_playlist_dialog_set_playlist_uuid(KotoCreateModifyPlaylistDialog *self, gchar *playlist_uuid) {
|
||||
if ((playlist_uuid == NULL) || g_strcmp0(playlist_uuid, "") == 0) { // Is an empty string or not a string at all
|
||||
return;
|
||||
}
|
||||
|
||||
KotoPlaylist *playlist = koto_cartographer_get_playlist_by_uuid(koto_maps, playlist_uuid);
|
||||
|
||||
if (!KOTO_IS_PLAYLIST(playlist)) {
|
||||
return;
|
||||
}
|
||||
|
||||
self->playlist_uuid = g_strdup(playlist_uuid);
|
||||
gtk_entry_buffer_set_text(gtk_entry_get_buffer(GTK_ENTRY(self->name_entry)), koto_playlist_get_name(playlist), -1); // Update the input buffer
|
||||
gtk_entry_set_placeholder_text(GTK_ENTRY(self->name_entry), ""); // Clear placeholder
|
||||
|
||||
gchar *art = koto_playlist_get_artwork(playlist);
|
||||
|
||||
if ((art == NULL) || (g_strcmp0(art, "") == 0)) { // Is an empty string or not set
|
||||
gtk_image_set_from_icon_name(GTK_IMAGE(self->playlist_image), "insert-image-symbolic"); // Reset the image
|
||||
} else {
|
||||
gtk_image_set_from_file(GTK_IMAGE(self->playlist_image), art);
|
||||
g_free(art);
|
||||
}
|
||||
|
||||
gtk_button_set_label(GTK_BUTTON(self->create_button), "Save");
|
||||
}
|
||||
|
||||
KotoCreateModifyPlaylistDialog* koto_create_modify_playlist_dialog_new(char *playlist_uuid) {
|
||||
(void) playlist_uuid;
|
||||
|
||||
return g_object_new(KOTO_TYPE_CREATE_MODIFY_PLAYLIST_DIALOG,
|
||||
"orientation",
|
||||
GTK_ORIENTATION_VERTICAL,
|
||||
"spacing",
|
||||
40,
|
||||
NULL);
|
||||
}
|
44
src/playlist/create-modify-dialog.h
Normal file
44
src/playlist/create-modify-dialog.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
/* create-modify-dialog.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>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* Type Definition
|
||||
**/
|
||||
|
||||
#define KOTO_TYPE_CREATE_MODIFY_PLAYLIST_DIALOG koto_create_modify_playlist_dialog_get_type()
|
||||
G_DECLARE_FINAL_TYPE(KotoCreateModifyPlaylistDialog, koto_create_modify_playlist_dialog, KOTO, CREATE_MODIFY_PLAYLIST_DIALOG, GtkBox);
|
||||
#define KOTO_IS_CURRENT_MODIFY_PLAYLIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KOTO_TYPE_CREATE_MODIFY_PLAYLIST_DIALOG))
|
||||
|
||||
/**
|
||||
* Functions
|
||||
**/
|
||||
|
||||
KotoCreateModifyPlaylistDialog* koto_create_modify_playlist_dialog_new();
|
||||
void koto_create_modify_playlist_dialog_handle_chooser_response(GtkNativeDialog *native, int response, gpointer user_data);
|
||||
void koto_create_modify_playlist_dialog_handle_create_click(GtkButton *button, gpointer user_data);
|
||||
gboolean koto_create_modify_playlist_dialog_handle_drop(GtkDropTarget *target, const GValue *val, double x, double y, gpointer user_data);
|
||||
void koto_create_modify_playlist_dialog_handle_image_click(GtkGestureClick *gesture, int n_press, double x, double y, gpointer user_data);
|
||||
void koto_create_modify_playlist_dialog_reset(KotoCreateModifyPlaylistDialog *self);
|
||||
void koto_create_modify_playlist_dialog_set_playlist_uuid(KotoCreateModifyPlaylistDialog *self, gchar *playlist_uuid);
|
||||
|
||||
G_END_DECLS
|
|
@ -112,6 +112,8 @@ void koto_current_playlist_set_playlist(KotoCurrentPlaylist *self, KotoPlaylist
|
|||
}
|
||||
|
||||
self->current_playlist = playlist;
|
||||
// TODO: Saved state
|
||||
koto_playlist_set_position(self->current_playlist, -1); // Reset our position, use -1 since "next" song is then 0
|
||||
g_object_ref(playlist); // Increment the reference
|
||||
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_CURRENT_PLAYLIST]);
|
||||
}
|
||||
|
|
|
@ -16,29 +16,16 @@
|
|||
*/
|
||||
|
||||
#include <glib-2.0/glib.h>
|
||||
#include <glib-2.0/gio/gio.h>
|
||||
#include <magic.h>
|
||||
#include <sqlite3.h>
|
||||
#include "../db/cartographer.h"
|
||||
#include "../koto-utils.h"
|
||||
#include "playlist.h"
|
||||
|
||||
extern KotoCartographer *koto_maps;
|
||||
extern sqlite3 *koto_db;
|
||||
|
||||
struct _KotoPlaylist {
|
||||
GObject parent_instance;
|
||||
gchar *uuid;
|
||||
gchar *name;
|
||||
gchar *art_path;
|
||||
gint current_position;
|
||||
gboolean ephemeral;
|
||||
gboolean is_shuffle_enabled;
|
||||
|
||||
GQueue *tracks;
|
||||
GQueue *played_tracks;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(KotoPlaylist, koto_playlist, G_TYPE_OBJECT);
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_UUID,
|
||||
|
@ -49,7 +36,47 @@ enum {
|
|||
N_PROPERTIES,
|
||||
};
|
||||
|
||||
enum {
|
||||
SIGNAL_TRACK_ADDED,
|
||||
SIGNAL_TRACK_LOAD_FINALIZED,
|
||||
SIGNAL_TRACK_REMOVED,
|
||||
N_SIGNALS
|
||||
};
|
||||
|
||||
struct _KotoPlaylist {
|
||||
GObject parent_instance;
|
||||
gchar *uuid;
|
||||
gchar *name;
|
||||
gchar *art_path;
|
||||
gint current_position;
|
||||
gchar *current_uuid;
|
||||
|
||||
KotoPreferredModelType model;
|
||||
|
||||
gboolean ephemeral;
|
||||
gboolean is_shuffle_enabled;
|
||||
gboolean finalized;
|
||||
|
||||
GListStore *store;
|
||||
GQueue *sorted_tracks;
|
||||
|
||||
GQueue *tracks; // This is effectively our vanilla value that should never change
|
||||
GQueue *played_tracks;
|
||||
};
|
||||
|
||||
struct _KotoPlaylistClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (* track_added) (KotoPlaylist *playlist, gchar *track_uuid);
|
||||
void (* track_load_finalized) (KotoPlaylist *playlist);
|
||||
void (* track_removed) (KotoPlaylist *playlist, gchar *track_uuid);
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(KotoPlaylist, koto_playlist, G_TYPE_OBJECT);
|
||||
|
||||
static GParamSpec *props[N_PROPERTIES] = { NULL };
|
||||
static guint playlist_signals[N_SIGNALS] = { 0 };
|
||||
|
||||
static void koto_playlist_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec);;
|
||||
static void koto_playlist_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec);
|
||||
|
||||
|
@ -100,6 +127,44 @@ static void koto_playlist_class_init(KotoPlaylistClass *c) {
|
|||
);
|
||||
|
||||
g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
|
||||
|
||||
playlist_signals[SIGNAL_TRACK_ADDED] = g_signal_new(
|
||||
"track-added",
|
||||
G_TYPE_FROM_CLASS(gobject_class),
|
||||
G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
|
||||
G_STRUCT_OFFSET(KotoPlaylistClass, track_added),
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
G_TYPE_NONE,
|
||||
1,
|
||||
G_TYPE_CHAR
|
||||
);
|
||||
|
||||
playlist_signals[SIGNAL_TRACK_LOAD_FINALIZED] = g_signal_new(
|
||||
"track-load-finalized",
|
||||
G_TYPE_FROM_CLASS(gobject_class),
|
||||
G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
|
||||
G_STRUCT_OFFSET(KotoPlaylistClass, track_load_finalized),
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
G_TYPE_NONE,
|
||||
0
|
||||
);
|
||||
|
||||
playlist_signals[SIGNAL_TRACK_REMOVED] = g_signal_new(
|
||||
"track-removed",
|
||||
G_TYPE_FROM_CLASS(gobject_class),
|
||||
G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
|
||||
G_STRUCT_OFFSET(KotoPlaylistClass, track_removed),
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
G_TYPE_NONE,
|
||||
1,
|
||||
G_TYPE_CHAR
|
||||
);
|
||||
}
|
||||
|
||||
static void koto_playlist_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) {
|
||||
|
@ -154,9 +219,16 @@ static void koto_playlist_set_property(GObject *obj, guint prop_id, const GValue
|
|||
|
||||
static void koto_playlist_init(KotoPlaylist *self) {
|
||||
self->current_position = -1; // Default to -1 so first time incrementing puts it at 0
|
||||
self->current_uuid = NULL;
|
||||
self->model = KOTO_PREFERRED_MODEL_TYPE_DEFAULT; // Default to default model
|
||||
self->is_shuffle_enabled = FALSE;
|
||||
self->played_tracks = g_queue_new(); // Set as an empty GQueue
|
||||
self->ephemeral = FALSE;
|
||||
self->finalized = FALSE;
|
||||
|
||||
self->tracks = g_queue_new(); // Set as an empty GQueue
|
||||
self->played_tracks = g_queue_new(); // Set as an empty GQueue
|
||||
self->sorted_tracks = g_queue_new(); // Set as an empty GQueue
|
||||
self->store = g_list_store_new(KOTO_TYPE_INDEXED_TRACK);
|
||||
}
|
||||
|
||||
void koto_playlist_add_to_played_tracks(KotoPlaylist *self, gchar *uuid) {
|
||||
|
@ -167,17 +239,62 @@ void koto_playlist_add_to_played_tracks(KotoPlaylist *self, gchar *uuid) {
|
|||
g_queue_push_tail(self->played_tracks, uuid); // Add to end
|
||||
}
|
||||
|
||||
void koto_playlist_add_track(KotoPlaylist *self, KotoIndexedTrack *track) {
|
||||
gchar *uuid = NULL;
|
||||
g_object_get(track, "uuid", &uuid, NULL); // Get the UUID
|
||||
koto_playlist_add_track_by_uuid(self, uuid); // Add by the file's UUID
|
||||
void koto_playlist_add_track(KotoPlaylist *self, KotoIndexedTrack *track, gboolean current, gboolean commit_to_table) {
|
||||
koto_playlist_add_track_by_uuid(self, koto_indexed_track_get_uuid(track), current, commit_to_table);
|
||||
}
|
||||
|
||||
void koto_playlist_add_track_by_uuid(KotoPlaylist *self, const gchar *uuid) {
|
||||
gchar *dup_uuid = g_strdup(uuid);
|
||||
void koto_playlist_add_track_by_uuid(KotoPlaylist *self, gchar *uuid, gboolean current, gboolean commit_to_table) {
|
||||
KotoIndexedTrack *track = koto_cartographer_get_track_by_uuid(koto_maps, uuid); // Get the track
|
||||
|
||||
g_queue_push_tail(self->tracks, dup_uuid); // Append the UUID to the tracks
|
||||
// TODO: Add to table
|
||||
if (!KOTO_IS_INDEXED_TRACK(track)) {
|
||||
return;
|
||||
}
|
||||
|
||||
GList *found_tracks_uuids = g_queue_find_custom(self->tracks, uuid, koto_playlist_compare_track_uuids);
|
||||
if (found_tracks_uuids != NULL) { // Is somewhere in the tracks already
|
||||
g_list_free(found_tracks_uuids);
|
||||
return;
|
||||
}
|
||||
|
||||
g_list_free(found_tracks_uuids);
|
||||
|
||||
g_queue_push_tail(self->tracks, uuid); // Prepend the UUID to the tracks
|
||||
g_queue_push_tail(self->sorted_tracks, uuid); // Also add to our sorted tracks
|
||||
g_list_store_append(self->store, track); // Add to the store
|
||||
|
||||
if (self->finalized) { // Is already finalized
|
||||
koto_playlist_apply_model(self, self->model); // Re-apply our current model to ensure our "sorted tracks" is sorted, as is our GLIstStore used in the playlist page
|
||||
}
|
||||
|
||||
if (commit_to_table) {
|
||||
koto_indexed_track_save_to_playlist(track, self->uuid, (g_strcmp0(self->current_uuid, uuid) == 0) ? 1 : 0); // Call to save the playlist to the track
|
||||
}
|
||||
|
||||
if (current && (g_queue_get_length(self->tracks) > 1)) { // Is current and NOT the first item
|
||||
self->current_uuid = uuid; // Mark this as current UUID
|
||||
}
|
||||
|
||||
g_signal_emit(
|
||||
self,
|
||||
playlist_signals[SIGNAL_TRACK_ADDED],
|
||||
0,
|
||||
uuid
|
||||
);
|
||||
}
|
||||
|
||||
void koto_playlist_apply_model(KotoPlaylist *self, KotoPreferredModelType preferred_model) {
|
||||
GList *sort_user_data = NULL;
|
||||
sort_user_data = g_list_prepend(sort_user_data, GUINT_TO_POINTER(preferred_model)); // Prepend our preferred model first
|
||||
sort_user_data = g_list_prepend(sort_user_data, self); // Prepend ourself
|
||||
|
||||
g_queue_sort(self->sorted_tracks, koto_playlist_model_sort_by_uuid, sort_user_data); // Sort tracks, which is by UUID
|
||||
g_list_store_sort(self->store, koto_playlist_model_sort_by_track, sort_user_data); // Sort tracks by indexed tracks
|
||||
|
||||
self->model = preferred_model; // Update our preferred model
|
||||
|
||||
/*if (self->current_position != -1) { // Have a position set
|
||||
koto_playlist_set_track_as_current(self, self->current_uuid); // Update the position based on the new model just by setting it as current again
|
||||
}*/
|
||||
}
|
||||
|
||||
void koto_playlist_commit(KotoPlaylist *self) {
|
||||
|
@ -186,8 +303,8 @@ void koto_playlist_commit(KotoPlaylist *self) {
|
|||
}
|
||||
|
||||
gchar *commit_op = g_strdup_printf(
|
||||
"INSERT INTO playlist_meta(id, name, art_path)"
|
||||
"VALUES('%s', quote(\"%s\"), quote(\"%s\")"
|
||||
"INSERT INTO playlist_meta(id, name, art_path, preferred_model)"
|
||||
"VALUES('%s', quote(\"%s\"), quote(\"%s\"), 0)"
|
||||
"ON CONFLICT(id) DO UPDATE SET name=excluded.name, art_path=excluded.art_path;",
|
||||
self->uuid,
|
||||
self->name,
|
||||
|
@ -215,22 +332,30 @@ void koto_playlist_commit_tracks(gpointer data, gpointer user_data) {
|
|||
gchar *playlist_uuid = self->uuid; // Get the playlist UUID
|
||||
|
||||
gchar *current_track = g_queue_peek_nth(self->tracks, self->current_position); // Get the UUID of the current track
|
||||
koto_indexed_track_save_to_playlist(track, playlist_uuid, g_queue_index(self->tracks, data), (data == current_track) ? 1 : 0); // Call to save the playlist to the track
|
||||
//koto_indexed_track_save_to_playlist(track, playlist_uuid, (data == current_track) ? 1 : 0); // Call to save the playlist to the track
|
||||
g_free(playlist_uuid);
|
||||
g_free(current_track);
|
||||
}
|
||||
}
|
||||
|
||||
gint koto_playlist_compare_track_uuids(gconstpointer a, gconstpointer b) {
|
||||
return g_strcmp0(a, b);
|
||||
}
|
||||
|
||||
gchar* koto_playlist_get_artwork(KotoPlaylist *self) {
|
||||
return g_strdup(self->art_path); // Return a duplicate of our art path
|
||||
return (self->art_path == NULL) ? NULL : g_strdup(self->art_path); // Return a duplicate of our art path
|
||||
}
|
||||
|
||||
KotoPreferredModelType koto_playlist_get_current_model(KotoPlaylist *self) {
|
||||
return self->model;
|
||||
}
|
||||
|
||||
guint koto_playlist_get_current_position(KotoPlaylist *self) {
|
||||
return self->current_position;
|
||||
}
|
||||
|
||||
gchar* koto_playlist_get_current_uuid(KotoPlaylist *self) {
|
||||
return g_queue_peek_nth(self->tracks, self->current_position);
|
||||
gboolean koto_playlist_get_is_finalized(KotoPlaylist *self) {
|
||||
return self->finalized;
|
||||
}
|
||||
|
||||
guint koto_playlist_get_length(KotoPlaylist *self) {
|
||||
|
@ -241,12 +366,35 @@ gchar* koto_playlist_get_name(KotoPlaylist *self) {
|
|||
return (self->name == NULL) ? NULL : g_strdup(self->name);
|
||||
}
|
||||
|
||||
gint koto_playlist_get_position_of_track(KotoPlaylist *self, KotoIndexedTrack *track) {
|
||||
if (!KOTO_IS_PLAYLIST(self)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!G_IS_LIST_STORE(self->store)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!KOTO_IS_INDEXED_TRACK(track)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
gint position = -1;
|
||||
guint found_pos = 0;
|
||||
|
||||
if (g_list_store_find(self->store , track, &found_pos)) { // Found the item
|
||||
position = (gint) found_pos; // Cast our found position from guint to gint
|
||||
}
|
||||
|
||||
return position;
|
||||
}
|
||||
|
||||
gchar* koto_playlist_get_random_track(KotoPlaylist *self) {
|
||||
gchar *track_uuid = NULL;
|
||||
guint tracks_len = g_queue_get_length(self->tracks);
|
||||
guint tracks_len = g_queue_get_length(self->sorted_tracks);
|
||||
|
||||
if (tracks_len == g_queue_get_length(self->played_tracks)) { // Played all tracks
|
||||
track_uuid = g_list_nth_data(self->tracks->head, 0); // Get the first
|
||||
track_uuid = g_list_nth_data(self->sorted_tracks->head, 0); // Get the first
|
||||
g_queue_clear(self->played_tracks); // Clear our played tracks
|
||||
} else { // Have not played all tracks
|
||||
GRand* rando_calrissian = g_rand_new(); // Create a new RNG
|
||||
|
@ -255,7 +403,7 @@ gchar* koto_playlist_get_random_track(KotoPlaylist *self) {
|
|||
while (track_uuid == NULL) { // Haven't selected a track yet
|
||||
attempt++;
|
||||
gint32 selected_item = g_rand_int_range(rando_calrissian, 0, (gint32) tracks_len);
|
||||
gchar *selected_track = g_queue_peek_nth(self->tracks, (guint) selected_item); // Get the UUID of the selected item
|
||||
gchar *selected_track = g_queue_peek_nth(self->sorted_tracks, (guint) selected_item); // Get the UUID of the selected item
|
||||
|
||||
if (g_queue_index(self->played_tracks, selected_track) == -1) { // Haven't played the track
|
||||
self->current_position = (gint) selected_item;
|
||||
|
@ -274,6 +422,10 @@ gchar* koto_playlist_get_random_track(KotoPlaylist *self) {
|
|||
return track_uuid;
|
||||
}
|
||||
|
||||
GListStore* koto_playlist_get_store(KotoPlaylist *self) {
|
||||
return self->store;
|
||||
}
|
||||
|
||||
GQueue* koto_playlist_get_tracks(KotoPlaylist *self) {
|
||||
return self->tracks;
|
||||
}
|
||||
|
@ -283,22 +435,38 @@ gchar* koto_playlist_get_uuid(KotoPlaylist *self) {
|
|||
}
|
||||
|
||||
gchar* koto_playlist_go_to_next(KotoPlaylist *self) {
|
||||
if (!KOTO_IS_PLAYLIST(self)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (self->is_shuffle_enabled) { // Shuffling enabled
|
||||
gchar *random_track_uuid = koto_playlist_get_random_track(self); // Get a random track
|
||||
koto_playlist_add_to_played_tracks(self, random_track_uuid);
|
||||
return random_track_uuid;
|
||||
}
|
||||
|
||||
gchar *current_uuid = koto_playlist_get_current_uuid(self); // Get the current UUID
|
||||
if (self->current_uuid == NULL || (g_strcmp0(self->current_uuid, "") == 0)) {
|
||||
self->current_position++;
|
||||
} else { // Have a UUID currently
|
||||
KotoIndexedTrack *track = koto_cartographer_get_track_by_uuid(koto_maps, self->current_uuid);
|
||||
|
||||
if (current_uuid == self->tracks->tail->data) { // Current UUID matches the last item in the playlist
|
||||
return NULL;
|
||||
if (!KOTO_IS_INDEXED_TRACK(track)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gint pos_of_song = koto_playlist_get_position_of_track(self, track); // Get the position of the current track based on the current model
|
||||
|
||||
if ((guint) pos_of_song == (g_queue_get_length(self->sorted_tracks) - 1)) { // At end
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->current_position = pos_of_song+1; // Increment our position based on position of song
|
||||
}
|
||||
|
||||
self->current_position++; // Increment our position
|
||||
current_uuid = koto_playlist_get_current_uuid(self); // Return the new UUID
|
||||
koto_playlist_add_to_played_tracks(self, current_uuid);
|
||||
return current_uuid;
|
||||
self->current_uuid = g_queue_peek_nth(self->sorted_tracks, self->current_position);
|
||||
koto_playlist_add_to_played_tracks(self, self->current_uuid);
|
||||
|
||||
return self->current_uuid;
|
||||
}
|
||||
|
||||
gchar* koto_playlist_go_to_previous(KotoPlaylist *self) {
|
||||
|
@ -306,45 +474,217 @@ gchar* koto_playlist_go_to_previous(KotoPlaylist *self) {
|
|||
return koto_playlist_get_random_track(self); // Get a random track
|
||||
}
|
||||
|
||||
gchar *current_uuid = koto_playlist_get_current_uuid(self); // Get the current UUID
|
||||
|
||||
if (current_uuid == self->tracks->head->data) { // Current UUID matches the first item in the playlist
|
||||
if (self->current_uuid == NULL || (g_strcmp0(self->current_uuid, "") == 0)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->current_position--; // Decrement our position
|
||||
return koto_playlist_get_current_uuid(self); // Return the new UUID
|
||||
KotoIndexedTrack *track = koto_cartographer_get_track_by_uuid(koto_maps, self->current_uuid);
|
||||
|
||||
if (!KOTO_IS_INDEXED_TRACK(track)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gint pos_of_song = koto_playlist_get_position_of_track(self, track); // Get the position of the current track based on the current model
|
||||
|
||||
if (pos_of_song == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
self->current_position = pos_of_song - 1; // Decrement our position based on position of song
|
||||
self->current_uuid = g_queue_peek_nth(self->sorted_tracks, self->current_position);
|
||||
|
||||
return self->current_uuid;
|
||||
}
|
||||
|
||||
void koto_playlist_mark_as_finalized(KotoPlaylist *self) {
|
||||
if (self->finalized) { // Already finalized
|
||||
return;
|
||||
}
|
||||
|
||||
self->finalized = TRUE;
|
||||
koto_playlist_apply_model(self, self->model); // Re-apply our model to enforce mass sort
|
||||
|
||||
g_signal_emit(
|
||||
self,
|
||||
playlist_signals[SIGNAL_TRACK_LOAD_FINALIZED],
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
gint koto_playlist_model_sort_by_uuid(gconstpointer first_item, gconstpointer second_item, gpointer data_list) {
|
||||
KotoIndexedTrack *first_track = koto_cartographer_get_track_by_uuid(koto_maps, (gchar*) first_item);
|
||||
KotoIndexedTrack *second_track = koto_cartographer_get_track_by_uuid(koto_maps, (gchar*) second_item);
|
||||
|
||||
return koto_playlist_model_sort_by_track(first_track, second_track, data_list);
|
||||
}
|
||||
|
||||
gint koto_playlist_model_sort_by_track(gconstpointer first_item, gconstpointer second_item, gpointer data_list) {
|
||||
KotoIndexedTrack *first_track = (KotoIndexedTrack*) first_item;
|
||||
KotoIndexedTrack *second_track = (KotoIndexedTrack*) second_item;
|
||||
|
||||
GList* ptr_list = data_list;
|
||||
KotoPlaylist *self = g_list_nth_data(ptr_list, 0); // First item in the GPtrArray is a pointer to our playlist
|
||||
KotoPreferredModelType model = GPOINTER_TO_UINT(g_list_nth_data(ptr_list, 1)); // Second item in the GPtrArray is a pointer to our KotoPreferredModelType
|
||||
|
||||
if (
|
||||
(model == KOTO_PREFERRED_MODEL_TYPE_DEFAULT) || // Newest first model
|
||||
(model == KOTO_PREFERRED_MODEL_TYPE_OLDEST_FIRST) // Oldest first
|
||||
) {
|
||||
gint first_track_pos = g_queue_index(self->tracks, koto_indexed_track_get_uuid(first_track));
|
||||
gint second_track_pos = g_queue_index(self->tracks, koto_indexed_track_get_uuid(second_track));
|
||||
|
||||
if (first_track_pos == -1) { // First track isn't in tracks
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (second_track_pos == -1) { // Second track isn't in tracks
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (model == KOTO_PREFERRED_MODEL_TYPE_DEFAULT) { // Newest first
|
||||
return (first_track_pos < second_track_pos) ? 1 : -1; // Display first at end, not beginning
|
||||
} else {
|
||||
return (first_track_pos < second_track_pos) ? -1 : 1; // Display at beginning, not end
|
||||
}
|
||||
}
|
||||
|
||||
if (model == KOTO_PREFERRED_MODEL_TYPE_SORT_BY_ALBUM) { // Sort by album name
|
||||
gchar *first_album_uuid = NULL;
|
||||
gchar *second_album_uuid = NULL;
|
||||
|
||||
g_object_get(
|
||||
first_track,
|
||||
"album-uuid",
|
||||
&first_album_uuid,
|
||||
NULL
|
||||
);
|
||||
|
||||
g_object_get(
|
||||
second_track,
|
||||
"album-uuid",
|
||||
&second_album_uuid,
|
||||
NULL
|
||||
);
|
||||
|
||||
if (g_strcmp0(first_album_uuid, second_album_uuid) == 0) { // Identical albums
|
||||
g_free(first_album_uuid);
|
||||
g_free(second_album_uuid);
|
||||
return 0; // Don't get too granular, just consider them equal
|
||||
}
|
||||
|
||||
KotoIndexedAlbum *first_album = koto_cartographer_get_album_by_uuid(koto_maps, first_album_uuid);
|
||||
KotoIndexedAlbum *second_album = koto_cartographer_get_album_by_uuid(koto_maps, second_album_uuid);
|
||||
|
||||
g_free(first_album_uuid);
|
||||
g_free(second_album_uuid);
|
||||
|
||||
if (!KOTO_IS_INDEXED_ALBUM(first_album) && !KOTO_IS_INDEXED_ALBUM(second_album)) { // Neither are valid albums
|
||||
return 0; // Just consider them as equal
|
||||
}
|
||||
|
||||
return g_utf8_collate(koto_indexed_album_get_album_name(first_album), koto_indexed_album_get_album_name(second_album));
|
||||
}
|
||||
|
||||
if (model == KOTO_PREFERRED_MODEL_TYPE_SORT_BY_ARTIST) { // Sort by artist name
|
||||
gchar *first_artist_uuid = NULL;
|
||||
gchar *second_artist_uuid = NULL;
|
||||
|
||||
g_object_get(
|
||||
first_track,
|
||||
"artist-uuid",
|
||||
&first_artist_uuid,
|
||||
NULL
|
||||
);
|
||||
|
||||
g_object_get(
|
||||
second_track,
|
||||
"artist-uuid",
|
||||
&second_artist_uuid,
|
||||
NULL
|
||||
);
|
||||
|
||||
KotoIndexedArtist *first_artist = koto_cartographer_get_artist_by_uuid(koto_maps, first_artist_uuid);
|
||||
KotoIndexedArtist *second_artist = koto_cartographer_get_artist_by_uuid(koto_maps, second_artist_uuid);
|
||||
|
||||
g_free(first_artist_uuid);
|
||||
g_free(second_artist_uuid);
|
||||
|
||||
if (!KOTO_IS_INDEXED_ARTIST(first_artist) && !KOTO_IS_INDEXED_ARTIST(second_artist)) { // Neither are valid artists
|
||||
return 0; // Just consider them as equal
|
||||
}
|
||||
|
||||
return g_utf8_collate(koto_indexed_artist_get_name(first_artist), koto_indexed_artist_get_name(second_artist));
|
||||
}
|
||||
|
||||
if (model == KOTO_PREFERRED_MODEL_TYPE_SORT_BY_TRACK_NAME) { // Track name
|
||||
gchar *first_track_name = NULL;
|
||||
gchar *second_track_name = NULL;
|
||||
|
||||
g_object_get(
|
||||
first_track,
|
||||
"parsed-name",
|
||||
&first_track_name,
|
||||
NULL
|
||||
);
|
||||
|
||||
g_object_get(
|
||||
second_track,
|
||||
"parsed-name",
|
||||
&second_track_name,
|
||||
NULL
|
||||
);
|
||||
|
||||
gint ret = g_utf8_collate(first_track_name, second_track_name);
|
||||
g_free(first_track_name);
|
||||
g_free(second_track_name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void koto_playlist_remove_from_played_tracks(KotoPlaylist *self, gchar *uuid) {
|
||||
g_queue_remove(self->played_tracks, uuid);
|
||||
}
|
||||
|
||||
void koto_playlist_remove_track(KotoPlaylist *self, KotoIndexedTrack *track) {
|
||||
gchar *track_uuid = NULL;
|
||||
g_object_get(track, "uuid", &track_uuid, NULL);
|
||||
|
||||
if (track_uuid != NULL) {
|
||||
koto_playlist_remove_track_by_uuid(self, track_uuid);
|
||||
g_free(track_uuid);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void koto_playlist_remove_track_by_uuid(KotoPlaylist *self, gchar *uuid) {
|
||||
if (uuid == NULL) {
|
||||
if (!KOTO_IS_PLAYLIST(self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
gint file_index = g_queue_index(self->tracks, uuid); // Get the position of this uuid
|
||||
|
||||
if (file_index == -1) { // Does not exist in our tracks list
|
||||
if (file_index != -1) { // Have in tracks
|
||||
g_queue_pop_nth(self->tracks, file_index); // Remove nth where it is the file index
|
||||
}
|
||||
|
||||
gint file_index_in_sorted = g_queue_index(self->sorted_tracks, uuid); // Get position in sorted tracks
|
||||
|
||||
if (file_index_in_sorted != -1) { // Have in sorted tracks
|
||||
g_queue_pop_nth(self->sorted_tracks, file_index_in_sorted); // Remove nth where it is the index in sorted tracks
|
||||
}
|
||||
|
||||
KotoIndexedTrack *track = koto_cartographer_get_track_by_uuid(koto_maps, uuid); // Get the track
|
||||
|
||||
if (!KOTO_IS_INDEXED_TRACK(track)) { // Is not a track
|
||||
return;
|
||||
}
|
||||
|
||||
g_queue_pop_nth(self->tracks, file_index); // Remove nth where it is the file index
|
||||
return;
|
||||
guint position = 0;
|
||||
|
||||
if (g_list_store_find(self->store, track, &position)) { // Got the position
|
||||
g_list_store_remove(self->store, position); // Remove from the store
|
||||
}
|
||||
|
||||
koto_indexed_track_remove_from_playlist(track, self->uuid);
|
||||
|
||||
g_signal_emit(
|
||||
self,
|
||||
playlist_signals[SIGNAL_TRACK_REMOVED],
|
||||
0,
|
||||
uuid
|
||||
);
|
||||
}
|
||||
|
||||
void koto_playlist_set_artwork(KotoPlaylist *self, const gchar *path) {
|
||||
|
@ -376,11 +716,10 @@ void koto_playlist_set_artwork(KotoPlaylist *self, const gchar *path) {
|
|||
|
||||
free_cookie:
|
||||
magic_close(cookie); // Close and free the cookie to the cookie monster
|
||||
return;
|
||||
}
|
||||
|
||||
void koto_playlist_set_name(KotoPlaylist *self, const gchar *name) {
|
||||
if (name == NULL) { // No actual name
|
||||
if (name == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -389,7 +728,16 @@ void koto_playlist_set_name(KotoPlaylist *self, const gchar *name) {
|
|||
}
|
||||
|
||||
self->name = g_strdup(name);
|
||||
return;
|
||||
}
|
||||
|
||||
void koto_playlist_set_position(KotoPlaylist *self, gint position) {
|
||||
self->current_position = position;
|
||||
}
|
||||
|
||||
void koto_playlist_set_track_as_current(KotoPlaylist *self, gchar *track_uuid) {
|
||||
gint position_of_track = g_queue_index(self->sorted_tracks, track_uuid); // Get the position of the UUID in our tracks
|
||||
g_return_if_fail(position_of_track != -1);
|
||||
self->current_position = position_of_track;
|
||||
}
|
||||
|
||||
void koto_playlist_set_uuid(KotoPlaylist *self, const gchar *uuid) {
|
||||
|
@ -402,7 +750,17 @@ void koto_playlist_set_uuid(KotoPlaylist *self, const gchar *uuid) {
|
|||
}
|
||||
|
||||
self->uuid = g_strdup(uuid); // Set the new UUID
|
||||
return;
|
||||
}
|
||||
|
||||
void koto_playlist_tracks_queue_push_to_store(gpointer data, gpointer user_data) {
|
||||
gchar *track_uuid = (gchar *) data;
|
||||
KotoIndexedTrack *track = koto_cartographer_get_track_by_uuid(koto_maps, track_uuid);
|
||||
|
||||
if (!KOTO_IS_INDEXED_TRACK(track)) { // Not a track
|
||||
return;
|
||||
}
|
||||
|
||||
g_list_store_append(G_LIST_STORE(user_data), track);
|
||||
}
|
||||
|
||||
void koto_playlist_unmap(KotoPlaylist *self) {
|
||||
|
|
|
@ -16,19 +16,34 @@
|
|||
*/
|
||||
|
||||
#pragma once
|
||||
#include <glib-2.0/glib-object.h>
|
||||
#include <glib-2.0/glib.h>
|
||||
#include <glib-2.0/gio/gio.h>
|
||||
#include "../indexer/structs.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef enum {
|
||||
KOTO_PREFERRED_MODEL_TYPE_DEFAULT, // Considered to be newest first
|
||||
KOTO_PREFERRED_MODEL_TYPE_OLDEST_FIRST,
|
||||
KOTO_PREFERRED_MODEL_TYPE_SORT_BY_ALBUM,
|
||||
KOTO_PREFERRED_MODEL_TYPE_SORT_BY_ARTIST,
|
||||
KOTO_PREFERRED_MODEL_TYPE_SORT_BY_TRACK_NAME
|
||||
} KotoPreferredModelType;
|
||||
|
||||
/**
|
||||
* Type Definition
|
||||
**/
|
||||
|
||||
#define KOTO_TYPE_PLAYLIST koto_playlist_get_type()
|
||||
G_DECLARE_FINAL_TYPE(KotoPlaylist, koto_playlist, KOTO, PLAYLIST, GObject);
|
||||
#define KOTO_PLAYLIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), KOTO_TYPE_PLAYLIST, KotoPlaylist))
|
||||
#define KOTO_IS_PLAYLIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KOTO_TYPE_PLAYLIST))
|
||||
|
||||
typedef struct _KotoPlaylist KotoPlaylist;
|
||||
typedef struct _KotoPlaylistClass KotoPlaylistClass;
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GType koto_playlist_get_type(void) G_GNUC_CONST;
|
||||
|
||||
/**
|
||||
* Playlist Functions
|
||||
**/
|
||||
|
@ -36,27 +51,36 @@ G_DECLARE_FINAL_TYPE(KotoPlaylist, koto_playlist, KOTO, PLAYLIST, GObject);
|
|||
KotoPlaylist* koto_playlist_new();
|
||||
KotoPlaylist* koto_playlist_new_with_uuid(const gchar *uuid);
|
||||
void koto_playlist_add_to_played_tracks(KotoPlaylist *self, gchar *uuid);
|
||||
void koto_playlist_add_track(KotoPlaylist *self, KotoIndexedTrack *track);
|
||||
void koto_playlist_add_track_by_uuid(KotoPlaylist *self, const gchar *uuid);
|
||||
void koto_playlist_add_track(KotoPlaylist *self, KotoIndexedTrack *track, gboolean current, gboolean commit_to_table);
|
||||
void koto_playlist_add_track_by_uuid(KotoPlaylist *self, gchar *uuid, gboolean current, gboolean commit_to_table);
|
||||
void koto_playlist_apply_model(KotoPlaylist *self, KotoPreferredModelType preferred_model);
|
||||
void koto_playlist_commit(KotoPlaylist *self);
|
||||
void koto_playlist_commit_tracks(gpointer data, gpointer user_data);
|
||||
gint koto_playlist_compare_track_uuids(gconstpointer a, gconstpointer b);
|
||||
gchar* koto_playlist_get_artwork(KotoPlaylist *self);
|
||||
KotoPreferredModelType koto_playlist_get_current_model(KotoPlaylist *self);
|
||||
guint koto_playlist_get_current_position(KotoPlaylist *self);
|
||||
gchar* koto_playlist_get_current_uuid(KotoPlaylist *self);
|
||||
guint koto_playlist_get_length(KotoPlaylist *self);
|
||||
gboolean koto_playlist_get_is_finalized(KotoPlaylist *self);
|
||||
gchar* koto_playlist_get_name(KotoPlaylist *self);
|
||||
gint koto_playlist_get_position_of_track(KotoPlaylist *self, KotoIndexedTrack *track);
|
||||
GListStore* koto_playlist_get_store(KotoPlaylist *self);
|
||||
GQueue* koto_playlist_get_tracks(KotoPlaylist *self);
|
||||
gchar* koto_playlist_get_uuid(KotoPlaylist *self);
|
||||
gchar* koto_playlist_go_to_next(KotoPlaylist *self);
|
||||
gchar* koto_playlist_go_to_previous(KotoPlaylist *self);
|
||||
void koto_playlist_mark_as_finalized(KotoPlaylist *self);
|
||||
gint koto_playlist_model_sort_by_uuid(gconstpointer first_item, gconstpointer second_item, gpointer data_list);
|
||||
gint koto_playlist_model_sort_by_track(gconstpointer first_item, gconstpointer second_item, gpointer model_ptr);
|
||||
void koto_playlist_remove_from_played_tracks(KotoPlaylist *self, gchar *uuid);
|
||||
void koto_playlist_remove_track(KotoPlaylist *self, KotoIndexedTrack *track);
|
||||
void koto_playlist_remove_track_by_uuid(KotoPlaylist *self, gchar *uuid);
|
||||
void koto_playlist_set_artwork(KotoPlaylist *self, const gchar *path);
|
||||
void koto_playlist_save_state(KotoPlaylist *self);
|
||||
void koto_playlist_set_name(KotoPlaylist *self, const gchar *name);
|
||||
void koto_playlist_set_position(KotoPlaylist *self, guint pos);
|
||||
void koto_playlist_set_position(KotoPlaylist *self, gint position);
|
||||
void koto_playlist_set_track_as_current(KotoPlaylist *self, gchar *track_uuid);
|
||||
void koto_playlist_set_uuid(KotoPlaylist *self, const gchar *uuid);
|
||||
void koto_playlist_tracks_queue_push_to_store(gpointer data, gpointer user_data);
|
||||
void koto_playlist_unmap(KotoPlaylist *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue