push
This commit is contained in:
448
track.c
Normal file
448
track.c
Normal file
@@ -0,0 +1,448 @@
|
||||
/*
|
||||
* Copyright 2008-2013 Various Authors
|
||||
* Copyright 2006 Timo Hirvonen
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "track.h"
|
||||
#include "lib.h"
|
||||
#include "iter.h"
|
||||
#include "search_mode.h"
|
||||
#include "window.h"
|
||||
#include "options.h"
|
||||
#include "uchar.h"
|
||||
#include "xmalloc.h"
|
||||
#include "debug.h"
|
||||
#include "misc.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
void simple_track_init(struct simple_track *track, struct track_info *ti)
|
||||
{
|
||||
track->info = ti;
|
||||
track->marked = 0;
|
||||
RB_CLEAR_NODE(&track->tree_node);
|
||||
}
|
||||
|
||||
struct simple_track *simple_track_new(struct track_info *ti)
|
||||
{
|
||||
struct simple_track *t = xnew(struct simple_track, 1);
|
||||
|
||||
track_info_ref(ti);
|
||||
simple_track_init(t, ti);
|
||||
return t;
|
||||
}
|
||||
|
||||
GENERIC_ITER_PREV(simple_track_get_prev, struct simple_track, node)
|
||||
GENERIC_ITER_NEXT(simple_track_get_next, struct simple_track, node)
|
||||
|
||||
int simple_track_search_get_current(void *data, struct iter *iter, enum search_direction dir)
|
||||
{
|
||||
return window_get_sel(data, iter);
|
||||
}
|
||||
|
||||
int _simple_track_search_matches(struct iter *iter, const char *text)
|
||||
{
|
||||
unsigned int flags = TI_MATCH_TITLE;
|
||||
struct simple_track *track = iter_to_simple_track(iter);
|
||||
|
||||
if (!search_restricted)
|
||||
flags |= TI_MATCH_ARTIST | TI_MATCH_ALBUM | TI_MATCH_ALBUMARTIST;
|
||||
|
||||
return track_info_matches(track->info, text, flags);
|
||||
}
|
||||
|
||||
int simple_track_search_matches(void *data, struct iter *iter, const char *text)
|
||||
{
|
||||
int rc = _simple_track_search_matches(iter, text);
|
||||
if (rc)
|
||||
window_set_sel(data, iter);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void shuffle_insert(struct rb_root *root, struct shuffle_info *previous, struct shuffle_info *next)
|
||||
{
|
||||
BUG_ON(root == NULL);
|
||||
BUG_ON(next == NULL);
|
||||
|
||||
if (previous == next)
|
||||
return;
|
||||
rb_erase(&next->tree_node, root);
|
||||
|
||||
struct rb_node *parent = previous ? &previous->tree_node : NULL;
|
||||
struct rb_node **new = parent ? &parent->rb_right : &root->rb_node;
|
||||
while (*new) {
|
||||
parent = *new;
|
||||
new = &(*new)->rb_left;
|
||||
}
|
||||
|
||||
rb_link_node(&next->tree_node, parent, new);
|
||||
rb_insert_color(&next->tree_node, root);
|
||||
}
|
||||
|
||||
struct shuffle_info *shuffle_list_get_next(struct rb_root *root, struct shuffle_info *cur,
|
||||
int (*filter_callback)(const struct album *))
|
||||
{
|
||||
struct rb_node *node;
|
||||
|
||||
if (!cur) {
|
||||
if (auto_reshuffle)
|
||||
shuffle_list_reshuffle(root);
|
||||
return tree_node_to_shuffle_info(rb_first(root));
|
||||
}
|
||||
|
||||
node = rb_next(&cur->tree_node);
|
||||
again:
|
||||
while (node) {
|
||||
struct shuffle_info *track = tree_node_to_shuffle_info(node);
|
||||
|
||||
if (filter_callback == NULL || filter_callback(track->album))
|
||||
return track;
|
||||
node = rb_next(node);
|
||||
}
|
||||
if (repeat) {
|
||||
if (auto_reshuffle)
|
||||
shuffle_list_reshuffle(root);
|
||||
node = rb_first(root);
|
||||
goto again;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct shuffle_info *shuffle_list_get_prev(struct rb_root *root, struct shuffle_info *cur,
|
||||
int (*filter_callback)(const struct album *))
|
||||
{
|
||||
struct rb_node *node;
|
||||
|
||||
if (!cur) {
|
||||
if (auto_reshuffle)
|
||||
shuffle_list_reshuffle(root);
|
||||
return tree_node_to_shuffle_info(rb_last(root));
|
||||
}
|
||||
|
||||
node = rb_prev(&cur->tree_node);
|
||||
again:
|
||||
while (node) {
|
||||
struct shuffle_info *track = tree_node_to_shuffle_info(node);
|
||||
|
||||
if (filter_callback == NULL || filter_callback(track->album))
|
||||
return track;
|
||||
node = rb_prev(node);
|
||||
}
|
||||
if (repeat) {
|
||||
if (auto_reshuffle)
|
||||
shuffle_list_reshuffle(root);
|
||||
node = rb_last(root);
|
||||
goto again;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct simple_track *simple_list_get_next(struct list_head *head, struct simple_track *cur,
|
||||
int (*filter_callback)(const struct album *), bool allow_repeat)
|
||||
{
|
||||
struct list_head *item;
|
||||
|
||||
if (cur == NULL) {
|
||||
if (!allow_repeat)
|
||||
return NULL;
|
||||
return to_simple_track(head->next);
|
||||
}
|
||||
|
||||
item = cur->node.next;
|
||||
again:
|
||||
while (item != head) {
|
||||
struct simple_track *track = to_simple_track(item);
|
||||
|
||||
if (filter_callback == NULL || filter_callback(((struct tree_track *)track)->album))
|
||||
return track;
|
||||
item = item->next;
|
||||
}
|
||||
item = head->next;
|
||||
if (allow_repeat && repeat)
|
||||
goto again;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct simple_track *simple_list_get_prev(struct list_head *head, struct simple_track *cur,
|
||||
int (*filter_callback)(const struct album *), bool allow_repeat)
|
||||
{
|
||||
struct list_head *item;
|
||||
|
||||
if (cur == NULL) {
|
||||
if (!allow_repeat)
|
||||
return NULL;
|
||||
return to_simple_track(head->prev);
|
||||
}
|
||||
|
||||
item = cur->node.prev;
|
||||
again:
|
||||
while (item != head) {
|
||||
struct simple_track *track = to_simple_track(item);
|
||||
|
||||
if (filter_callback == NULL || filter_callback(((struct tree_track *)track)->album))
|
||||
return track;
|
||||
item = item->prev;
|
||||
}
|
||||
item = head->prev;
|
||||
if (allow_repeat && repeat)
|
||||
goto again;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void sorted_list_add_track(struct list_head *head, struct rb_root *tree_root, struct simple_track *track,
|
||||
const sort_key_t *keys, int tiebreak)
|
||||
{
|
||||
struct rb_node **new = &(tree_root->rb_node), *parent = NULL, *curr, *next;
|
||||
struct list_head *node;
|
||||
int result = 0;
|
||||
|
||||
/* try to locate track in tree */
|
||||
while (*new) {
|
||||
const struct simple_track *t = tree_node_to_simple_track(*new);
|
||||
result = track_info_cmp(track->info, t->info, keys);
|
||||
|
||||
parent = *new;
|
||||
if (result < 0)
|
||||
new = &(parent->rb_left);
|
||||
else if (result > 0)
|
||||
new = &(parent->rb_right);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
/* duplicate is present in the tree */
|
||||
if (parent && result == 0) {
|
||||
if (tiebreak < 0) {
|
||||
node = &(tree_node_to_simple_track(parent)->node);
|
||||
rb_replace_node(parent, &track->tree_node, tree_root);
|
||||
RB_CLEAR_NODE(parent);
|
||||
} else {
|
||||
next = rb_next(parent);
|
||||
node = next ? &(tree_node_to_simple_track(next)->node) : head;
|
||||
}
|
||||
} else {
|
||||
rb_link_node(&track->tree_node, parent, new);
|
||||
curr = *new;
|
||||
rb_insert_color(&track->tree_node, tree_root);
|
||||
if (result < 0) {
|
||||
node = &(tree_node_to_simple_track(parent)->node);
|
||||
} else if (result > 0) {
|
||||
next = rb_next(curr);
|
||||
node = next ? &(tree_node_to_simple_track(next)->node) : head;
|
||||
} else {
|
||||
/* rbtree was empty, just add after list head */
|
||||
node = head;
|
||||
}
|
||||
}
|
||||
list_add(&track->node, node->prev);
|
||||
}
|
||||
|
||||
void sorted_list_remove_track(struct list_head *head, struct rb_root *tree_root, struct simple_track *track)
|
||||
{
|
||||
struct simple_track *next_track;
|
||||
struct rb_node *tree_next;
|
||||
|
||||
if (!RB_EMPTY_NODE(&track->tree_node)) {
|
||||
next_track = (track->node.next != head) ? to_simple_track(track->node.next) : NULL;
|
||||
tree_next = rb_next(&track->tree_node);
|
||||
|
||||
if (next_track && (!tree_next || tree_node_to_simple_track(tree_next) != next_track)) {
|
||||
rb_replace_node(&track->tree_node, &next_track->tree_node, tree_root);
|
||||
RB_CLEAR_NODE(&track->tree_node);
|
||||
} else
|
||||
rb_erase(&track->tree_node, tree_root);
|
||||
}
|
||||
list_del(&track->node);
|
||||
}
|
||||
|
||||
void rand_list_rebuild(struct list_head *head, struct rb_root *tree_root)
|
||||
{
|
||||
struct list_head *item, *tmp;
|
||||
struct rb_root tmp_tree = RB_ROOT;
|
||||
struct simple_track **track_array;
|
||||
static const sort_key_t empty_sort_keys[] = { SORT_INVALID };
|
||||
|
||||
unsigned int len = 0, track_cnt = 0;
|
||||
|
||||
list_for_each(item, head) {
|
||||
len++;
|
||||
}
|
||||
track_array = xmalloc(len * sizeof(track_array[0]));
|
||||
|
||||
LIST_HEAD(tmp_head);
|
||||
list_for_each_safe(item, tmp, head) {
|
||||
struct simple_track *track = to_simple_track(item);
|
||||
sorted_list_remove_track(head, tree_root, track);
|
||||
track_array[track_cnt] = track;
|
||||
track_cnt++;
|
||||
}
|
||||
shuffle_array(track_array, len, sizeof(track_array[0]));
|
||||
for (unsigned int i=0; i<len; i++) {
|
||||
sorted_list_add_track(&tmp_head, &tmp_tree, track_array[i], empty_sort_keys, 0);
|
||||
}
|
||||
free(track_array);
|
||||
|
||||
tree_root->rb_node = tmp_tree.rb_node;
|
||||
_list_add(head, tmp_head.prev, tmp_head.next);
|
||||
}
|
||||
|
||||
void sorted_list_rebuild(struct list_head *head, struct rb_root *tree_root, const sort_key_t *keys)
|
||||
{
|
||||
struct list_head *item, *tmp;
|
||||
struct rb_root tmp_tree = RB_ROOT;
|
||||
LIST_HEAD(tmp_head);
|
||||
|
||||
list_for_each_safe(item, tmp, head) {
|
||||
struct simple_track *track = to_simple_track(item);
|
||||
sorted_list_remove_track(head, tree_root, track);
|
||||
sorted_list_add_track(&tmp_head, &tmp_tree, track, keys, 0);
|
||||
}
|
||||
tree_root->rb_node = tmp_tree.rb_node;
|
||||
_list_add(head, tmp_head.prev, tmp_head.next);
|
||||
}
|
||||
|
||||
static int compare_rand(const struct rb_node *a, const struct rb_node *b)
|
||||
{
|
||||
struct shuffle_info *tr_a = tree_node_to_shuffle_info(a);
|
||||
struct shuffle_info *tr_b = tree_node_to_shuffle_info(b);
|
||||
|
||||
if (tr_a->rand < tr_b->rand)
|
||||
return -1;
|
||||
if (tr_a->rand > tr_b->rand)
|
||||
return +1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void shuffle_info_init(struct shuffle_info *info, struct album *album)
|
||||
{
|
||||
info->rand = rand() / ((double) RAND_MAX + 1);
|
||||
info->album = album;
|
||||
}
|
||||
|
||||
void shuffle_list_add(struct shuffle_info *track, struct rb_root *tree_root, struct album *album)
|
||||
{
|
||||
struct rb_node **new = &(tree_root->rb_node), *parent = NULL;
|
||||
|
||||
shuffle_info_init(track, album);
|
||||
|
||||
/* try to locate track in tree */
|
||||
while (*new) {
|
||||
int result = compare_rand(&track->tree_node, *new);
|
||||
|
||||
parent = *new;
|
||||
if (result < 0)
|
||||
new = &((*new)->rb_left);
|
||||
else if (result > 0)
|
||||
new = &((*new)->rb_right);
|
||||
else {
|
||||
/* very unlikely, try again! */
|
||||
shuffle_list_add(track, tree_root, album);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
rb_link_node(&track->tree_node, parent, new);
|
||||
rb_insert_color(&track->tree_node, tree_root);
|
||||
}
|
||||
|
||||
void shuffle_list_reshuffle(struct rb_root *tree_root)
|
||||
{
|
||||
struct rb_node *node, *tmp;
|
||||
struct rb_root tmptree = RB_ROOT;
|
||||
|
||||
rb_for_each_safe(node, tmp, tree_root) {
|
||||
struct shuffle_info *track = tree_node_to_shuffle_info(node);
|
||||
struct album *album = track->album;
|
||||
rb_erase(node, tree_root);
|
||||
shuffle_list_add(track, &tmptree, album);
|
||||
}
|
||||
|
||||
tree_root->rb_node = tmptree.rb_node;
|
||||
}
|
||||
|
||||
/* expensive */
|
||||
void list_add_rand(struct list_head *head, struct list_head *node, int nr)
|
||||
{
|
||||
struct list_head *item;
|
||||
int pos;
|
||||
|
||||
pos = rand() % (nr + 1);
|
||||
item = head;
|
||||
if (pos <= nr / 2) {
|
||||
while (pos) {
|
||||
item = item->next;
|
||||
pos--;
|
||||
}
|
||||
/* add after item */
|
||||
list_add(node, item);
|
||||
} else {
|
||||
pos = nr - pos;
|
||||
while (pos) {
|
||||
item = item->prev;
|
||||
pos--;
|
||||
}
|
||||
/* add before item */
|
||||
list_add_tail(node, item);
|
||||
}
|
||||
}
|
||||
|
||||
int simple_list_for_each_marked(struct list_head *head, track_info_cb cb,
|
||||
void *data, int reverse)
|
||||
{
|
||||
struct simple_track *t;
|
||||
int rc = 0;
|
||||
|
||||
if (reverse) {
|
||||
list_for_each_entry_reverse(t, head, node) {
|
||||
if (t->marked) {
|
||||
rc = cb(data, t->info);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
list_for_each_entry(t, head, node) {
|
||||
if (t->marked) {
|
||||
rc = cb(data, t->info);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int simple_list_for_each(struct list_head *head, track_info_cb cb, void *data,
|
||||
int reverse)
|
||||
{
|
||||
struct simple_track *t;
|
||||
int rc = 0;
|
||||
|
||||
if (reverse) {
|
||||
list_for_each_entry_reverse(t, head, node) {
|
||||
if ((rc = cb(data, t->info)))
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
list_for_each_entry(t, head, node) {
|
||||
if ((rc = cb(data, t->info)))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
Reference in New Issue
Block a user