push
This commit is contained in:
928
lib.c
Normal file
928
lib.c
Normal file
@@ -0,0 +1,928 @@
|
||||
/*
|
||||
* Copyright 2008-2013 Various Authors
|
||||
* Copyright 2004-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 "lib.h"
|
||||
#include "editable.h"
|
||||
#include "track_info.h"
|
||||
#include "options.h"
|
||||
#include "xmalloc.h"
|
||||
#include "rbtree.h"
|
||||
#include "debug.h"
|
||||
#include "utils.h"
|
||||
#include "u_collate.h"
|
||||
#include "ui_curses.h" /* cur_view */
|
||||
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
|
||||
struct editable lib_editable;
|
||||
static struct editable_shared lib_editable_shared;
|
||||
|
||||
struct tree_track *lib_cur_track = NULL;
|
||||
unsigned int play_sorted = 0;
|
||||
enum aaa_mode aaa_mode = AAA_MODE_ALL;
|
||||
/* used in ui_curses.c for status display */
|
||||
char *lib_live_filter = NULL;
|
||||
|
||||
struct rb_root lib_shuffle_root;
|
||||
struct rb_root lib_album_shuffle_root;
|
||||
static struct expr *filter = NULL;
|
||||
static struct expr *add_filter = NULL;
|
||||
static int remove_from_hash = 1;
|
||||
|
||||
static struct expr *live_filter_expr = NULL;
|
||||
static struct track_info *cur_track_ti = NULL;
|
||||
static struct track_info *sel_track_ti = NULL;
|
||||
|
||||
const char *artist_sort_name(const struct artist *a)
|
||||
{
|
||||
if (a->sort_name)
|
||||
return a->sort_name;
|
||||
|
||||
if (smart_artist_sort && a->auto_sort_name)
|
||||
return a->auto_sort_name;
|
||||
|
||||
return a->name;
|
||||
}
|
||||
|
||||
static inline void sorted_track_to_iter(struct tree_track *track, struct iter *iter)
|
||||
{
|
||||
iter->data0 = &lib_editable.head;
|
||||
iter->data1 = track;
|
||||
iter->data2 = NULL;
|
||||
}
|
||||
|
||||
static void all_wins_changed(void)
|
||||
{
|
||||
lib_tree_win->changed = 1;
|
||||
lib_track_win->changed = 1;
|
||||
lib_editable.shared->win->changed = 1;
|
||||
}
|
||||
|
||||
static void shuffle_add(struct tree_track *track)
|
||||
{
|
||||
shuffle_list_add(&track->simple_track.shuffle_info, &lib_shuffle_root, track->album);
|
||||
}
|
||||
|
||||
static void album_shuffle_list_add(struct album *album)
|
||||
{
|
||||
shuffle_list_add(&album->shuffle_info, &lib_album_shuffle_root, album);
|
||||
}
|
||||
|
||||
static void album_shuffle_list_remove(struct album *album)
|
||||
{
|
||||
rb_erase(&album->shuffle_info.tree_node, &lib_album_shuffle_root);
|
||||
}
|
||||
|
||||
static void views_add_track(struct track_info *ti)
|
||||
{
|
||||
struct tree_track *track = xnew(struct tree_track, 1);
|
||||
|
||||
/* NOTE: does not ref ti */
|
||||
simple_track_init((struct simple_track *)track, ti);
|
||||
|
||||
/* both the hash table and views have refs */
|
||||
track_info_ref(ti);
|
||||
|
||||
tree_add_track(track, album_shuffle_list_add);
|
||||
shuffle_add(track);
|
||||
editable_add(&lib_editable, (struct simple_track *)track);
|
||||
}
|
||||
|
||||
struct fh_entry {
|
||||
struct fh_entry *next;
|
||||
|
||||
/* ref count is increased when added to this hash */
|
||||
struct track_info *ti;
|
||||
};
|
||||
|
||||
#define FH_SIZE (1024)
|
||||
static struct fh_entry *ti_hash[FH_SIZE] = { NULL, };
|
||||
|
||||
static int hash_insert(struct track_info *ti)
|
||||
{
|
||||
const char *filename = ti->filename;
|
||||
unsigned int pos = hash_str(filename) % FH_SIZE;
|
||||
struct fh_entry **entryp;
|
||||
struct fh_entry *e;
|
||||
|
||||
entryp = &ti_hash[pos];
|
||||
e = *entryp;
|
||||
while (e) {
|
||||
if (strcmp(e->ti->filename, filename) == 0) {
|
||||
/* found, don't insert */
|
||||
return 0;
|
||||
}
|
||||
e = e->next;
|
||||
}
|
||||
|
||||
e = xnew(struct fh_entry, 1);
|
||||
track_info_ref(ti);
|
||||
e->ti = ti;
|
||||
e->next = *entryp;
|
||||
*entryp = e;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void hash_remove(struct track_info *ti)
|
||||
{
|
||||
const char *filename = ti->filename;
|
||||
unsigned int pos = hash_str(filename) % FH_SIZE;
|
||||
struct fh_entry **entryp;
|
||||
|
||||
entryp = &ti_hash[pos];
|
||||
while (1) {
|
||||
struct fh_entry *e = *entryp;
|
||||
|
||||
BUG_ON(e == NULL);
|
||||
if (strcmp(e->ti->filename, filename) == 0) {
|
||||
*entryp = e->next;
|
||||
track_info_unref(e->ti);
|
||||
free(e);
|
||||
break;
|
||||
}
|
||||
entryp = &e->next;
|
||||
}
|
||||
}
|
||||
|
||||
static int is_filtered(struct track_info *ti)
|
||||
{
|
||||
if (live_filter_expr && !expr_eval(live_filter_expr, ti))
|
||||
return 1;
|
||||
if (!live_filter_expr && lib_live_filter && !track_info_matches(ti, lib_live_filter, TI_MATCH_ALL))
|
||||
return 1;
|
||||
if (filter && !expr_eval(filter, ti))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool track_exists(struct track_info *ti)
|
||||
{
|
||||
struct rb_node *node;
|
||||
struct artist *artist;
|
||||
struct album *album;
|
||||
struct tree_track *track;
|
||||
|
||||
if (!ti->collkey_title)
|
||||
return false;
|
||||
|
||||
char *artist_collkey_name = u_strcasecoll_key(tree_artist_name(ti));
|
||||
rb_for_each_entry(artist, node, &lib_artist_root, tree_node) {
|
||||
if (strcmp(artist->collkey_name, artist_collkey_name) == 0)
|
||||
break;
|
||||
}
|
||||
free(artist_collkey_name);
|
||||
|
||||
if (!artist)
|
||||
return false;
|
||||
|
||||
char *album_collkey_name = u_strcasecoll_key(tree_album_name(ti));
|
||||
rb_for_each_entry(album, node, &artist->album_root, tree_node) {
|
||||
if (strcmp(album->collkey_name, album_collkey_name) == 0)
|
||||
break;
|
||||
}
|
||||
free(album_collkey_name);
|
||||
|
||||
if (!album)
|
||||
return false;
|
||||
|
||||
rb_for_each_entry(track, node, &album->track_root, tree_node) {
|
||||
struct track_info *iter_ti = tree_track_info(track);
|
||||
if (iter_ti->tracknumber == ti->tracknumber
|
||||
&& iter_ti->discnumber == ti->discnumber
|
||||
&& iter_ti->collkey_title && strcmp(iter_ti->collkey_title, ti->collkey_title) == 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void lib_add_track(struct track_info *ti, void *opaque)
|
||||
{
|
||||
if (!ti)
|
||||
return;
|
||||
|
||||
if (add_filter && !expr_eval(add_filter, ti)) {
|
||||
/* filter any files excluded by lib_add_filter */
|
||||
return;
|
||||
}
|
||||
|
||||
if (ignore_duplicates && track_exists(ti))
|
||||
return;
|
||||
|
||||
if (!hash_insert(ti)) {
|
||||
/* duplicate files not allowed */
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_filtered(ti))
|
||||
views_add_track(ti);
|
||||
}
|
||||
|
||||
static struct tree_track *album_first_track(const struct album *album)
|
||||
{
|
||||
return to_tree_track(rb_first(&album->track_root));
|
||||
}
|
||||
|
||||
static struct tree_track *artist_first_track(const struct artist *artist)
|
||||
{
|
||||
return album_first_track(to_album(rb_first(&artist->album_root)));
|
||||
}
|
||||
|
||||
static struct tree_track *normal_get_first(void)
|
||||
{
|
||||
return artist_first_track(to_artist(rb_first(&lib_artist_root)));
|
||||
}
|
||||
|
||||
static struct tree_track *album_last_track(const struct album *album)
|
||||
{
|
||||
return to_tree_track(rb_last(&album->track_root));
|
||||
}
|
||||
|
||||
static struct tree_track *artist_last_track(const struct artist *artist)
|
||||
{
|
||||
return album_last_track(to_album(rb_last(&artist->album_root)));
|
||||
}
|
||||
|
||||
static struct tree_track *normal_get_last(void)
|
||||
{
|
||||
return artist_last_track(to_artist(rb_last(&lib_artist_root)));
|
||||
}
|
||||
|
||||
static int aaa_mode_filter(const struct album *album)
|
||||
{
|
||||
if (aaa_mode == AAA_MODE_ALBUM)
|
||||
return CUR_ALBUM == album;
|
||||
|
||||
if (aaa_mode == AAA_MODE_ARTIST)
|
||||
return CUR_ARTIST == album->artist;
|
||||
|
||||
/* AAA_MODE_ALL */
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int cur_album_filter(const struct album *album)
|
||||
{
|
||||
return CUR_ALBUM == album;
|
||||
}
|
||||
|
||||
/* set next/prev (tree) {{{ */
|
||||
|
||||
static struct tree_track *normal_get_next(enum aaa_mode aaa, bool allow_repeat, bool skip_album)
|
||||
{
|
||||
if (lib_cur_track == NULL) {
|
||||
if (!allow_repeat)
|
||||
return NULL;
|
||||
return normal_get_first();
|
||||
}
|
||||
|
||||
/* not last track of the album? */
|
||||
if (!skip_album && rb_next(&lib_cur_track->tree_node)) {
|
||||
/* next track of the current album */
|
||||
return to_tree_track(rb_next(&lib_cur_track->tree_node));
|
||||
}
|
||||
|
||||
if (aaa == AAA_MODE_ALBUM) {
|
||||
if (!allow_repeat || !repeat)
|
||||
return NULL;
|
||||
/* first track of the current album */
|
||||
return album_first_track(CUR_ALBUM);
|
||||
}
|
||||
|
||||
/* not last album of the artist? */
|
||||
if (rb_next(&CUR_ALBUM->tree_node) != NULL) {
|
||||
/* first track of the next album */
|
||||
return album_first_track(to_album(rb_next(&CUR_ALBUM->tree_node)));
|
||||
}
|
||||
|
||||
if (aaa == AAA_MODE_ARTIST) {
|
||||
if (!allow_repeat || !repeat)
|
||||
return NULL;
|
||||
/* first track of the first album of the current artist */
|
||||
return artist_first_track(CUR_ARTIST);
|
||||
}
|
||||
|
||||
/* not last artist of the library? */
|
||||
if (rb_next(&CUR_ARTIST->tree_node) != NULL) {
|
||||
/* first track of the next artist */
|
||||
return artist_first_track(to_artist(rb_next(&CUR_ARTIST->tree_node)));
|
||||
}
|
||||
|
||||
if (!allow_repeat || !repeat)
|
||||
return NULL;
|
||||
|
||||
/* first track */
|
||||
return normal_get_first();
|
||||
}
|
||||
|
||||
static struct tree_track *normal_get_prev(enum aaa_mode aaa, bool allow_repeat, bool skip_album)
|
||||
{
|
||||
if (lib_cur_track == NULL) {
|
||||
if (!allow_repeat)
|
||||
return NULL;
|
||||
return normal_get_last();
|
||||
}
|
||||
|
||||
/* not first track of the album? */
|
||||
if (!skip_album && rb_prev(&lib_cur_track->tree_node)) {
|
||||
/* prev track of the album */
|
||||
return to_tree_track(rb_prev(&lib_cur_track->tree_node));
|
||||
}
|
||||
|
||||
if (aaa == AAA_MODE_ALBUM) {
|
||||
if (!allow_repeat || !repeat)
|
||||
return NULL;
|
||||
/* last track of the album */
|
||||
return album_last_track(CUR_ALBUM);
|
||||
}
|
||||
|
||||
/* not first album of the artist? */
|
||||
if (rb_prev(&CUR_ALBUM->tree_node) != NULL) {
|
||||
/* last track of the prev album of the artist */
|
||||
return album_last_track(to_album(rb_prev(&CUR_ALBUM->tree_node)));
|
||||
}
|
||||
|
||||
if (aaa == AAA_MODE_ARTIST) {
|
||||
if (!allow_repeat || !repeat)
|
||||
return NULL;
|
||||
/* last track of the last album of the artist */
|
||||
return album_last_track(to_album(rb_last(&CUR_ARTIST->album_root)));
|
||||
}
|
||||
|
||||
/* not first artist of the library? */
|
||||
if (rb_prev(&CUR_ARTIST->tree_node) != NULL) {
|
||||
/* last track of the last album of the prev artist */
|
||||
return artist_last_track(to_artist(rb_prev(&CUR_ARTIST->tree_node)));
|
||||
}
|
||||
|
||||
if (!allow_repeat || !repeat)
|
||||
return NULL;
|
||||
|
||||
/* last track */
|
||||
return normal_get_last();
|
||||
}
|
||||
|
||||
static struct tree_track *shuffle_album_get_next(void)
|
||||
{
|
||||
struct shuffle_info *shuffle_info = NULL;
|
||||
struct album *album;
|
||||
|
||||
if (lib_cur_track != NULL)
|
||||
shuffle_info = &lib_cur_track->album->shuffle_info;
|
||||
album = (struct album *)shuffle_list_get_next(&lib_album_shuffle_root,
|
||||
shuffle_info, aaa_mode_filter);
|
||||
if (album != NULL)
|
||||
return album_first_track(album);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct tree_track *shuffle_album_get_prev(void)
|
||||
{
|
||||
struct shuffle_info *shuffle_info = NULL;
|
||||
struct album *album;
|
||||
|
||||
if (lib_cur_track != NULL)
|
||||
shuffle_info = &lib_cur_track->album->shuffle_info;
|
||||
album = (struct album *)shuffle_list_get_prev(&lib_album_shuffle_root,
|
||||
shuffle_info, aaa_mode_filter);
|
||||
if (album != NULL)
|
||||
return album_last_track(album);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct tree_track *sorted_album_first_track(struct tree_track *track)
|
||||
{
|
||||
struct tree_track *prev = track;
|
||||
|
||||
while (true) {
|
||||
prev = (struct tree_track *)simple_list_get_prev(&lib_editable.head,
|
||||
(struct simple_track *)prev, NULL, false);
|
||||
if (prev == NULL)
|
||||
return track;
|
||||
if (prev->album == track->album)
|
||||
track = prev;
|
||||
}
|
||||
}
|
||||
|
||||
static struct tree_track *sorted_album_last_track(struct tree_track *track)
|
||||
{
|
||||
struct tree_track *next = track;
|
||||
|
||||
while (true) {
|
||||
next = (struct tree_track *)simple_list_get_next(&lib_editable.head,
|
||||
(struct simple_track *)next, NULL, false);
|
||||
if (next == NULL)
|
||||
return track;
|
||||
if (next->album == track->album)
|
||||
track = next;
|
||||
}
|
||||
}
|
||||
|
||||
/* set next/prev (tree) }}} */
|
||||
|
||||
void lib_reshuffle(void)
|
||||
{
|
||||
shuffle_list_reshuffle(&lib_shuffle_root);
|
||||
shuffle_list_reshuffle(&lib_album_shuffle_root);
|
||||
if (lib_cur_track) {
|
||||
shuffle_insert(&lib_shuffle_root, NULL, &lib_cur_track->simple_track.shuffle_info);
|
||||
shuffle_insert(&lib_album_shuffle_root, NULL, &lib_cur_track->album->shuffle_info);
|
||||
}
|
||||
}
|
||||
|
||||
void lib_sort_artists(void) {
|
||||
tree_sort_artists(album_shuffle_list_add, album_shuffle_list_remove);
|
||||
}
|
||||
|
||||
static void free_lib_track(struct editable *e, struct list_head *item)
|
||||
{
|
||||
struct tree_track *track = (struct tree_track *)to_simple_track(item);
|
||||
struct track_info *ti = tree_track_info(track);
|
||||
|
||||
if (track == lib_cur_track)
|
||||
lib_cur_track = NULL;
|
||||
|
||||
if (remove_from_hash)
|
||||
hash_remove(ti);
|
||||
|
||||
rb_erase(&track->simple_track.shuffle_info.tree_node, &lib_shuffle_root);
|
||||
tree_remove(track, album_shuffle_list_remove);
|
||||
|
||||
track_info_unref(ti);
|
||||
free(track);
|
||||
}
|
||||
|
||||
void lib_init(void)
|
||||
{
|
||||
editable_shared_init(&lib_editable_shared, free_lib_track);
|
||||
editable_init(&lib_editable, &lib_editable_shared, 1);
|
||||
tree_init();
|
||||
srand(time(NULL));
|
||||
}
|
||||
|
||||
struct track_info *lib_set_track(struct tree_track *track)
|
||||
{
|
||||
struct track_info *ti = NULL;
|
||||
|
||||
if (track) {
|
||||
lib_cur_track = track;
|
||||
ti = tree_track_info(track);
|
||||
track_info_ref(ti);
|
||||
if (follow) {
|
||||
tree_sel_current(auto_expand_albums_follow);
|
||||
sorted_sel_current();
|
||||
}
|
||||
all_wins_changed();
|
||||
}
|
||||
return ti;
|
||||
}
|
||||
|
||||
struct track_info *lib_goto_next(void)
|
||||
{
|
||||
struct tree_track *track;
|
||||
|
||||
if (rb_root_empty(&lib_artist_root)) {
|
||||
BUG_ON(lib_cur_track != NULL);
|
||||
return NULL;
|
||||
}
|
||||
if (shuffle == SHUFFLE_TRACKS) {
|
||||
track = (struct tree_track *)shuffle_list_get_next(&lib_shuffle_root,
|
||||
(struct shuffle_info *)lib_cur_track, aaa_mode_filter);
|
||||
} else if (shuffle == SHUFFLE_ALBUMS) {
|
||||
if (play_sorted)
|
||||
track = (struct tree_track *)simple_list_get_next(&lib_editable.head,
|
||||
(struct simple_track *)lib_cur_track, cur_album_filter, false);
|
||||
else
|
||||
track = normal_get_next(AAA_MODE_ALBUM, false, false);
|
||||
if (track == NULL) {
|
||||
track = shuffle_album_get_next();
|
||||
if (play_sorted)
|
||||
track = sorted_album_first_track(track);
|
||||
}
|
||||
} else if (play_sorted) {
|
||||
track = (struct tree_track *)simple_list_get_next(&lib_editable.head,
|
||||
(struct simple_track *)lib_cur_track, aaa_mode_filter, true);
|
||||
} else {
|
||||
track = normal_get_next(aaa_mode, true, false);
|
||||
}
|
||||
return lib_set_track(track);
|
||||
}
|
||||
|
||||
struct track_info *lib_goto_prev(void)
|
||||
{
|
||||
struct tree_track *track;
|
||||
|
||||
if (rb_root_empty(&lib_artist_root)) {
|
||||
BUG_ON(lib_cur_track != NULL);
|
||||
return NULL;
|
||||
}
|
||||
if (shuffle == SHUFFLE_TRACKS) {
|
||||
track = (struct tree_track *)shuffle_list_get_prev(&lib_shuffle_root,
|
||||
(struct shuffle_info *)lib_cur_track, aaa_mode_filter);
|
||||
} else if (shuffle == SHUFFLE_ALBUMS) {
|
||||
if (play_sorted)
|
||||
track = (struct tree_track *)simple_list_get_prev(&lib_editable.head,
|
||||
(struct simple_track *)lib_cur_track, cur_album_filter, false);
|
||||
else
|
||||
track = normal_get_prev(AAA_MODE_ALBUM, false, false);
|
||||
if (track == NULL) {
|
||||
track = shuffle_album_get_prev();
|
||||
if (play_sorted)
|
||||
track = sorted_album_last_track(track);
|
||||
}
|
||||
} else if (play_sorted) {
|
||||
track = (struct tree_track *)simple_list_get_prev(&lib_editable.head,
|
||||
(struct simple_track *)lib_cur_track, aaa_mode_filter, true);
|
||||
} else {
|
||||
track = normal_get_prev(aaa_mode, true, false);
|
||||
}
|
||||
return lib_set_track(track);
|
||||
}
|
||||
|
||||
struct track_info *lib_goto_next_album(void)
|
||||
{
|
||||
struct tree_track *track = NULL;
|
||||
|
||||
if (rb_root_empty(&lib_artist_root)) {
|
||||
BUG_ON(lib_cur_track != NULL);
|
||||
return NULL;
|
||||
}
|
||||
if (shuffle == SHUFFLE_TRACKS) {
|
||||
return lib_goto_next();
|
||||
} else if (shuffle == SHUFFLE_ALBUMS) {
|
||||
track = shuffle_album_get_next();
|
||||
if (play_sorted)
|
||||
track = sorted_album_first_track(track);
|
||||
} else if (play_sorted) {
|
||||
track = sorted_album_last_track(lib_cur_track);
|
||||
track = (struct tree_track *)simple_list_get_next(&lib_editable.head,
|
||||
(struct simple_track *)track, aaa_mode_filter, true);
|
||||
} else {
|
||||
track = normal_get_next(aaa_mode, true, true);
|
||||
}
|
||||
|
||||
return lib_set_track(track);
|
||||
}
|
||||
|
||||
struct track_info *lib_goto_prev_album(void)
|
||||
{
|
||||
struct tree_track *track = NULL;
|
||||
|
||||
if (rb_root_empty(&lib_artist_root)) {
|
||||
BUG_ON(lib_cur_track != NULL);
|
||||
return NULL;
|
||||
}
|
||||
if (shuffle == SHUFFLE_TRACKS) {
|
||||
return lib_goto_prev();
|
||||
} else if (shuffle == SHUFFLE_ALBUMS) {
|
||||
track = shuffle_album_get_prev();
|
||||
if (play_sorted)
|
||||
track = sorted_album_first_track(track);
|
||||
else if (track)
|
||||
track = album_first_track(track->album);
|
||||
} else if (play_sorted) {
|
||||
track = sorted_album_first_track(lib_cur_track);
|
||||
track = (struct tree_track *)simple_list_get_prev(&lib_editable.head,
|
||||
(struct simple_track *)track, aaa_mode_filter, true);
|
||||
track = sorted_album_first_track(track);
|
||||
} else {
|
||||
track = normal_get_prev(aaa_mode, true, true);
|
||||
if (track)
|
||||
track = album_first_track(track->album);
|
||||
}
|
||||
|
||||
return lib_set_track(track);
|
||||
}
|
||||
|
||||
static struct tree_track *sorted_get_selected(void)
|
||||
{
|
||||
struct iter sel;
|
||||
|
||||
if (list_empty(&lib_editable.head))
|
||||
return NULL;
|
||||
|
||||
window_get_sel(lib_editable.shared->win, &sel);
|
||||
return iter_to_sorted_track(&sel);
|
||||
}
|
||||
|
||||
struct track_info *sorted_activate_selected(void)
|
||||
{
|
||||
return lib_set_track(sorted_get_selected());
|
||||
}
|
||||
|
||||
static void hash_add_to_views(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < FH_SIZE; i++) {
|
||||
struct fh_entry *e;
|
||||
|
||||
e = ti_hash[i];
|
||||
while (e) {
|
||||
struct track_info *ti = e->ti;
|
||||
|
||||
if (!is_filtered(ti) && !(ignore_duplicates && track_exists(ti)))
|
||||
views_add_track(ti);
|
||||
e = e->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct tree_track *lib_find_track(struct track_info *ti)
|
||||
{
|
||||
struct simple_track *track;
|
||||
|
||||
list_for_each_entry(track, &lib_editable.head, node) {
|
||||
if (strcmp(track->info->filename, ti->filename) == 0) {
|
||||
struct tree_track *tt = (struct tree_track *)track;
|
||||
return tt;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void lib_store_cur_track(struct track_info *ti)
|
||||
{
|
||||
if (cur_track_ti)
|
||||
track_info_unref(cur_track_ti);
|
||||
cur_track_ti = ti;
|
||||
track_info_ref(cur_track_ti);
|
||||
}
|
||||
|
||||
struct track_info *lib_get_cur_stored_track(void)
|
||||
{
|
||||
if (cur_track_ti && lib_find_track(cur_track_ti))
|
||||
return cur_track_ti;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void restore_cur_track(struct track_info *ti)
|
||||
{
|
||||
struct tree_track *tt = lib_find_track(ti);
|
||||
if (tt)
|
||||
lib_cur_track = tt;
|
||||
}
|
||||
|
||||
static int is_filtered_cb(void *data, struct track_info *ti)
|
||||
{
|
||||
return is_filtered(ti);
|
||||
}
|
||||
|
||||
static void do_lib_filter(int clear_before)
|
||||
{
|
||||
/* try to save cur_track */
|
||||
if (lib_cur_track)
|
||||
lib_store_cur_track(tree_track_info(lib_cur_track));
|
||||
|
||||
if (clear_before)
|
||||
d_print("filter results could grow, clear tracks and re-add (slow)\n");
|
||||
|
||||
remove_from_hash = 0;
|
||||
if (clear_before) {
|
||||
editable_clear(&lib_editable);
|
||||
hash_add_to_views();
|
||||
} else
|
||||
editable_remove_matching_tracks(&lib_editable, is_filtered_cb, NULL);
|
||||
remove_from_hash = 1;
|
||||
|
||||
window_changed(lib_editable.shared->win);
|
||||
window_goto_top(lib_editable.shared->win);
|
||||
lib_cur_win = lib_tree_win;
|
||||
window_goto_top(lib_tree_win);
|
||||
|
||||
/* restore cur_track */
|
||||
if (cur_track_ti && !lib_cur_track)
|
||||
restore_cur_track(cur_track_ti);
|
||||
}
|
||||
|
||||
static void unset_live_filter(void)
|
||||
{
|
||||
free(lib_live_filter);
|
||||
lib_live_filter = NULL;
|
||||
free(live_filter_expr);
|
||||
live_filter_expr = NULL;
|
||||
}
|
||||
|
||||
void lib_set_filter(struct expr *expr)
|
||||
{
|
||||
int clear_before = lib_live_filter || filter;
|
||||
unset_live_filter();
|
||||
if (filter)
|
||||
expr_free(filter);
|
||||
filter = expr;
|
||||
do_lib_filter(clear_before);
|
||||
}
|
||||
|
||||
void lib_set_add_filter(struct expr *expr)
|
||||
{
|
||||
if (add_filter)
|
||||
expr_free(add_filter);
|
||||
add_filter = expr;
|
||||
}
|
||||
|
||||
static struct tree_track *get_sel_track(void)
|
||||
{
|
||||
switch (cur_view) {
|
||||
case TREE_VIEW:
|
||||
return tree_get_selected();
|
||||
case SORTED_VIEW:
|
||||
return sorted_get_selected();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void set_sel_track(struct tree_track *tt)
|
||||
{
|
||||
struct iter iter;
|
||||
|
||||
switch (cur_view) {
|
||||
case TREE_VIEW:
|
||||
tree_sel_track(tt, auto_expand_albums_selcur);
|
||||
break;
|
||||
case SORTED_VIEW:
|
||||
sorted_track_to_iter(tt, &iter);
|
||||
window_set_sel(lib_editable.shared->win, &iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void store_sel_track(void)
|
||||
{
|
||||
struct tree_track *tt = get_sel_track();
|
||||
if (tt) {
|
||||
sel_track_ti = tree_track_info(tt);
|
||||
track_info_ref(sel_track_ti);
|
||||
}
|
||||
}
|
||||
|
||||
static void restore_sel_track(void)
|
||||
{
|
||||
if (sel_track_ti) {
|
||||
struct tree_track *tt = lib_find_track(sel_track_ti);
|
||||
if (tt) {
|
||||
set_sel_track(tt);
|
||||
track_info_unref(sel_track_ti);
|
||||
sel_track_ti = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* determine if filter results could grow, in which case all tracks must be cleared and re-added */
|
||||
static int do_clear_before(const char *str, struct expr *expr)
|
||||
{
|
||||
if (!lib_live_filter)
|
||||
return 0;
|
||||
if (!str)
|
||||
return 1;
|
||||
if ((!expr && live_filter_expr) || (expr && !live_filter_expr))
|
||||
return 1;
|
||||
if (!expr || expr_is_harmless(expr))
|
||||
return !strstr(str, lib_live_filter);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void lib_set_live_filter(const char *str)
|
||||
{
|
||||
int clear_before;
|
||||
struct expr *expr = NULL;
|
||||
|
||||
if (strcmp0(str, lib_live_filter) == 0)
|
||||
return;
|
||||
|
||||
if (str && expr_is_short(str)) {
|
||||
expr = expr_parse(str);
|
||||
if (!expr)
|
||||
return;
|
||||
}
|
||||
|
||||
clear_before = do_clear_before(str, expr);
|
||||
|
||||
if (!str)
|
||||
store_sel_track();
|
||||
|
||||
unset_live_filter();
|
||||
lib_live_filter = str ? xstrdup(str) : NULL;
|
||||
live_filter_expr = expr;
|
||||
do_lib_filter(clear_before);
|
||||
|
||||
if (expr) {
|
||||
unsigned int match_type = expr_get_match_type(expr);
|
||||
if (match_type & TI_MATCH_ALBUM)
|
||||
tree_expand_all();
|
||||
if (match_type & TI_MATCH_TITLE)
|
||||
tree_sel_first();
|
||||
} else if (str)
|
||||
tree_expand_matching(str);
|
||||
|
||||
if (!str)
|
||||
restore_sel_track();
|
||||
}
|
||||
|
||||
int lib_remove(struct track_info *ti)
|
||||
{
|
||||
struct simple_track *track;
|
||||
|
||||
list_for_each_entry(track, &lib_editable.head, node) {
|
||||
if (track->info == ti) {
|
||||
editable_remove_track(&lib_editable, track);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void lib_clear_store(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < FH_SIZE; i++) {
|
||||
struct fh_entry *e, *next;
|
||||
|
||||
e = ti_hash[i];
|
||||
while (e) {
|
||||
next = e->next;
|
||||
track_info_unref(e->ti);
|
||||
free(e);
|
||||
e = next;
|
||||
}
|
||||
ti_hash[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void sorted_sel_current(void)
|
||||
{
|
||||
if (lib_cur_track) {
|
||||
struct iter iter;
|
||||
|
||||
sorted_track_to_iter(lib_cur_track, &iter);
|
||||
window_set_sel(lib_editable.shared->win, &iter);
|
||||
}
|
||||
}
|
||||
|
||||
static int ti_cmp(const void *a, const void *b)
|
||||
{
|
||||
const struct track_info *ai = *(const struct track_info **)a;
|
||||
const struct track_info *bi = *(const struct track_info **)b;
|
||||
|
||||
return track_info_cmp(ai, bi, lib_editable.shared->sort_keys);
|
||||
}
|
||||
|
||||
static int do_lib_for_each(int (*cb)(void *data, struct track_info *ti), void *data, int filtered)
|
||||
{
|
||||
int i, rc = 0, count = 0, size = 1024;
|
||||
struct track_info **tis;
|
||||
|
||||
tis = xnew(struct track_info *, size);
|
||||
|
||||
/* collect all track_infos */
|
||||
for (i = 0; i < FH_SIZE; i++) {
|
||||
struct fh_entry *e;
|
||||
|
||||
e = ti_hash[i];
|
||||
while (e) {
|
||||
if (count == size) {
|
||||
size *= 2;
|
||||
tis = xrenew(struct track_info *, tis, size);
|
||||
}
|
||||
if (!filtered || !filter || expr_eval(filter, e->ti))
|
||||
tis[count++] = e->ti;
|
||||
e = e->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* sort to speed up playlist loading */
|
||||
qsort(tis, count, sizeof(struct track_info *), ti_cmp);
|
||||
for (i = 0; i < count; i++) {
|
||||
rc = cb(data, tis[i]);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
|
||||
free(tis);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int lib_for_each(int (*cb)(void *data, struct track_info *ti), void *data,
|
||||
void *opaque)
|
||||
{
|
||||
return do_lib_for_each(cb, data, 0);
|
||||
}
|
||||
|
||||
int lib_for_each_filtered(int (*cb)(void *data, struct track_info *ti),
|
||||
void *data, void *opaque)
|
||||
{
|
||||
return do_lib_for_each(cb, data, 1);
|
||||
}
|
||||
Reference in New Issue
Block a user