/* * 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 . */ #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 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; irb_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; }