Fix configure check for ffmpeg header file
[cmus.git] / tree.c
blob73c52a38bb123689352747b62037135762a691e1
1 /*
2 * Copyright 2006 Timo Hirvonen
3 */
5 #include "lib.h"
6 #include "search_mode.h"
7 #include "xmalloc.h"
8 #include "comment.h"
9 #include "utils.h"
10 #include "debug.h"
12 struct searchable *tree_searchable;
13 struct window *lib_tree_win;
14 struct window *lib_track_win;
15 struct window *lib_cur_win;
16 LIST_HEAD(lib_artist_head);
18 /* tree (search) iterators {{{ */
19 static int tree_search_get_prev(struct iter *iter)
21 struct list_head *head = iter->data0;
22 struct tree_track *track = iter->data1;
23 struct artist *artist;
24 struct album *album;
26 BUG_ON(iter->data2);
27 if (head == NULL)
28 return 0;
29 if (track == NULL) {
30 /* head, get last track */
31 if (head->prev == head) {
32 /* empty, iter points to the head already */
33 return 0;
35 artist = to_artist(head->prev);
36 album = to_album(artist->album_head.prev);
37 iter->data1 = to_tree_track(album->track_head.prev);
38 return 1;
40 /* prev track */
41 if (track->node.prev == &track->album->track_head || search_restricted) {
42 /* prev album */
43 if (track->album->node.prev == &track->album->artist->album_head) {
44 /* prev artist */
45 if (track->album->artist->node.prev == &lib_artist_head)
46 return 0;
47 artist = to_artist(track->album->artist->node.prev);
48 album = to_album(artist->album_head.prev);
49 track = to_tree_track(album->track_head.prev);
50 } else {
51 album = to_album(track->album->node.prev);
52 track = to_tree_track(album->track_head.prev);
54 } else {
55 track = to_tree_track(track->node.prev);
57 iter->data1 = track;
58 return 1;
61 static int tree_search_get_next(struct iter *iter)
63 struct list_head *head = iter->data0;
64 struct tree_track *track = iter->data1;
65 struct artist *artist;
66 struct album *album;
68 BUG_ON(iter->data2);
69 if (head == NULL)
70 return 0;
71 if (track == NULL) {
72 /* head, get first track */
73 if (head->next == head) {
74 /* empty, iter points to the head already */
75 return 0;
77 artist = to_artist(head->next);
78 album = to_album(artist->album_head.next);
79 iter->data1 = to_tree_track(album->track_head.next);
80 return 1;
82 /* next track */
83 if (track->node.next == &track->album->track_head || search_restricted) {
84 /* next album */
85 if (track->album->node.next == &track->album->artist->album_head) {
86 /* next artist */
87 if (track->album->artist->node.next == &lib_artist_head)
88 return 0;
89 artist = to_artist(track->album->artist->node.next);
90 album = to_album(artist->album_head.next);
91 track = to_tree_track(album->track_head.next);
92 } else {
93 album = to_album(track->album->node.next);
94 track = to_tree_track(album->track_head.next);
96 } else {
97 track = to_tree_track(track->node.next);
99 iter->data1 = track;
100 return 1;
102 /* }}} */
104 /* tree window iterators {{{ */
105 static int tree_get_prev(struct iter *iter)
107 struct list_head *head = iter->data0;
108 struct artist *artist = iter->data1;
109 struct album *album = iter->data2;
111 BUG_ON(head == NULL);
112 BUG_ON(artist == NULL && album != NULL);
113 if (artist == NULL) {
114 /* head, get last artist and/or album */
115 if (head->prev == head) {
116 /* empty, iter points to the head already */
117 return 0;
119 artist = to_artist(head->prev);
120 if (artist->expanded) {
121 album = to_album(artist->album_head.prev);
122 } else {
123 album = NULL;
125 iter->data1 = artist;
126 iter->data2 = album;
127 return 1;
129 if (artist->expanded && album) {
130 /* prev album */
131 if (album->node.prev == &artist->album_head) {
132 iter->data2 = NULL;
133 return 1;
134 } else {
135 iter->data2 = to_album(album->node.prev);
136 return 1;
140 /* prev artist */
141 if (artist->node.prev == &lib_artist_head) {
142 iter->data1 = NULL;
143 iter->data2 = NULL;
144 return 0;
146 artist = to_artist(artist->node.prev);
147 iter->data1 = artist;
148 iter->data2 = NULL;
149 if (artist->expanded) {
150 /* last album */
151 iter->data2 = to_album(artist->album_head.prev);
153 return 1;
156 static int tree_get_next(struct iter *iter)
158 struct list_head *head = iter->data0;
159 struct artist *artist = iter->data1;
160 struct album *album = iter->data2;
162 BUG_ON(head == NULL);
163 BUG_ON(artist == NULL && album != NULL);
164 if (artist == NULL) {
165 /* head, get first artist */
166 if (head->next == head) {
167 /* empty, iter points to the head already */
168 return 0;
170 iter->data1 = to_artist(head->next);
171 iter->data2 = NULL;
172 return 1;
174 if (artist->expanded) {
175 /* next album */
176 if (album == NULL) {
177 /* first album */
178 iter->data2 = to_album(artist->album_head.next);
179 return 1;
181 if (album->node.next != &artist->album_head) {
182 iter->data2 = to_album(album->node.next);
183 return 1;
187 /* next artist */
188 if (artist->node.next == head) {
189 iter->data1 = NULL;
190 iter->data2 = NULL;
191 return 0;
193 iter->data1 = to_artist(artist->node.next);
194 iter->data2 = NULL;
195 return 1;
197 /* }}} */
199 static GENERIC_ITER_PREV(tree_track_get_prev, struct tree_track, node)
200 static GENERIC_ITER_NEXT(tree_track_get_next, struct tree_track, node)
202 static inline void tree_search_track_to_iter(struct tree_track *track, struct iter *iter)
204 iter->data0 = &lib_artist_head;
205 iter->data1 = track;
206 iter->data2 = NULL;
209 static inline void album_to_iter(struct album *album, struct iter *iter)
211 iter->data0 = &lib_artist_head;
212 iter->data1 = album->artist;
213 iter->data2 = album;
216 static inline void artist_to_iter(struct artist *artist, struct iter *iter)
218 iter->data0 = &lib_artist_head;
219 iter->data1 = artist;
220 iter->data2 = NULL;
223 static inline void tree_track_to_iter(struct tree_track *track, struct iter *iter)
225 iter->data0 = &track->album->track_head;
226 iter->data1 = track;
227 iter->data2 = NULL;
230 /* search (tree) {{{ */
231 static int tree_search_get_current(void *data, struct iter *iter)
233 struct artist *artist;
234 struct album *album;
235 struct tree_track *track;
236 struct iter tmpiter;
238 if (list_empty(&lib_artist_head))
239 return 0;
240 if (window_get_sel(lib_track_win, &tmpiter)) {
241 track = iter_to_tree_track(&tmpiter);
242 tree_search_track_to_iter(track, iter);
243 return 1;
246 /* artist not expanded. track_win is empty
247 * set tmp to the first track of the selected artist */
248 window_get_sel(lib_tree_win, &tmpiter);
249 artist = iter_to_artist(&tmpiter);
250 album = to_album(artist->album_head.next);
251 track = to_tree_track(album->track_head.next);
252 tree_search_track_to_iter(track, iter);
253 return 1;
256 static inline struct tree_track *iter_to_tree_search_track(const struct iter *iter)
258 BUG_ON(iter->data0 != &lib_artist_head);
259 return iter->data1;
262 static int tree_search_matches(void *data, struct iter *iter, const char *text)
264 struct tree_track *track;
265 struct iter tmpiter;
266 unsigned int flags = TI_MATCH_ARTIST | TI_MATCH_ALBUM;
268 if (!search_restricted)
269 flags |= TI_MATCH_TITLE;
270 track = iter_to_tree_search_track(iter);
271 if (!track_info_matches(tree_track_info(track), text, flags))
272 return 0;
273 track->album->artist->expanded = 1;
274 album_to_iter(track->album, &tmpiter);
275 window_set_sel(lib_tree_win, &tmpiter);
277 tree_track_to_iter(track, &tmpiter);
278 window_set_sel(lib_track_win, &tmpiter);
279 return 1;
282 static const struct searchable_ops tree_search_ops = {
283 .get_prev = tree_search_get_prev,
284 .get_next = tree_search_get_next,
285 .get_current = tree_search_get_current,
286 .matches = tree_search_matches
288 /* search (tree) }}} */
290 static inline int album_selected(struct album *album)
292 struct iter sel;
294 if (window_get_sel(lib_tree_win, &sel))
295 return album == iter_to_album(&sel);
296 return 0;
299 static void tree_sel_changed(void)
301 struct iter sel;
302 struct album *album;
304 window_get_sel(lib_tree_win, &sel);
305 album = iter_to_album(&sel);
306 if (album == NULL) {
307 window_set_empty(lib_track_win);
308 } else {
309 window_set_contents(lib_track_win, &album->track_head);
313 static inline void tree_win_get_selected(struct artist **artist, struct album **album)
315 struct iter sel;
317 *artist = NULL;
318 *album = NULL;
319 if (window_get_sel(lib_tree_win, &sel)) {
320 *artist = iter_to_artist(&sel);
321 *album = iter_to_album(&sel);
325 static void artist_free(struct artist *artist)
327 free(artist->name);
328 free(artist);
331 static void album_free(struct album *album)
333 free(album->name);
334 free(album);
337 void tree_init(void)
339 struct iter iter;
341 list_init(&lib_artist_head);
343 lib_tree_win = window_new(tree_get_prev, tree_get_next);
344 lib_track_win = window_new(tree_track_get_prev, tree_track_get_next);
345 lib_cur_win = lib_tree_win;
347 lib_tree_win->sel_changed = tree_sel_changed;
349 window_set_empty(lib_track_win);
350 window_set_contents(lib_tree_win, &lib_artist_head);
352 iter.data0 = &lib_artist_head;
353 iter.data1 = NULL;
354 iter.data2 = NULL;
355 tree_searchable = searchable_new(NULL, &iter, &tree_search_ops);
358 struct track_info *tree_set_selected(void)
360 struct artist *artist;
361 struct album *album;
362 struct track_info *info;
363 struct iter sel;
365 if (list_empty(&lib_artist_head))
366 return NULL;
368 tree_win_get_selected(&artist, &album);
369 if (album == NULL) {
370 /* only artist selected, track window is empty
371 * => get first album of the selected artist and first track of that album
373 album = to_album(artist->album_head.next);
374 lib_cur_track = to_tree_track(album->track_head.next);
375 } else {
376 window_get_sel(lib_track_win, &sel);
377 lib_cur_track = iter_to_tree_track(&sel);
380 lib_tree_win->changed = 1;
381 lib_track_win->changed = 1;
383 info = tree_track_info(lib_cur_track);
384 track_info_ref(info);
385 return info;
388 static void find_artist_and_album(const char *artist_name,
389 const char *album_name, struct artist **_artist,
390 struct album **_album)
392 struct artist *artist;
393 struct album *album;
395 list_for_each_entry(artist, &lib_artist_head, node) {
396 int res;
398 res = u_strcasecmp(artist->name, artist_name);
399 if (res == 0) {
400 *_artist = artist;
401 list_for_each_entry(album, &artist->album_head, node) {
402 res = u_strcasecmp(album->name, album_name);
403 if (res == 0) {
404 *_album = album;
405 return;
408 *_album = NULL;
409 return;
412 *_artist = NULL;
413 *_album = NULL;
414 return;
417 static int special_name_cmp(const char *a, const char *b)
419 /* keep <Stream> etc. top */
420 int cmp = (*a != '<') - (*b != '<');
422 if (cmp)
423 return cmp;
424 return u_strcasecmp(a, b);
427 static struct artist *add_artist(const char *name)
429 struct list_head *item;
430 struct artist *artist;
432 artist = xnew(struct artist, 1);
433 artist->name = xstrdup(name);
434 list_init(&artist->album_head);
435 artist->expanded = 0;
436 list_for_each(item, &lib_artist_head) {
437 if (special_name_cmp(name, to_artist(item)->name) < 0)
438 break;
440 /* add before item */
441 list_add_tail(&artist->node, item);
442 return artist;
445 static struct album *artist_add_album(struct artist *artist, const char *name, int date)
447 struct list_head *item;
448 struct album *album;
450 album = xnew(struct album, 1);
451 album->name = xstrdup(name);
452 album->date = date;
453 list_init(&album->track_head);
454 album->artist = artist;
455 list_for_each(item, &artist->album_head) {
456 struct album *a = to_album(item);
458 if (date < a->date)
459 break;
460 if (date > a->date)
461 continue;
462 if (special_name_cmp(name, a->name) < 0)
463 break;
465 /* add before item */
466 list_add_tail(&album->node, item);
467 return album;
470 static void album_add_track(struct album *album, struct tree_track *track)
473 * NOTE: This is not perfect. You should ignore track numbers if
474 * either is unset and use filename instead, but usually you
475 * have all track numbers set or all unset (within one album
476 * of course).
478 static const char * const album_track_sort_keys[] = {
479 "discnumber", "tracknumber", "filename", NULL
481 struct list_head *item;
483 track->album = album;
484 list_for_each(item, &album->track_head) {
485 const struct simple_track *a = (const struct simple_track *)track;
486 const struct simple_track *b = (const struct simple_track *)to_tree_track(item);
488 if (track_info_cmp(a->info, b->info, album_track_sort_keys) < 0)
489 break;
491 /* add before item */
492 list_add_tail(&track->node, item);
495 void tree_add_track(struct tree_track *track)
497 const struct track_info *ti = tree_track_info(track);
498 const char *album_name, *artist_name, *albumartist,
499 *artist_sort, *albumartist_sort;
500 struct artist *artist;
501 struct album *album;
502 int date;
504 album_name = keyvals_get_val(ti->comments, "album");
505 artist_name = keyvals_get_val(ti->comments, "artist");
506 albumartist = keyvals_get_val(ti->comments, "albumartist");
507 artist_sort = keyvals_get_val(ti->comments, "artistsort");
508 albumartist_sort = keyvals_get_val(ti->comments, "albumartistsort");
510 if (is_url(ti->filename)) {
511 artist_name = "<Stream>";
512 album_name = "<Stream>";
515 if (artist_name == NULL)
516 artist_name = "<No Name>";
517 if (album_name == NULL)
518 album_name = "<No Name>";
520 if (albumartist_sort)
521 artist_name = albumartist_sort;
522 else if (albumartist)
523 artist_name = albumartist;
524 else if (artist_sort)
525 artist_name = artist_sort;
526 else {
527 const char *compilation = keyvals_get_val(ti->comments, "compilation");
529 if (compilation && (!strcasecmp(compilation, "1")
530 || !strcasecmp(compilation, "yes"))) {
531 /* Store all compilations under compilations */
532 artist_name = "<Compilations>";
536 find_artist_and_album(artist_name, album_name, &artist, &album);
537 if (album) {
538 album_add_track(album, track);
540 /* is the album where we added the track selected? */
541 if (album_selected(album)) {
542 /* update track window */
543 window_changed(lib_track_win);
545 } else if (artist) {
546 date = comments_get_date(ti->comments, "date");
547 album = artist_add_album(artist, album_name, date);
548 album_add_track(album, track);
550 if (artist->expanded) {
551 /* update tree window */
552 window_changed(lib_tree_win);
553 /* album is not selected => no need to update track_win */
555 } else {
556 date = comments_get_date(ti->comments, "date");
557 artist = add_artist(artist_name);
558 album = artist_add_album(artist, album_name, date);
559 album_add_track(album, track);
561 window_changed(lib_tree_win);
565 static void remove_sel_artist(struct artist *artist)
567 struct list_head *aitem, *ahead;
569 ahead = &artist->album_head;
570 aitem = ahead->next;
571 while (aitem != ahead) {
572 struct list_head *titem, *thead;
573 struct list_head *anext = aitem->next;
574 struct album *album = to_album(aitem);
576 thead = &album->track_head;
577 titem = thead->next;
578 while (titem != thead) {
579 struct list_head *tnext = titem->next;
580 struct tree_track *track = to_tree_track(titem);
582 editable_remove_track(&lib_editable, (struct simple_track *)track);
583 titem = tnext;
585 /* all tracks removed => album removed
586 * if the last album was removed then the artist was removed too
588 aitem = anext;
592 static void remove_sel_album(struct album *album)
594 struct list_head *item, *head;
596 head = &album->track_head;
597 item = head->next;
598 while (item != head) {
599 struct list_head *next = item->next;
600 struct tree_track *track = to_tree_track(item);
602 editable_remove_track(&lib_editable, (struct simple_track *)track);
603 item = next;
607 static void tree_win_remove_sel(void)
609 struct artist *artist;
610 struct album *album;
612 tree_win_get_selected(&artist, &album);
613 if (album) {
614 remove_sel_album(album);
615 } else if (artist) {
616 remove_sel_artist(artist);
620 static void track_win_remove_sel(void)
622 struct iter sel;
623 struct tree_track *track;
625 if (window_get_sel(lib_track_win, &sel)) {
626 track = iter_to_tree_track(&sel);
627 BUG_ON(track == NULL);
628 editable_remove_track(&lib_editable, (struct simple_track *)track);
632 void tree_toggle_active_window(void)
634 if (lib_cur_win == lib_tree_win) {
635 struct artist *artist;
636 struct album *album;
638 tree_win_get_selected(&artist, &album);
639 if (album) {
640 lib_cur_win = lib_track_win;
641 lib_tree_win->changed = 1;
642 lib_track_win->changed = 1;
644 } else if (lib_cur_win == lib_track_win) {
645 lib_cur_win = lib_tree_win;
646 lib_tree_win->changed = 1;
647 lib_track_win->changed = 1;
651 void tree_toggle_expand_artist(void)
653 struct iter sel;
654 struct artist *artist;
656 window_get_sel(lib_tree_win, &sel);
657 artist = iter_to_artist(&sel);
658 if (artist) {
659 if (artist->expanded) {
660 /* deselect album, select artist */
661 artist_to_iter(artist, &sel);
662 window_set_sel(lib_tree_win, &sel);
664 artist->expanded = 0;
665 lib_cur_win = lib_tree_win;
666 } else {
667 artist->expanded = 1;
669 window_changed(lib_tree_win);
673 static void remove_track(struct tree_track *track)
675 if (album_selected(track->album)) {
676 struct iter iter;
678 tree_track_to_iter(track, &iter);
679 window_row_vanishes(lib_track_win, &iter);
681 list_del(&track->node);
684 static void remove_album(struct album *album)
686 if (album->artist->expanded) {
687 struct iter iter;
689 album_to_iter(album, &iter);
690 window_row_vanishes(lib_tree_win, &iter);
692 list_del(&album->node);
695 static void remove_artist(struct artist *artist)
697 struct iter iter;
699 artist_to_iter(artist, &iter);
700 window_row_vanishes(lib_tree_win, &iter);
701 list_del(&artist->node);
704 void tree_remove(struct tree_track *track)
706 struct album *album = track->album;
707 struct artist *sel_artist;
708 struct album *sel_album;
710 tree_win_get_selected(&sel_artist, &sel_album);
712 remove_track(track);
713 /* don't free the track */
715 if (list_empty(&album->track_head)) {
716 struct artist *artist = album->artist;
718 if (sel_album == album)
719 lib_cur_win = lib_tree_win;
721 remove_album(album);
722 album_free(album);
724 if (list_empty(&artist->album_head)) {
725 artist->expanded = 0;
726 remove_artist(artist);
727 artist_free(artist);
732 void tree_remove_sel(void)
734 if (lib_cur_win == lib_tree_win) {
735 tree_win_remove_sel();
736 } else {
737 track_win_remove_sel();
741 void tree_sel_current(void)
743 if (lib_cur_track) {
744 struct iter iter;
746 CUR_ARTIST->expanded = 1;
748 if (lib_cur_win == lib_tree_win) {
749 lib_cur_win = lib_track_win;
750 lib_tree_win->changed = 1;
751 lib_track_win->changed = 1;
754 album_to_iter(CUR_ALBUM, &iter);
755 window_set_sel(lib_tree_win, &iter);
757 tree_track_to_iter(lib_cur_track, &iter);
758 window_set_sel(lib_track_win, &iter);
762 static int album_for_each_track(struct album *album, int (*cb)(void *data, struct track_info *ti),
763 void *data, int reverse)
765 struct tree_track *track;
766 int rc = 0;
768 if (reverse) {
769 list_for_each_entry_reverse(track, &album->track_head, node) {
770 rc = cb(data, tree_track_info(track));
771 if (rc)
772 break;
774 } else {
775 list_for_each_entry(track, &album->track_head, node) {
776 rc = cb(data, tree_track_info(track));
777 if (rc)
778 break;
781 return rc;
784 static int artist_for_each_track(struct artist *artist, int (*cb)(void *data, struct track_info *ti),
785 void *data, int reverse)
787 struct album *album;
788 int rc = 0;
790 if (reverse) {
791 list_for_each_entry_reverse(album, &artist->album_head, node) {
792 rc = album_for_each_track(album, cb, data, 1);
793 if (rc)
794 break;
796 } else {
797 list_for_each_entry(album, &artist->album_head, node) {
798 rc = album_for_each_track(album, cb, data, 0);
799 if (rc)
800 break;
803 return rc;
806 int __tree_for_each_sel(int (*cb)(void *data, struct track_info *ti), void *data, int reverse)
808 int rc = 0;
810 if (lib_cur_win == lib_tree_win) {
811 struct artist *artist;
812 struct album *album;
814 tree_win_get_selected(&artist, &album);
815 if (artist) {
816 if (album == NULL) {
817 rc = artist_for_each_track(artist, cb, data, reverse);
818 } else {
819 rc = album_for_each_track(album, cb, data, reverse);
822 } else {
823 struct iter sel;
824 struct tree_track *track;
826 if (window_get_sel(lib_track_win, &sel)) {
827 track = iter_to_tree_track(&sel);
828 rc = cb(data, tree_track_info(track));
831 return rc;
834 int tree_for_each_sel(int (*cb)(void *data, struct track_info *ti), void *data, int reverse)
836 int rc = __tree_for_each_sel(cb, data, reverse);
838 window_down(lib_cur_win, 1);
839 return rc;