Move detection of supported filenames to cmus.c
[cmus.git] / lib.c
blob71b662a9edc98f090505e8ca1d04107dc068d86a
1 /*
2 * Copyright 2004-2006 Timo Hirvonen
3 */
5 #include "lib.h"
6 #include "editable.h"
7 #include "options.h"
8 #include "xmalloc.h"
9 #include "debug.h"
11 #include <pthread.h>
12 #include <string.h>
14 struct editable lib_editable;
15 LIST_HEAD(lib_shuffle_head);
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 struct expr *filter = NULL;
21 static int remove_from_hash = 1;
23 static inline struct tree_track *to_sorted(const struct list_head *item)
25 return (struct tree_track *)container_of(item, struct simple_track, node);
28 static inline void sorted_track_to_iter(struct tree_track *track, struct iter *iter)
30 iter->data0 = &lib_editable.head;
31 iter->data1 = track;
32 iter->data2 = NULL;
35 static void all_wins_changed(void)
37 lib_tree_win->changed = 1;
38 lib_track_win->changed = 1;
39 lib_editable.win->changed = 1;
42 static void shuffle_add(struct tree_track *track)
44 shuffle_list_add_track(&lib_shuffle_head, &track->shuffle_track.node, lib_editable.nr_tracks);
47 static void views_add_track(struct track_info *ti)
49 struct tree_track *track = xnew(struct tree_track, 1);
51 /* NOTE: does not ref ti */
52 simple_track_init((struct simple_track *)track, ti);
54 /* both the hash table and views have refs */
55 track_info_ref(ti);
57 tree_add_track(track);
58 shuffle_add(track);
59 editable_add(&lib_editable, (struct simple_track *)track);
62 struct fh_entry {
63 struct fh_entry *next;
65 /* ref count is increased when added to this hash */
66 struct track_info *ti;
69 #define FH_SIZE (1024)
70 static struct fh_entry *ti_hash[FH_SIZE] = { NULL, };
72 /* this is from glib */
73 static unsigned int str_hash(const char *str)
75 unsigned int hash = 0;
76 int i;
78 for (i = 0; str[i]; i++)
79 hash = (hash << 5) - hash + str[i];
80 return hash;
83 static int hash_insert(struct track_info *ti)
85 const char *filename = ti->filename;
86 unsigned int pos = str_hash(filename) % FH_SIZE;
87 struct fh_entry **entryp;
88 struct fh_entry *e;
90 entryp = &ti_hash[pos];
91 e = *entryp;
92 while (e) {
93 if (strcmp(e->ti->filename, filename) == 0) {
94 /* found, don't insert */
95 return 0;
97 e = e->next;
100 e = xnew(struct fh_entry, 1);
101 track_info_ref(ti);
102 e->ti = ti;
103 e->next = *entryp;
104 *entryp = e;
105 return 1;
108 static void hash_remove(struct track_info *ti)
110 const char *filename = ti->filename;
111 unsigned int pos = str_hash(filename) % FH_SIZE;
112 struct fh_entry **entryp;
114 entryp = &ti_hash[pos];
115 while (1) {
116 struct fh_entry *e = *entryp;
118 BUG_ON(e == NULL);
119 if (strcmp(e->ti->filename, filename) == 0) {
120 *entryp = e->next;
121 track_info_unref(e->ti);
122 free(e);
123 break;
125 entryp = &e->next;
129 void lib_add_track(struct track_info *ti)
131 if (!hash_insert(ti)) {
132 /* duplicate files not allowed */
133 return;
135 if (filter == NULL || expr_eval(filter, ti))
136 views_add_track(ti);
139 static struct tree_track *album_first_track(const struct album *album)
141 return to_tree_track(album->track_head.next);
144 static struct tree_track *artist_first_track(const struct artist *artist)
146 return album_first_track(to_album(artist->album_head.next));
149 static struct tree_track *normal_get_first(void)
151 return artist_first_track(to_artist(lib_artist_head.next));
154 static struct tree_track *album_last_track(const struct album *album)
156 return to_tree_track(album->track_head.prev);
159 static struct tree_track *artist_last_track(const struct artist *artist)
161 return album_last_track(to_album(artist->album_head.prev));
164 static int aaa_mode_filter(const struct simple_track *track)
166 const struct album *album = ((struct tree_track *)track)->album;
168 if (aaa_mode == AAA_MODE_ALBUM)
169 return CUR_ALBUM == album;
171 if (aaa_mode == AAA_MODE_ARTIST)
172 return CUR_ARTIST == album->artist;
174 /* AAA_MODE_ALL */
175 return 1;
178 /* set next/prev (tree) {{{ */
180 static struct tree_track *normal_get_next(void)
182 if (lib_cur_track == NULL)
183 return normal_get_first();
185 /* not last track of the album? */
186 if (lib_cur_track->node.next != &CUR_ALBUM->track_head) {
187 /* next track of the current album */
188 return to_tree_track(lib_cur_track->node.next);
191 if (aaa_mode == AAA_MODE_ALBUM) {
192 if (!repeat)
193 return NULL;
194 /* first track of the current album */
195 return album_first_track(CUR_ALBUM);
198 /* not last album of the artist? */
199 if (CUR_ALBUM->node.next != &CUR_ARTIST->album_head) {
200 /* first track of the next album */
201 return album_first_track(to_album(CUR_ALBUM->node.next));
204 if (aaa_mode == AAA_MODE_ARTIST) {
205 if (!repeat)
206 return NULL;
207 /* first track of the first album of the current artist */
208 return artist_first_track(CUR_ARTIST);
211 /* not last artist of the library? */
212 if (CUR_ARTIST->node.next != &lib_artist_head) {
213 /* first track of the next artist */
214 return artist_first_track(to_artist(CUR_ARTIST->node.next));
217 if (!repeat)
218 return NULL;
220 /* first track */
221 return normal_get_first();
224 static struct tree_track *normal_get_prev(void)
226 if (lib_cur_track == NULL)
227 return normal_get_first();
229 /* not first track of the album? */
230 if (lib_cur_track->node.prev != &CUR_ALBUM->track_head) {
231 /* prev track of the album */
232 return to_tree_track(lib_cur_track->node.prev);
235 if (aaa_mode == AAA_MODE_ALBUM) {
236 if (!repeat)
237 return NULL;
238 /* last track of the album */
239 return to_tree_track(CUR_ALBUM->track_head.prev);
242 /* not first album of the artist? */
243 if (CUR_ALBUM->node.prev != &CUR_ARTIST->album_head) {
244 /* last track of the prev album of the artist */
245 return album_last_track(to_album(CUR_ALBUM->node.prev));
248 if (aaa_mode == AAA_MODE_ARTIST) {
249 if (!repeat)
250 return NULL;
251 /* last track of the last album of the artist */
252 return album_last_track(to_album(CUR_ARTIST->album_head.prev));
255 /* not first artist of the library? */
256 if (CUR_ARTIST->node.prev != &lib_artist_head) {
257 /* last track of the last album of the prev artist */
258 return artist_last_track(to_artist(CUR_ARTIST->node.prev));
261 if (!repeat)
262 return NULL;
264 /* last track */
265 return artist_last_track(to_artist(lib_artist_head.prev));
268 /* set next/prev (tree) }}} */
270 void lib_reshuffle(void)
272 reshuffle(&lib_shuffle_head);
275 static void free_lib_track(struct list_head *item)
277 struct tree_track *track = (struct tree_track *)to_simple_track(item);
278 struct track_info *ti = tree_track_info(track);
280 if (track == lib_cur_track)
281 lib_cur_track = NULL;
283 if (remove_from_hash)
284 hash_remove(ti);
286 list_del(&track->shuffle_track.node);
287 tree_remove(track);
289 track_info_unref(ti);
290 free(track);
293 void lib_init(void)
295 editable_init(&lib_editable, free_lib_track);
296 tree_init();
297 srand(time(NULL));
300 struct track_info *lib_set_next(void)
302 struct tree_track *track;
303 struct track_info *ti = NULL;
305 if (list_empty(&lib_artist_head)) {
306 BUG_ON(lib_cur_track != NULL);
307 return NULL;
309 if (shuffle) {
310 track = (struct tree_track *)shuffle_list_get_next(&lib_shuffle_head,
311 (struct shuffle_track *)lib_cur_track, aaa_mode_filter);
312 } else if (play_sorted) {
313 track = (struct tree_track *)simple_list_get_next(&lib_editable.head,
314 (struct simple_track *)lib_cur_track, aaa_mode_filter);
315 } else {
316 track = normal_get_next();
318 if (track) {
319 lib_cur_track = track;
320 ti = tree_track_info(track);
322 track_info_ref(ti);
323 all_wins_changed();
325 return ti;
328 struct track_info *lib_set_prev(void)
330 struct tree_track *track;
331 struct track_info *ti = NULL;
333 if (list_empty(&lib_artist_head)) {
334 BUG_ON(lib_cur_track != NULL);
335 return NULL;
337 if (shuffle) {
338 track = (struct tree_track *)shuffle_list_get_prev(&lib_shuffle_head,
339 (struct shuffle_track *)lib_cur_track, aaa_mode_filter);
340 } else if (play_sorted) {
341 track = (struct tree_track *)simple_list_get_prev(&lib_editable.head,
342 (struct simple_track *)lib_cur_track, aaa_mode_filter);
343 } else {
344 track = normal_get_prev();
346 if (track) {
347 lib_cur_track = track;
348 ti = tree_track_info(track);
350 track_info_ref(ti);
351 all_wins_changed();
353 return ti;
356 struct track_info *sorted_set_selected(void)
358 struct track_info *info;
359 struct iter sel;
361 if (list_empty(&lib_editable.head))
362 return NULL;
364 window_get_sel(lib_editable.win, &sel);
365 lib_cur_track = iter_to_sorted_track(&sel);
366 info = tree_track_info(lib_cur_track);
367 track_info_ref(info);
368 all_wins_changed();
369 return info;
372 void lib_set_filter(struct expr *expr)
374 static const char *tmp_keys[1] = { NULL };
375 struct track_info *cur_ti = NULL;
376 const char **sort_keys;
377 int i;
379 /* try to save cur_track */
380 if (lib_cur_track) {
381 cur_ti = tree_track_info(lib_cur_track);
382 track_info_ref(cur_ti);
385 remove_from_hash = 0;
386 editable_clear(&lib_editable);
387 remove_from_hash = 1;
389 if (filter)
390 expr_free(filter);
391 filter = expr;
393 /* disable sorting temporarily */
394 sort_keys = lib_editable.sort_keys;
395 lib_editable.sort_keys = tmp_keys;
397 for (i = 0; i < FH_SIZE; i++) {
398 struct fh_entry *e;
400 e = ti_hash[i];
401 while (e) {
402 struct track_info *ti = e->ti;
404 if (filter == NULL || expr_eval(filter, ti))
405 views_add_track(ti);
406 e = e->next;
410 /* enable sorting */
411 lib_editable.sort_keys = sort_keys;
412 editable_sort(&lib_editable);
414 lib_cur_win = lib_tree_win;
415 window_goto_top(lib_tree_win);
417 /* restore cur_track */
418 if (cur_ti) {
419 struct simple_track *track;
421 list_for_each_entry(track, &lib_editable.head, node) {
422 if (strcmp(track->info->filename, cur_ti->filename) == 0) {
423 struct tree_track *tt = (struct tree_track *)track;
425 lib_cur_track = tt;
426 break;
429 track_info_unref(cur_ti);
433 void lib_remove(struct track_info *ti)
435 struct simple_track *track;
437 list_for_each_entry(track, &lib_editable.head, node) {
438 if (track->info == ti) {
439 editable_remove_track(&lib_editable, track);
440 break;
445 void lib_clear_store(void)
447 int i;
449 for (i = 0; i < FH_SIZE; i++) {
450 struct fh_entry *e, *next;
452 e = ti_hash[i];
453 while (e) {
454 next = e->next;
455 track_info_unref(e->ti);
456 free(e);
457 e = next;
459 ti_hash[i] = NULL;
463 void sorted_sel_current(void)
465 if (lib_cur_track) {
466 struct iter iter;
468 sorted_track_to_iter(lib_cur_track, &iter);
469 window_set_sel(lib_editable.win, &iter);
473 static int ti_filename_cmp(const void *a, const void *b)
475 const struct track_info *tia = *(const struct track_info **)a;
476 const struct track_info *tib = *(const struct track_info **)b;
478 return strcmp(tia->filename, tib->filename);
481 int lib_for_each(int (*cb)(void *data, struct track_info *ti), void *data)
483 int i, rc = 0, count = 0, size = 1024;
484 struct track_info **tis;
486 tis = xnew(struct track_info *, size);
488 /* collect all track_infos */
489 for (i = 0; i < FH_SIZE; i++) {
490 struct fh_entry *e;
492 e = ti_hash[i];
493 while (e) {
494 if (count == size) {
495 size *= 2;
496 tis = xrenew(struct track_info *, tis, size);
498 tis[count++] = e->ti;
499 e = e->next;
503 /* sort them by filename and call cb for each */
504 qsort(tis, count, sizeof(struct track_info *), ti_filename_cmp);
505 for (i = 0; i < count; i++) {
506 rc = cb(data, tis[i]);
507 if (rc)
508 break;
511 free(tis);
512 return rc;