/* * Copyright 2013 (c) Anna Schumaker. */ #include #include #include 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, void *data) { if (queue->q_ops) return queue->q_ops->qop_init(queue, data); 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 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; } void queue_init(struct queue *queue, unsigned int flags, const struct queue_ops *ops, void *data) { 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, data); } 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_flags(struct queue *queue, struct file *file, bool iter) { GSList *cur = queue->q_sort; int field; if (iter) file_writef(file, "%u ", queue->q_cur.it_pos); file_writef(file, "%u %u", queue->q_flags, g_slist_length(cur)); while (cur) { field = GPOINTER_TO_INT(cur->data); file_writef(file, " %u %d", abs(field) - 1, field > 0); cur = g_slist_next(cur); } file_writef(file, "\n"); } 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", track_index(queue_iter_val(&it))); file_writef(file, "\n"); } void queue_load_flags(struct queue *queue, struct file *file, bool iter) { unsigned int flags, n, i; int field, ascending; unsigned int it = 0; gchar *line; if (iter) file_readf(file, "%u", &it); file_readf(file, "%u %u", &flags, &n); for (i = 0; i < n; i++) { file_readf(file, "%u %d", &field, &ascending); queue_sort(queue, field + 1, (i == 0) ? true : false); if (ascending == false) queue_sort(queue, field + 1, false); } flags &= Q_UNUSED_MASK; queue->q_flags |= flags; queue->q_cur.it_pos = it; if (file_readf(file, "%m\n", &line)) g_free(line); } void queue_load_tracks(struct queue *queue, struct file *file) { unsigned int i, n, t; gchar *line; file_readf(file, "%u ", &n); for (i = 0; i < n; i++) { file_readf(file, "%u", &t); queue_add(queue, track_get(t)); } if (file_readf(file, "%m\n", &line)) g_free(line); } void queue_set_flag(struct queue *queue, enum queue_flags flag) { queue->q_flags |= flag; } void queue_unset_flag(struct queue *queue, enum queue_flags flag) { if (!queue_has_flag(queue, flag)) return; queue->q_flags &= ~flag; } unsigned int queue_add(struct queue *queue, struct track *track) { if (queue->q_sort) return __queue_add_sorted(queue, track); return __queue_add_tail(queue, track); } unsigned int queue_add_front(struct queue *queue, struct track *track) { return __queue_add_head(queue, track); } 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 (size == 0) return NULL; else 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); queue_iter_set(queue, &queue->q_cur, queue->q_cur.it_pos); 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 (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); }