ocarina/core/queue.c
Anna Schumaker c845530812 core/queue: Resort the queue when unsetting Q_ADD_FRONT
This flag is used for bulk inserts to cut down on the number of gui
operations during startup.  Let's just make it standard that unsetting
the flag causes the queue to resort itself.

Signed-off-by: Anna Schumaker <Anna@OcarinaProject.net>
2016-07-19 08:13:10 -04:00

337 lines
7.3 KiB
C

/*
* Copyright 2013 (c) Anna Schumaker.
*/
#include <core/queue.h>
#include <core/string.h>
#include <stdlib.h>
static int track_less_than(const void *a, const void *b, void *data)
{
struct track *lhs = (struct track *)a;
struct track *rhs = (struct track *)b;
GSList *cur = (GSList *)data;
int res, field;
while (cur) {
field = GPOINTER_TO_INT(cur->data);
if (field > 0)
res = track_compare(lhs, rhs, field);
else
res = track_compare(rhs, lhs, abs(field));
if (res == 0) {
cur = g_slist_next(cur);
continue;
}
break;
};
return res;
}
static inline void *__queue_init(struct queue *queue)
{
if (queue->q_ops)
return queue->q_ops->qop_init(queue);
return NULL;
}
static inline void __queue_deinit(struct queue *queue)
{
if (queue->q_ops)
queue->q_ops->qop_deinit(queue);
}
static inline unsigned int __queue_added(struct queue *queue,
struct track *track,
unsigned int pos)
{
queue->q_length += track->tr_length;
if (queue->q_ops)
queue->q_ops->qop_added(queue, pos);
return pos;
}
static inline unsigned int __queue_add_head(struct queue *queue,
struct track *track)
{
g_queue_push_head(&queue->q_tracks, track);
return __queue_added(queue, track, 0);
}
static inline unsigned int __queue_add_tail(struct queue *queue,
struct track *track)
{
g_queue_push_tail(&queue->q_tracks, track);
return __queue_added(queue, track, queue_size(queue) - 1);
}
static inline unsigned int __queue_add_sorted(struct queue *queue,
struct track *track)
{
struct queue_iter it;
queue_for_each(queue, &it) {
if (track_less_than(queue_iter_val(&it), track, queue->q_sort) > 0) {
g_queue_insert_before(&queue->q_tracks, it.it_iter, track);
return __queue_added(queue, track, it.it_pos);
}
}
return __queue_add_tail(queue, track);
}
static inline bool __queue_erase(struct queue *queue, struct queue_iter *it)
{
struct track *track = queue_iter_val(it);
if (queue->q_ops)
return queue->q_ops->qop_erase(queue, track);
return true;
}
static inline void __queue_remove(struct queue *queue, struct queue_iter *it)
{
struct track *track = queue_iter_val(it);
unsigned int pos = it->it_pos;
GList *link = it->it_iter;
queue_iter_prev(it);
g_queue_delete_link(&queue->q_tracks, link);
queue->q_length -= track->tr_length;
if (queue->q_ops)
queue->q_ops->qop_removed(queue, pos);
}
static inline void __queue_clear(struct queue *queue, unsigned int n)
{
queue->q_length = 0;
if (queue->q_ops)
queue->q_ops->qop_cleared(queue, n);
}
static inline void __queue_updated(struct queue *queue, unsigned int pos)
{
if (queue->q_ops)
queue->q_ops->qop_updated(queue, pos);
}
static inline struct track *__queue_selected(struct queue *queue, unsigned int pos)
{
struct track *track = queue_iter_val(&queue->q_cur);
if (queue_has_flag(queue, Q_REPEAT) == false)
__queue_remove(queue, &queue->q_cur);
else
__queue_updated(queue, pos);
return track;
}
static inline void __queue_save(struct queue *queue, enum queue_flags flag)
{
if (queue->q_ops && queue_has_flag(queue, flag))
queue->q_ops->qop_save(queue, flag);
}
void queue_init(struct queue *queue, unsigned int flags,
const struct queue_ops *ops)
{
queue->q_flags = flags;
queue->q_length = 0;
queue->q_sort = NULL;
queue->q_ops = ops;
g_queue_init(&queue->q_tracks);
queue_iter_init(queue, &queue->q_cur);
queue->q_private = __queue_init(queue);
}
void queue_deinit(struct queue *queue)
{
queue_clear(queue);
__queue_deinit(queue);
g_slist_free(queue->q_sort);
queue->q_sort = NULL;
}
void queue_save_tracks(struct queue *queue, struct file *file)
{
struct queue_iter it;
file_writef(file, "%u", queue_size(queue));
queue_for_each(queue, &it)
file_writef(file, " %u", queue_iter_val(&it)->tr_dbe.dbe_index);
}
void queue_load_tracks(struct queue *queue, struct file *file)
{
unsigned int i, n, t;
file_readf(file, "%u ", &n);
for (i = 0; i < n; i++) {
file_readf(file, "%u", &t);
queue_add(queue, track_get(t));
}
}
void queue_set_flag(struct queue *queue, enum queue_flags flag)
{
queue->q_flags |= flag;
__queue_save(queue, Q_SAVE_FLAGS);
}
void queue_unset_flag(struct queue *queue, enum queue_flags flag)
{
if (!queue_has_flag(queue, flag))
return;
queue->q_flags &= ~flag;
if (flag == Q_ADD_FRONT)
queue_resort(queue);
__queue_save(queue, Q_SAVE_FLAGS);
}
unsigned int queue_add(struct queue *queue, struct track *track)
{
if (queue_has_flag(queue, Q_ADD_FRONT))
return __queue_add_head(queue, track);
if (queue->q_sort)
return __queue_add_sorted(queue, track);
return __queue_add_tail(queue, track);
}
void queue_erase(struct queue *queue, unsigned int index)
{
struct queue_iter it;
queue_iter_set(queue, &it, index);
if (__queue_erase(queue, &it))
__queue_remove(queue, &it);
}
void queue_remove(struct queue *queue, unsigned int index)
{
struct queue_iter it;
queue_iter_set(queue, &it, index);
__queue_remove(queue, &it);
}
unsigned int queue_remove_all(struct queue *queue, struct track *track)
{
unsigned int count = 0;
struct queue_iter it;
while (queue_at(queue, 0) == track) {
queue_remove(queue, 0);
count++;
}
queue_for_each(queue, &it) {
if (queue_iter_val(&it) == track) {
__queue_remove(queue, &it);
count++;
}
}
return count;
}
void queue_clear(struct queue *queue)
{
unsigned int n = queue_size(queue);
g_queue_clear(&queue->q_tracks);
__queue_clear(queue, n);
}
bool queue_has(struct queue *queue, struct track *track)
{
struct queue_iter it;
queue_for_each(queue, &it) {
if (queue_iter_val(&it) == track)
return true;
}
return false;
}
void queue_updated(struct queue *queue, struct track *track)
{
struct queue_iter it;
queue_for_each(queue, &it) {
if (queue_iter_val(&it) == track)
__queue_updated(queue, it.it_pos);
}
}
struct track *queue_selected(struct queue *queue, unsigned int index)
{
if (index > queue_size(queue))
return NULL;
if (queue->q_cur.it_pos != index)
queue_iter_set(queue, &queue->q_cur, index);
return __queue_selected(queue, index);
}
struct track *queue_next(struct queue *queue)
{
unsigned int pos, size = queue_size(queue);
if (!queue_has_flag(queue, Q_ENABLED))
return NULL;
else if (size == 0)
return NULL;
if (size == 1)
queue_iter_set(queue, &queue->q_cur, 0);
else if (queue_has_flag(queue, Q_RANDOM)) {
pos = g_random_int_range(1, (size < 15) ? size : size / 3);
pos += queue->q_cur.it_pos;
queue_iter_set(queue, &queue->q_cur, pos % size);
} else {
queue_iter_next(&queue->q_cur);
if (!queue->q_cur.it_iter)
queue_iter_set(queue, &queue->q_cur, 0);
}
return __queue_selected(queue, queue->q_cur.it_pos);
}
void queue_resort(struct queue *queue)
{
g_queue_sort(&queue->q_tracks, track_less_than, queue->q_sort);
for (unsigned int i = 0; i < queue_size(queue); i++)
__queue_updated(queue, i);
}
void queue_sort(struct queue *queue, enum compare_t sort, bool reset)
{
GSList *cur = NULL;
int field;
if (queue_has_flag(queue, Q_NO_SORT))
return;
if (reset) {
g_slist_free(queue->q_sort);
queue->q_sort = NULL;
}
cur = queue->q_sort;
while (cur) {
field = GPOINTER_TO_INT(cur->data);
if (abs(field) == sort) {
cur->data = GINT_TO_POINTER(-field);
goto out_sort;
}
cur = g_slist_next(cur);
}
queue->q_sort = g_slist_append(queue->q_sort, GINT_TO_POINTER(sort));
out_sort:
queue_resort(queue);
__queue_save(queue, Q_SAVE_SORT);
}