287 lines
6.6 KiB
C
287 lines
6.6 KiB
C
/*
|
|
* Copyright 2016 (c) Anna Schumaker.
|
|
*/
|
|
#include <core/idle.h>
|
|
#include <core/playlists/generic.h>
|
|
#include <stdlib.h>
|
|
|
|
static struct playlist_callbacks *callbacks = NULL;
|
|
|
|
|
|
static int __playlist_generic_find_sort(gconstpointer a, gconstpointer b)
|
|
{
|
|
return abs(GPOINTER_TO_INT(a)) - abs(GPOINTER_TO_INT(b));
|
|
}
|
|
|
|
static int __playlist_generic_less_than(gconstpointer a, gconstpointer b,
|
|
gpointer data)
|
|
{
|
|
struct track *lhs = (struct track *)a;
|
|
struct track *rhs = (struct track *)b;
|
|
GSList *cur = ((struct playlist *)data)->pl_sort;
|
|
int res, field;
|
|
|
|
while (cur) {
|
|
field = GPOINTER_TO_INT(cur->data);
|
|
res = track_compare(lhs, rhs, abs(field));
|
|
if (res != 0)
|
|
break;
|
|
cur = g_slist_next(cur);
|
|
};
|
|
|
|
return (field > 0) ? res : -res;
|
|
}
|
|
|
|
|
|
void playlist_generic_set_callbacks(struct playlist_callbacks *cb)
|
|
{
|
|
callbacks = cb;
|
|
}
|
|
|
|
void playlist_generic_init(struct playlist *playlist)
|
|
{
|
|
if (playlist) {
|
|
g_queue_init(&playlist->pl_tracks);
|
|
playlist->pl_length = 0;
|
|
playlist->pl_random = false;
|
|
playlist->pl_current = NULL;
|
|
playlist->pl_sort = NULL;
|
|
playlist->pl_search = NULL;
|
|
}
|
|
}
|
|
|
|
void playlist_generic_init_sorted(struct playlist *playlist)
|
|
{
|
|
if (playlist) {
|
|
playlist_generic_init(playlist);
|
|
playlist_generic_sort(playlist, COMPARE_ARTIST);
|
|
playlist_generic_sort(playlist, COMPARE_YEAR);
|
|
playlist_generic_sort(playlist, COMPARE_TRACK);
|
|
}
|
|
}
|
|
|
|
void playlist_generic_deinit(struct playlist *playlist)
|
|
{
|
|
if (playlist) {
|
|
playlist_generic_clear(playlist);
|
|
playlist_clear_sort(playlist);
|
|
g_strfreev(playlist->pl_search);
|
|
playlist->pl_search = NULL;
|
|
playlist->pl_length = 0;
|
|
}
|
|
}
|
|
|
|
struct playlist *playlist_generic_alloc(gchar *name, enum playlist_type_t type,
|
|
unsigned int id, struct playlist_ops *ops)
|
|
{
|
|
struct playlist *playlist = g_malloc(sizeof(struct playlist));
|
|
|
|
playlist->pl_name = name;
|
|
playlist->pl_type = type;
|
|
playlist->pl_id = id;
|
|
playlist->pl_ops = ops;
|
|
|
|
playlist_generic_init_sorted(playlist);
|
|
if (callbacks)
|
|
callbacks->pl_cb_alloc(playlist);
|
|
return playlist;
|
|
}
|
|
|
|
void playlist_generic_free(struct playlist *playlist)
|
|
{
|
|
if (playlist) {
|
|
playlist_generic_deinit(playlist);
|
|
g_free(playlist);
|
|
}
|
|
}
|
|
|
|
void playlist_generic_save(struct playlist *playlist, struct file *file,
|
|
unsigned int flags)
|
|
{
|
|
playlist_iter it;
|
|
GSList *sort;
|
|
int field;
|
|
|
|
if (!playlist)
|
|
return;
|
|
|
|
if (flags & PL_SAVE_ITER)
|
|
file_writef(file, "%u ", playlist_current_index(playlist));
|
|
|
|
if (flags & PL_SAVE_FLAGS) {
|
|
sort = playlist->pl_sort;
|
|
file_writef(file, "%u ", playlist->pl_random ? PL_RANDOM : 0);
|
|
file_writef(file, "%u", g_slist_length(sort));
|
|
while (sort) {
|
|
field = GPOINTER_TO_INT(sort->data);
|
|
file_writef(file, " %u %d", abs(field) - 1, field > 0);
|
|
sort = g_slist_next(sort);
|
|
}
|
|
file_writef(file, "\n");
|
|
}
|
|
|
|
if (flags & PL_SAVE_TRACKS) {
|
|
file_writef(file, "%u", playlist_size(playlist));
|
|
playlist_for_each(playlist, it)
|
|
file_writef(file, " %u",
|
|
track_index(playlist_iter_track(it)));
|
|
file_writef(file, "\n");
|
|
}
|
|
}
|
|
|
|
void playlist_generic_load(struct playlist *playlist, struct file *file,
|
|
unsigned int flags)
|
|
{
|
|
unsigned int f, n, i, t, it = 0;
|
|
int field, ascending;
|
|
|
|
if (!playlist)
|
|
return;
|
|
|
|
if (flags & PL_SAVE_ITER)
|
|
it = file_readu(file);
|
|
|
|
if (flags & PL_SAVE_FLAGS) {
|
|
f = file_readu(file);
|
|
n = file_readu(file);
|
|
playlist_clear_sort(playlist);
|
|
for (i = 0; i < n; i++) {
|
|
field = file_readu(file) + 1;
|
|
ascending = file_readd(file);
|
|
if (!ascending)
|
|
field = -field;
|
|
playlist->pl_sort = g_slist_append(playlist->pl_sort,
|
|
GINT_TO_POINTER(field));
|
|
}
|
|
playlist_generic_resort(playlist);
|
|
}
|
|
|
|
if (flags & PL_SAVE_TRACKS) {
|
|
n = file_readu(file);
|
|
for (i = 0; i < n; i++) {
|
|
t = file_readu(file);
|
|
playlist_generic_add(playlist, track_get(t));
|
|
}
|
|
}
|
|
|
|
playlist_generic_set_random(playlist, f == PL_RANDOM);
|
|
playlist_current_set(playlist, it);
|
|
}
|
|
|
|
bool playlist_generic_can_select(struct playlist *playlist)
|
|
{
|
|
return playlist_size(playlist) > 0;
|
|
}
|
|
|
|
void playlist_generic_clear(struct playlist *playlist)
|
|
{
|
|
unsigned int n;
|
|
|
|
if (!playlist)
|
|
return;
|
|
|
|
n = playlist_size(playlist);
|
|
g_queue_clear(&playlist->pl_tracks);
|
|
playlist->pl_length = 0;
|
|
playlist->pl_current = NULL;
|
|
if (callbacks)
|
|
callbacks->pl_cb_removed(playlist, NULL, n);
|
|
}
|
|
|
|
bool playlist_generic_add(struct playlist *playlist, struct track *track)
|
|
{
|
|
if (!playlist || !track || playlist_has(playlist, track))
|
|
return false;
|
|
|
|
playlist->pl_length += track->tr_length;
|
|
if (playlist->pl_sort) {
|
|
g_queue_insert_sorted(&playlist->pl_tracks, track,
|
|
__playlist_generic_less_than, playlist);
|
|
} else
|
|
g_queue_push_tail(&playlist->pl_tracks, track);
|
|
|
|
if (callbacks)
|
|
callbacks->pl_cb_added(playlist, track);
|
|
return true;
|
|
}
|
|
|
|
bool playlist_generic_add_front(struct playlist *playlist, struct track *track)
|
|
{
|
|
if (!playlist || !track)
|
|
return false;
|
|
|
|
playlist->pl_length += track->tr_length;
|
|
g_queue_push_head(&playlist->pl_tracks, track);
|
|
if (callbacks)
|
|
callbacks->pl_cb_added(playlist, track);
|
|
return true;
|
|
}
|
|
|
|
bool playlist_generic_remove(struct playlist *playlist, struct track *track)
|
|
{
|
|
unsigned int count;
|
|
|
|
if (!playlist || !track)
|
|
return false;
|
|
|
|
while (playlist_current_track(playlist) == track)
|
|
playlist_current_previous(playlist);
|
|
|
|
count = g_queue_remove_all(&playlist->pl_tracks, track);
|
|
playlist->pl_length -= (count * track->tr_length);
|
|
|
|
if (callbacks)
|
|
callbacks->pl_cb_removed(playlist, track, count);
|
|
return count > 0;
|
|
}
|
|
|
|
void playlist_generic_update(struct playlist *playlist, struct track *track)
|
|
{
|
|
if (playlist && callbacks)
|
|
callbacks->pl_cb_updated(playlist, track);
|
|
}
|
|
|
|
void playlist_generic_set_random(struct playlist *playlist, bool enabled)
|
|
{
|
|
playlist->pl_random = enabled;
|
|
}
|
|
|
|
void playlist_generic_sort(struct playlist *playlist, enum compare_t field)
|
|
{
|
|
gpointer sort = GINT_TO_POINTER(field);
|
|
GSList *found = g_slist_find_custom(playlist->pl_sort, sort,
|
|
__playlist_generic_find_sort);
|
|
|
|
if (found)
|
|
found->data = GINT_TO_POINTER(-field);
|
|
else
|
|
playlist->pl_sort = g_slist_append(playlist->pl_sort, sort);
|
|
|
|
playlist_generic_resort(playlist);
|
|
}
|
|
|
|
void playlist_generic_resort(struct playlist *playlist)
|
|
{
|
|
if (!playlist || !playlist->pl_sort)
|
|
return;
|
|
|
|
g_queue_sort(&playlist->pl_tracks, __playlist_generic_less_than, playlist);
|
|
playlist_generic_update(playlist, NULL);
|
|
}
|
|
|
|
struct track *playlist_generic_next(struct playlist *playlist)
|
|
{
|
|
unsigned int pos, size = playlist_size(playlist);
|
|
|
|
if (size == 0)
|
|
return NULL;
|
|
else if (playlist->pl_random) {
|
|
pos = playlist_current_index(playlist);
|
|
pos += g_random_int_range(1, size);
|
|
playlist_current_set(playlist, pos % size);
|
|
} else if (!playlist_current_next(playlist))
|
|
playlist_current_set(playlist, 0);
|
|
|
|
return playlist_current_track(playlist);
|
|
}
|