/* * Copyright 2013 (c) Anna Schumaker. */ #include #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 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 void __queue_remove(struct queue *queue, struct _q_iter *it) { unsigned int pos = it->it_pos; struct track *track = (struct track *)_q_remove_it(&queue->q_tracks, it); queue->q_length -= track->tr_length; if (queue->q_cur.it_pos == pos) _q_iter_prev(&queue->q_cur); 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) { struct track *track = (struct track *)_q_iter_val(&queue->q_cur); if (queue_has_flag(queue, Q_REPEAT) == false) __queue_remove(queue, &queue->q_cur); 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; queue->q_cur.it_pos = -1; queue->q_cur.it_iter = NULL; _q_init(&queue->q_tracks); } void queue_deinit(struct queue *queue) { queue_clear(queue); g_slist_free(queue->q_sort); queue->q_sort = NULL; } 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) { queue->q_flags &= ~flag; __queue_save(queue, Q_SAVE_FLAGS); } unsigned int queue_add(struct queue *queue, struct track *track) { unsigned int pos; if (queue_has_flag(queue, Q_ADD_FRONT)) pos = _q_add_head(&queue->q_tracks, track); else if (queue->q_sort) pos = _q_add_sorted(&queue->q_tracks, track, track_less_than, queue->q_sort); else pos = _q_add_tail(&queue->q_tracks, track); return __queue_added(queue, track, pos); } void queue_remove(struct queue *queue, unsigned int index) { struct _q_iter it; _q_iter_set(&queue->q_tracks, &it, index); __queue_remove(queue, &it); } void queue_remove_all(struct queue *queue, struct track *track) { struct _q_iter it; while (queue_at(queue, 0) == track) queue_remove(queue, 0); _q_for_each(&queue->q_tracks, &it) { if (_q_iter_val(&it) == track) __queue_remove(queue, &it); } } void queue_clear(struct queue *queue) { unsigned int n = queue_size(queue); _q_clear(&queue->q_tracks); __queue_clear(queue, n); } void queue_updated(struct queue *queue, struct track *track) { struct _q_iter it; _q_for_each(&queue->q_tracks, &it) { if (_q_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) _q_iter_set(&queue->q_tracks, &queue->q_cur, index); return __queue_selected(queue); } 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) _q_iter_set(&queue->q_tracks, &queue->q_cur, 0); else if (queue_has_flag(queue, Q_RANDOM)) { pos = random_range(1, (size < 15) ? size : size / 3); pos += queue->q_cur.it_pos; _q_iter_set(&queue->q_tracks, &queue->q_cur, pos % size); } else { _q_iter_next(&queue->q_cur); if (!queue->q_cur.it_iter) _q_iter_set(&queue->q_tracks, &queue->q_cur, 0); } return __queue_selected(queue); } void queue_resort(struct queue *queue) { _q_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); }