flac: Saner EOF handling
[cmus.git] / lib.c
blob26519f335dbf557ce3ba1cf30408f7ff3385b1f2
1 /*
2 * Copyright 2004-2006 Timo Hirvonen
3 */
5 #include "lib.h"
6 #include "editable.h"
7 #include "track_info.h"
8 #include "options.h"
9 #include "xmalloc.h"
10 #include "debug.h"
12 #include <pthread.h>
13 #include <string.h>
15 struct editable lib_editable;
16 struct tree_track *lib_cur_track = NULL;
17 unsigned int play_sorted = 0;
18 enum aaa_mode aaa_mode = AAA_MODE_ALL;
20 static LIST_HEAD(lib_shuffle_head);
21 static struct expr *filter = NULL;
22 static int remove_from_hash = 1;
24 static inline struct tree_track *to_sorted(const struct list_head *item)
26 return (struct tree_track *)container_of(item, struct simple_track, node);
29 static inline void sorted_track_to_iter(struct tree_track *track, struct iter *iter)
31 iter->data0 = &lib_editable.head;
32 iter->data1 = track;
33 iter->data2 = NULL;
36 static void all_wins_changed(void)
38 lib_tree_win->changed = 1;
39 lib_track_win->changed = 1;
40 lib_editable.win->changed = 1;
43 static void shuffle_add(struct tree_track *track)
45 list_add_rand(&lib_shuffle_head, &track->shuffle_track.node, lib_editable.nr_tracks);
48 static void views_add_track(struct track_info *ti)
50 struct tree_track *track = xnew(struct tree_track, 1);
52 /* NOTE: does not ref ti */
53 simple_track_init((struct simple_track *)track, ti);
55 /* both the hash table and views have refs */
56 track_info_ref(ti);
58 tree_add_track(track);
59 shuffle_add(track);
60 editable_add(&lib_editable, (struct simple_track *)track);
63 struct fh_entry {
64 struct fh_entry *next;
66 /* ref count is increased when added to this hash */
67 struct track_info *ti;
70 #define FH_SIZE (1024)
71 static struct fh_entry *ti_hash[FH_SIZE] = { NULL, };
73 /* this is from glib */
74 static unsigned int str_hash(const char *str)
76 unsigned int hash = 0;
77 int i;
79 for (i = 0; str[i]; i++)
80 hash = (hash << 5) - hash + str[i];
81 return hash;
84 static int hash_insert(struct track_info *ti)
86 const char *filename = ti->filename;
87 unsigned int pos = str_hash(filename) % FH_SIZE;
88 struct fh_entry **entryp;
89 struct fh_entry *e;
91 entryp = &ti_hash[pos];
92 e = *entryp;
93 while (e) {
94 if (strcmp(e->ti->filename, filename) == 0) {
95 /* found, don't insert */
96 return 0;
98 e = e->next;
101 e = xnew(struct fh_entry, 1);
102 track_info_ref(ti);
103 e->ti = ti;
104 e->next = *entryp;
105 *entryp = e;
106 return 1;
109 static void hash_remove(struct track_info *ti)
111 const char *filename = ti->filename;
112 unsigned int pos = str_hash(filename) % FH_SIZE;
113 struct fh_entry **entryp;
115 entryp = &ti_hash[pos];
116 while (1) {
117 struct fh_entry *e = *entryp;
119 BUG_ON(e == NULL);
120 if (strcmp(e->ti->filename, filename) == 0) {
121 *entryp = e->next;
122 track_info_unref(e->ti);
123 free(e);
124 break;
126 entryp = &e->next;
130 void lib_add_track(struct track_info *ti)
132 if (!hash_insert(ti)) {
133 /* duplicate files not allowed */
134 return;
136 if (filter == NULL || expr_eval(filter, ti))
137 views_add_track(ti);
140 static struct tree_track *album_first_track(const struct album *album)
142 return to_tree_track(album->track_head.next);
145 static struct tree_track *artist_first_track(const struct artist *artist)
147 return album_first_track(to_album(artist->album_head.next));
150 static struct tree_track *normal_get_first(void)
152 return artist_first_track(to_artist(lib_artist_head.next));
155 static struct tree_track *album_last_track(const struct album *album)
157 return to_tree_track(album->track_head.prev);
160 static struct tree_track *artist_last_track(const struct artist *artist)
162 return album_last_track(to_album(artist->album_head.prev));
165 static int aaa_mode_filter(const struct simple_track *track)
167 const struct album *album = ((struct tree_track *)track)->album;
169 if (aaa_mode == AAA_MODE_ALBUM)
170 return CUR_ALBUM == album;
172 if (aaa_mode == AAA_MODE_ARTIST)
173 return CUR_ARTIST == album->artist;
175 /* AAA_MODE_ALL */
176 return 1;
179 /* set next/prev (tree) {{{ */
181 static struct tree_track *normal_get_next(void)
183 if (lib_cur_track == NULL)
184 return normal_get_first();
186 /* not last track of the album? */
187 if (lib_cur_track->node.next != &CUR_ALBUM->track_head) {
188 /* next track of the current album */
189 return to_tree_track(lib_cur_track->node.next);
192 if (aaa_mode == AAA_MODE_ALBUM) {
193 if (!repeat)
194 return NULL;
195 /* first track of the current album */
196 return album_first_track(CUR_ALBUM);
199 /* not last album of the artist? */
200 if (CUR_ALBUM->node.next != &CUR_ARTIST->album_head) {
201 /* first track of the next album */
202 return album_first_track(to_album(CUR_ALBUM->node.next));
205 if (aaa_mode == AAA_MODE_ARTIST) {
206 if (!repeat)
207 return NULL;
208 /* first track of the first album of the current artist */
209 return artist_first_track(CUR_ARTIST);
212 /* not last artist of the library? */
213 if (CUR_ARTIST->node.next != &lib_artist_head) {
214 /* first track of the next artist */
215 return artist_first_track(to_artist(CUR_ARTIST->node.next));
218 if (!repeat)
219 return NULL;
221 /* first track */
222 return normal_get_first();
225 static struct tree_track *normal_get_prev(void)
227 if (lib_cur_track == NULL)
228 return normal_get_first();
230 /* not first track of the album? */
231 if (lib_cur_track->node.prev != &CUR_ALBUM->track_head) {
232 /* prev track of the album */
233 return to_tree_track(lib_cur_track->node.prev);
236 if (aaa_mode == AAA_MODE_ALBUM) {
237 if (!repeat)
238 return NULL;
239 /* last track of the album */
240 return to_tree_track(CUR_ALBUM->track_head.prev);
243 /* not first album of the artist? */
244 if (CUR_ALBUM->node.prev != &CUR_ARTIST->album_head) {
245 /* last track of the prev album of the artist */
246 return album_last_track(to_album(CUR_ALBUM->node.prev));
249 if (aaa_mode == AAA_MODE_ARTIST) {
250 if (!repeat)
251 return NULL;
252 /* last track of the last album of the artist */
253 return album_last_track(to_album(CUR_ARTIST->album_head.prev));
256 /* not first artist of the library? */
257 if (CUR_ARTIST->node.prev != &lib_artist_head) {
258 /* last track of the last album of the prev artist */
259 return artist_last_track(to_artist(CUR_ARTIST->node.prev));
262 if (!repeat)
263 return NULL;
265 /* last track */
266 return artist_last_track(to_artist(lib_artist_head.prev));
269 /* set next/prev (tree) }}} */
271 void lib_reshuffle(void)
273 reshuffle(&lib_shuffle_head);
276 static void free_lib_track(struct list_head *item)
278 struct tree_track *track = (struct tree_track *)to_simple_track(item);
279 struct track_info *ti = tree_track_info(track);
281 if (track == lib_cur_track)
282 lib_cur_track = NULL;
284 if (remove_from_hash)
285 hash_remove(ti);
287 list_del(&track->shuffle_track.node);
288 tree_remove(track);
290 track_info_unref(ti);
291 free(track);
294 void lib_init(void)
296 editable_init(&lib_editable, free_lib_track);
297 tree_init();
298 srand(time(NULL));
301 static struct track_info *lib_set_track(struct tree_track *track)
303 struct track_info *ti = NULL;
305 if (track) {
306 lib_cur_track = track;
307 ti = tree_track_info(track);
308 track_info_ref(ti);
309 all_wins_changed();
311 return ti;
314 struct track_info *lib_set_next(void)
316 struct tree_track *track;
318 if (list_empty(&lib_artist_head)) {
319 BUG_ON(lib_cur_track != NULL);
320 return NULL;
322 if (shuffle) {
323 track = (struct tree_track *)shuffle_list_get_next(&lib_shuffle_head,
324 (struct shuffle_track *)lib_cur_track, aaa_mode_filter);
325 } else if (play_sorted) {
326 track = (struct tree_track *)simple_list_get_next(&lib_editable.head,
327 (struct simple_track *)lib_cur_track, aaa_mode_filter);
328 } else {
329 track = normal_get_next();
331 return lib_set_track(track);
334 struct track_info *lib_set_prev(void)
336 struct tree_track *track;
338 if (list_empty(&lib_artist_head)) {
339 BUG_ON(lib_cur_track != NULL);
340 return NULL;
342 if (shuffle) {
343 track = (struct tree_track *)shuffle_list_get_prev(&lib_shuffle_head,
344 (struct shuffle_track *)lib_cur_track, aaa_mode_filter);
345 } else if (play_sorted) {
346 track = (struct tree_track *)simple_list_get_prev(&lib_editable.head,
347 (struct simple_track *)lib_cur_track, aaa_mode_filter);
348 } else {
349 track = normal_get_prev();
351 return lib_set_track(track);
354 struct track_info *sorted_set_selected(void)
356 struct iter sel;
358 if (list_empty(&lib_editable.head))
359 return NULL;
361 window_get_sel(lib_editable.win, &sel);
362 return lib_set_track(iter_to_sorted_track(&sel));
365 void lib_set_filter(struct expr *expr)
367 static const char *tmp_keys[1] = { NULL };
368 struct track_info *cur_ti = NULL;
369 const char **sort_keys;
370 int i;
372 /* try to save cur_track */
373 if (lib_cur_track) {
374 cur_ti = tree_track_info(lib_cur_track);
375 track_info_ref(cur_ti);
378 remove_from_hash = 0;
379 editable_clear(&lib_editable);
380 remove_from_hash = 1;
382 if (filter)
383 expr_free(filter);
384 filter = expr;
386 /* disable sorting temporarily */
387 sort_keys = lib_editable.sort_keys;
388 lib_editable.sort_keys = tmp_keys;
390 for (i = 0; i < FH_SIZE; i++) {
391 struct fh_entry *e;
393 e = ti_hash[i];
394 while (e) {
395 struct track_info *ti = e->ti;
397 if (filter == NULL || expr_eval(filter, ti))
398 views_add_track(ti);
399 e = e->next;
403 /* enable sorting */
404 lib_editable.sort_keys = sort_keys;
405 editable_sort(&lib_editable);
407 lib_cur_win = lib_tree_win;
408 window_goto_top(lib_tree_win);
410 /* restore cur_track */
411 if (cur_ti) {
412 struct simple_track *track;
414 list_for_each_entry(track, &lib_editable.head, node) {
415 if (strcmp(track->info->filename, cur_ti->filename) == 0) {
416 struct tree_track *tt = (struct tree_track *)track;
418 lib_cur_track = tt;
419 break;
422 track_info_unref(cur_ti);
426 int lib_remove(struct track_info *ti)
428 struct simple_track *track;
430 list_for_each_entry(track, &lib_editable.head, node) {
431 if (track->info == ti) {
432 editable_remove_track(&lib_editable, track);
433 return 1;
436 return 0;
439 void lib_clear_store(void)
441 int i;
443 for (i = 0; i < FH_SIZE; i++) {
444 struct fh_entry *e, *next;
446 e = ti_hash[i];
447 while (e) {
448 next = e->next;
449 track_info_unref(e->ti);
450 free(e);
451 e = next;
453 ti_hash[i] = NULL;
457 void sorted_sel_current(void)
459 if (lib_cur_track) {
460 struct iter iter;
462 sorted_track_to_iter(lib_cur_track, &iter);
463 window_set_sel(lib_editable.win, &iter);
467 static int ti_cmp(const void *a, const void *b)
469 const struct track_info *ai = *(const struct track_info **)a;
470 const struct track_info *bi = *(const struct track_info **)b;
472 return track_info_cmp(ai, bi, lib_editable.sort_keys);
475 int lib_for_each(int (*cb)(void *data, struct track_info *ti), void *data)
477 int i, rc = 0, count = 0, size = 1024;
478 struct track_info **tis;
480 tis = xnew(struct track_info *, size);
482 /* collect all track_infos */
483 for (i = 0; i < FH_SIZE; i++) {
484 struct fh_entry *e;
486 e = ti_hash[i];
487 while (e) {
488 if (count == size) {
489 size *= 2;
490 tis = xrenew(struct track_info *, tis, size);
492 tis[count++] = e->ti;
493 e = e->next;
497 /* sort to speed up playlist loading */
498 qsort(tis, count, sizeof(struct track_info *), ti_cmp);
499 for (i = 0; i < count; i++) {
500 rc = cb(data, tis[i]);
501 if (rc)
502 break;
505 free(tis);
506 return rc;