flac: Saner EOF handling
[cmus.git] / tree.c
blobd7ca73bb2fce05f5c4666de2f784bf158aea3e3b
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"
11 #include "mergesort.h"
12 #include "options.h"
14 struct searchable *tree_searchable;
15 struct window *lib_tree_win;
16 struct window *lib_track_win;
17 struct window *lib_cur_win;
18 LIST_HEAD(lib_artist_head);
20 /* tree (search) iterators {{{ */
21 static int tree_search_get_prev(struct iter *iter)
23 struct list_head *head = iter->data0;
24 struct tree_track *track = iter->data1;
25 struct artist *artist;
26 struct album *album;
28 BUG_ON(iter->data2);
29 if (head == NULL)
30 return 0;
31 if (track == NULL) {
32 /* head, get last track */
33 if (head->prev == head) {
34 /* empty, iter points to the head already */
35 return 0;
37 artist = to_artist(head->prev);
38 album = to_album(artist->album_head.prev);
39 iter->data1 = to_tree_track(album->track_head.prev);
40 return 1;
42 /* prev track */
43 if (track->node.prev == &track->album->track_head || search_restricted) {
44 /* prev album */
45 if (track->album->node.prev == &track->album->artist->album_head) {
46 /* prev artist */
47 if (track->album->artist->node.prev == &lib_artist_head)
48 return 0;
49 artist = to_artist(track->album->artist->node.prev);
50 album = to_album(artist->album_head.prev);
51 track = to_tree_track(album->track_head.prev);
52 } else {
53 album = to_album(track->album->node.prev);
54 track = to_tree_track(album->track_head.prev);
56 } else {
57 track = to_tree_track(track->node.prev);
59 iter->data1 = track;
60 return 1;
63 static int tree_search_get_next(struct iter *iter)
65 struct list_head *head = iter->data0;
66 struct tree_track *track = iter->data1;
67 struct artist *artist;
68 struct album *album;
70 BUG_ON(iter->data2);
71 if (head == NULL)
72 return 0;
73 if (track == NULL) {
74 /* head, get first track */
75 if (head->next == head) {
76 /* empty, iter points to the head already */
77 return 0;
79 artist = to_artist(head->next);
80 album = to_album(artist->album_head.next);
81 iter->data1 = to_tree_track(album->track_head.next);
82 return 1;
84 /* next track */
85 if (track->node.next == &track->album->track_head || search_restricted) {
86 /* next album */
87 if (track->album->node.next == &track->album->artist->album_head) {
88 /* next artist */
89 if (track->album->artist->node.next == &lib_artist_head)
90 return 0;
91 artist = to_artist(track->album->artist->node.next);
92 album = to_album(artist->album_head.next);
93 track = to_tree_track(album->track_head.next);
94 } else {
95 album = to_album(track->album->node.next);
96 track = to_tree_track(album->track_head.next);
98 } else {
99 track = to_tree_track(track->node.next);
101 iter->data1 = track;
102 return 1;
104 /* }}} */
106 /* tree window iterators {{{ */
107 static int tree_get_prev(struct iter *iter)
109 struct list_head *head = iter->data0;
110 struct artist *artist = iter->data1;
111 struct album *album = iter->data2;
113 BUG_ON(head == NULL);
114 BUG_ON(artist == NULL && album != NULL);
115 if (artist == NULL) {
116 /* head, get last artist and/or album */
117 if (head->prev == head) {
118 /* empty, iter points to the head already */
119 return 0;
121 artist = to_artist(head->prev);
122 if (artist->expanded) {
123 album = to_album(artist->album_head.prev);
124 } else {
125 album = NULL;
127 iter->data1 = artist;
128 iter->data2 = album;
129 return 1;
131 if (artist->expanded && album) {
132 /* prev album */
133 if (album->node.prev == &artist->album_head) {
134 iter->data2 = NULL;
135 return 1;
136 } else {
137 iter->data2 = to_album(album->node.prev);
138 return 1;
142 /* prev artist */
143 if (artist->node.prev == &lib_artist_head) {
144 iter->data1 = NULL;
145 iter->data2 = NULL;
146 return 0;
148 artist = to_artist(artist->node.prev);
149 iter->data1 = artist;
150 iter->data2 = NULL;
151 if (artist->expanded) {
152 /* last album */
153 iter->data2 = to_album(artist->album_head.prev);
155 return 1;
158 static int tree_get_next(struct iter *iter)
160 struct list_head *head = iter->data0;
161 struct artist *artist = iter->data1;
162 struct album *album = iter->data2;
164 BUG_ON(head == NULL);
165 BUG_ON(artist == NULL && album != NULL);
166 if (artist == NULL) {
167 /* head, get first artist */
168 if (head->next == head) {
169 /* empty, iter points to the head already */
170 return 0;
172 iter->data1 = to_artist(head->next);
173 iter->data2 = NULL;
174 return 1;
176 if (artist->expanded) {
177 /* next album */
178 if (album == NULL) {
179 /* first album */
180 iter->data2 = to_album(artist->album_head.next);
181 return 1;
183 if (album->node.next != &artist->album_head) {
184 iter->data2 = to_album(album->node.next);
185 return 1;
189 /* next artist */
190 if (artist->node.next == head) {
191 iter->data1 = NULL;
192 iter->data2 = NULL;
193 return 0;
195 iter->data1 = to_artist(artist->node.next);
196 iter->data2 = NULL;
197 return 1;
199 /* }}} */
201 static GENERIC_ITER_PREV(tree_track_get_prev, struct tree_track, node)
202 static GENERIC_ITER_NEXT(tree_track_get_next, struct tree_track, node)
204 static inline void tree_search_track_to_iter(struct tree_track *track, struct iter *iter)
206 iter->data0 = &lib_artist_head;
207 iter->data1 = track;
208 iter->data2 = NULL;
211 static inline void album_to_iter(struct album *album, struct iter *iter)
213 iter->data0 = &lib_artist_head;
214 iter->data1 = album->artist;
215 iter->data2 = album;
218 static inline void artist_to_iter(struct artist *artist, struct iter *iter)
220 iter->data0 = &lib_artist_head;
221 iter->data1 = artist;
222 iter->data2 = NULL;
225 static inline void tree_track_to_iter(struct tree_track *track, struct iter *iter)
227 iter->data0 = &track->album->track_head;
228 iter->data1 = track;
229 iter->data2 = NULL;
232 /* search (tree) {{{ */
233 static int tree_search_get_current(void *data, struct iter *iter)
235 struct artist *artist;
236 struct album *album;
237 struct tree_track *track;
238 struct iter tmpiter;
240 if (list_empty(&lib_artist_head))
241 return 0;
242 if (window_get_sel(lib_track_win, &tmpiter)) {
243 track = iter_to_tree_track(&tmpiter);
244 tree_search_track_to_iter(track, iter);
245 return 1;
248 /* artist not expanded. track_win is empty
249 * set tmp to the first track of the selected artist */
250 window_get_sel(lib_tree_win, &tmpiter);
251 artist = iter_to_artist(&tmpiter);
252 album = to_album(artist->album_head.next);
253 track = to_tree_track(album->track_head.next);
254 tree_search_track_to_iter(track, iter);
255 return 1;
258 static inline struct tree_track *iter_to_tree_search_track(const struct iter *iter)
260 BUG_ON(iter->data0 != &lib_artist_head);
261 return iter->data1;
264 static int tree_search_matches(void *data, struct iter *iter, const char *text)
266 struct tree_track *track;
267 struct iter tmpiter;
268 unsigned int flags = TI_MATCH_ARTIST | TI_MATCH_ALBUM;
270 if (!search_restricted)
271 flags |= TI_MATCH_TITLE;
272 track = iter_to_tree_search_track(iter);
273 if (!track_info_matches(tree_track_info(track), text, flags))
274 return 0;
275 track->album->artist->expanded = 1;
276 album_to_iter(track->album, &tmpiter);
277 window_set_sel(lib_tree_win, &tmpiter);
279 tree_track_to_iter(track, &tmpiter);
280 window_set_sel(lib_track_win, &tmpiter);
281 return 1;
284 static const struct searchable_ops tree_search_ops = {
285 .get_prev = tree_search_get_prev,
286 .get_next = tree_search_get_next,
287 .get_current = tree_search_get_current,
288 .matches = tree_search_matches
290 /* search (tree) }}} */
292 static inline int album_selected(struct album *album)
294 struct iter sel;
296 if (window_get_sel(lib_tree_win, &sel))
297 return album == iter_to_album(&sel);
298 return 0;
301 static void tree_sel_changed(void)
303 struct iter sel;
304 struct album *album;
306 window_get_sel(lib_tree_win, &sel);
307 album = iter_to_album(&sel);
308 if (album == NULL) {
309 window_set_empty(lib_track_win);
310 } else {
311 window_set_contents(lib_track_win, &album->track_head);
315 static inline void tree_win_get_selected(struct artist **artist, struct album **album)
317 struct iter sel;
319 *artist = NULL;
320 *album = NULL;
321 if (window_get_sel(lib_tree_win, &sel)) {
322 *artist = iter_to_artist(&sel);
323 *album = iter_to_album(&sel);
327 static void artist_free(struct artist *artist)
329 free(artist->name);
330 free(artist);
333 static void album_free(struct album *album)
335 free(album->name);
336 free(album);
339 void tree_init(void)
341 struct iter iter;
343 list_init(&lib_artist_head);
345 lib_tree_win = window_new(tree_get_prev, tree_get_next);
346 lib_track_win = window_new(tree_track_get_prev, tree_track_get_next);
347 lib_cur_win = lib_tree_win;
349 lib_tree_win->sel_changed = tree_sel_changed;
351 window_set_empty(lib_track_win);
352 window_set_contents(lib_tree_win, &lib_artist_head);
354 iter.data0 = &lib_artist_head;
355 iter.data1 = NULL;
356 iter.data2 = NULL;
357 tree_searchable = searchable_new(NULL, &iter, &tree_search_ops);
360 struct track_info *tree_set_selected(void)
362 struct artist *artist;
363 struct album *album;
364 struct track_info *info;
365 struct iter sel;
367 if (list_empty(&lib_artist_head))
368 return NULL;
370 tree_win_get_selected(&artist, &album);
371 if (album == NULL) {
372 /* only artist selected, track window is empty
373 * => get first album of the selected artist and first track of that album
375 album = to_album(artist->album_head.next);
376 lib_cur_track = to_tree_track(album->track_head.next);
377 } else {
378 window_get_sel(lib_track_win, &sel);
379 lib_cur_track = iter_to_tree_track(&sel);
382 lib_tree_win->changed = 1;
383 lib_track_win->changed = 1;
385 info = tree_track_info(lib_cur_track);
386 track_info_ref(info);
387 return info;
390 static const char *artist_name_skip_the(const char *a)
392 if (!strncasecmp(a, "the ", 4)) {
393 a += 4;
394 while (*a == ' ' || *a == '\t')
395 ++a;
397 return a;
400 static void find_artist_and_album(const char *artist_name,
401 const char *album_name, struct artist **_artist,
402 struct album **_album)
404 struct artist *artist;
405 struct album *album;
407 list_for_each_entry(artist, &lib_artist_head, node) {
408 int res;
410 res = u_strcasecmp(artist->name, artist_name);
411 if (res == 0) {
412 *_artist = artist;
413 list_for_each_entry(album, &artist->album_head, node) {
414 res = u_strcasecmp(album->name, album_name);
415 if (res == 0) {
416 *_album = album;
417 return;
420 *_album = NULL;
421 return;
424 *_artist = NULL;
425 *_album = NULL;
426 return;
429 static int special_name_cmp(const char *a, const char *b)
431 /* keep <Stream> etc. top */
432 int cmp = (*a != '<') - (*b != '<');
434 if (cmp)
435 return cmp;
436 return u_strcasecmp(a, b);
439 static void insert_artist(struct artist *artist)
441 const char *a = artist->name;
442 struct list_head *item;
444 if (fuzzy_artist_sort)
445 a = artist_name_skip_the(a);
447 list_for_each(item, &lib_artist_head) {
448 const char *b = to_artist(item)->name;
450 if (fuzzy_artist_sort)
451 b = artist_name_skip_the(b);
453 if (special_name_cmp(a, b) < 0)
454 break;
456 /* add before item */
457 list_add_tail(&artist->node, item);
460 static int artist_cmp(const struct list_head *a, const struct list_head *b)
462 return special_name_cmp(to_artist(a)->name, to_artist(b)->name);
465 static int fuzzy_artist_cmp(const struct list_head *a, const struct list_head *b)
467 return special_name_cmp(artist_name_skip_the(to_artist(a)->name),
468 artist_name_skip_the(to_artist(b)->name));
471 void tree_sort_artists(void)
473 if (fuzzy_artist_sort)
474 list_mergesort(&lib_artist_head, fuzzy_artist_cmp);
475 else
476 list_mergesort(&lib_artist_head, artist_cmp);
477 window_changed(lib_tree_win);
480 static struct artist *add_artist(const char *name)
482 struct artist *artist;
484 artist = xnew(struct artist, 1);
485 artist->name = xstrdup(name);
486 list_init(&artist->album_head);
487 artist->expanded = 0;
489 insert_artist(artist);
490 return artist;
493 static struct album *artist_add_album(struct artist *artist, const char *name, int date)
495 struct list_head *item;
496 struct album *album;
498 album = xnew(struct album, 1);
499 album->name = xstrdup(name);
500 album->date = date;
501 list_init(&album->track_head);
502 album->artist = artist;
504 list_for_each(item, &artist->album_head) {
505 struct album *a = to_album(item);
507 if (date < a->date)
508 break;
509 if (date > a->date)
510 continue;
511 if (special_name_cmp(name, a->name) < 0)
512 break;
514 /* add before item */
515 list_add_tail(&album->node, item);
516 return album;
519 static void album_add_track(struct album *album, struct tree_track *track)
522 * NOTE: This is not perfect. You should ignore track numbers if
523 * either is unset and use filename instead, but usually you
524 * have all track numbers set or all unset (within one album
525 * of course).
527 static const char * const album_track_sort_keys[] = {
528 "discnumber", "tracknumber", "filename", NULL
530 struct list_head *item;
532 track->album = album;
533 list_for_each(item, &album->track_head) {
534 const struct simple_track *a = (const struct simple_track *)track;
535 const struct simple_track *b = (const struct simple_track *)to_tree_track(item);
537 if (track_info_cmp(a->info, b->info, album_track_sort_keys) < 0)
538 break;
540 /* add before item */
541 list_add_tail(&track->node, item);
544 void tree_add_track(struct tree_track *track)
546 const struct track_info *ti = tree_track_info(track);
547 const char *album_name, *artist_name;
548 struct artist *artist;
549 struct album *album;
550 int date;
552 if (is_url(ti->filename)) {
553 artist_name = "<Stream>";
554 album_name = "<Stream>";
555 } else {
556 album_name = keyvals_get_val(ti->comments, "album");
558 artist_name = keyvals_get_val(ti->comments, "albumartistsort");
559 if (!artist_name)
560 artist_name= keyvals_get_val(ti->comments, "albumartist");
561 if (!artist_name)
562 artist_name= keyvals_get_val(ti->comments, "artistsort");
563 if (!artist_name) {
564 const char *compilation = keyvals_get_val(ti->comments, "compilation");
565 if (compilation && (!strcasecmp(compilation, "1") ||
566 !strcasecmp(compilation, "yes")))
567 artist_name = "<Compilations>";
569 if (!artist_name)
570 artist_name = keyvals_get_val(ti->comments, "artist");
572 if (artist_name == NULL)
573 artist_name = "<No Name>";
574 if (album_name == NULL)
575 album_name = "<No Name>";
579 find_artist_and_album(artist_name, album_name, &artist, &album);
580 if (album) {
581 album_add_track(album, track);
583 /* is the album where we added the track selected? */
584 if (album_selected(album)) {
585 /* update track window */
586 window_changed(lib_track_win);
588 } else if (artist) {
589 date = comments_get_date(ti->comments, "date");
590 album = artist_add_album(artist, album_name, date);
591 album_add_track(album, track);
593 if (artist->expanded) {
594 /* update tree window */
595 window_changed(lib_tree_win);
596 /* album is not selected => no need to update track_win */
598 } else {
599 date = comments_get_date(ti->comments, "date");
600 artist = add_artist(artist_name);
601 album = artist_add_album(artist, album_name, date);
602 album_add_track(album, track);
604 window_changed(lib_tree_win);
608 static void remove_sel_artist(struct artist *artist)
610 struct list_head *aitem, *ahead;
612 ahead = &artist->album_head;
613 aitem = ahead->next;
614 while (aitem != ahead) {
615 struct list_head *titem, *thead;
616 struct list_head *anext = aitem->next;
617 struct album *album = to_album(aitem);
619 thead = &album->track_head;
620 titem = thead->next;
621 while (titem != thead) {
622 struct list_head *tnext = titem->next;
623 struct tree_track *track = to_tree_track(titem);
625 editable_remove_track(&lib_editable, (struct simple_track *)track);
626 titem = tnext;
628 /* all tracks removed => album removed
629 * if the last album was removed then the artist was removed too
631 aitem = anext;
635 static void remove_sel_album(struct album *album)
637 struct list_head *item, *head;
639 head = &album->track_head;
640 item = head->next;
641 while (item != head) {
642 struct list_head *next = item->next;
643 struct tree_track *track = to_tree_track(item);
645 editable_remove_track(&lib_editable, (struct simple_track *)track);
646 item = next;
650 static void tree_win_remove_sel(void)
652 struct artist *artist;
653 struct album *album;
655 tree_win_get_selected(&artist, &album);
656 if (album) {
657 remove_sel_album(album);
658 } else if (artist) {
659 remove_sel_artist(artist);
663 static void track_win_remove_sel(void)
665 struct iter sel;
666 struct tree_track *track;
668 if (window_get_sel(lib_track_win, &sel)) {
669 track = iter_to_tree_track(&sel);
670 BUG_ON(track == NULL);
671 editable_remove_track(&lib_editable, (struct simple_track *)track);
675 void tree_toggle_active_window(void)
677 if (lib_cur_win == lib_tree_win) {
678 struct artist *artist;
679 struct album *album;
681 tree_win_get_selected(&artist, &album);
682 if (album) {
683 lib_cur_win = lib_track_win;
684 lib_tree_win->changed = 1;
685 lib_track_win->changed = 1;
687 } else if (lib_cur_win == lib_track_win) {
688 lib_cur_win = lib_tree_win;
689 lib_tree_win->changed = 1;
690 lib_track_win->changed = 1;
694 void tree_toggle_expand_artist(void)
696 struct iter sel;
697 struct artist *artist;
699 window_get_sel(lib_tree_win, &sel);
700 artist = iter_to_artist(&sel);
701 if (artist) {
702 if (artist->expanded) {
703 /* deselect album, select artist */
704 artist_to_iter(artist, &sel);
705 window_set_sel(lib_tree_win, &sel);
707 artist->expanded = 0;
708 lib_cur_win = lib_tree_win;
709 } else {
710 artist->expanded = 1;
712 window_changed(lib_tree_win);
716 static void remove_track(struct tree_track *track)
718 if (album_selected(track->album)) {
719 struct iter iter;
721 tree_track_to_iter(track, &iter);
722 window_row_vanishes(lib_track_win, &iter);
724 list_del(&track->node);
727 static void remove_album(struct album *album)
729 if (album->artist->expanded) {
730 struct iter iter;
732 album_to_iter(album, &iter);
733 window_row_vanishes(lib_tree_win, &iter);
735 list_del(&album->node);
738 static void remove_artist(struct artist *artist)
740 struct iter iter;
742 artist_to_iter(artist, &iter);
743 window_row_vanishes(lib_tree_win, &iter);
744 list_del(&artist->node);
747 void tree_remove(struct tree_track *track)
749 struct album *album = track->album;
750 struct artist *sel_artist;
751 struct album *sel_album;
753 tree_win_get_selected(&sel_artist, &sel_album);
755 remove_track(track);
756 /* don't free the track */
758 if (list_empty(&album->track_head)) {
759 struct artist *artist = album->artist;
761 if (sel_album == album)
762 lib_cur_win = lib_tree_win;
764 remove_album(album);
765 album_free(album);
767 if (list_empty(&artist->album_head)) {
768 artist->expanded = 0;
769 remove_artist(artist);
770 artist_free(artist);
775 void tree_remove_sel(void)
777 if (lib_cur_win == lib_tree_win) {
778 tree_win_remove_sel();
779 } else {
780 track_win_remove_sel();
784 void tree_sel_current(void)
786 if (lib_cur_track) {
787 struct iter iter;
789 CUR_ARTIST->expanded = 1;
791 if (lib_cur_win == lib_tree_win) {
792 lib_cur_win = lib_track_win;
793 lib_tree_win->changed = 1;
794 lib_track_win->changed = 1;
797 album_to_iter(CUR_ALBUM, &iter);
798 window_set_sel(lib_tree_win, &iter);
800 tree_track_to_iter(lib_cur_track, &iter);
801 window_set_sel(lib_track_win, &iter);
805 static int album_for_each_track(struct album *album, int (*cb)(void *data, struct track_info *ti),
806 void *data, int reverse)
808 struct tree_track *track;
809 int rc = 0;
811 if (reverse) {
812 list_for_each_entry_reverse(track, &album->track_head, node) {
813 rc = cb(data, tree_track_info(track));
814 if (rc)
815 break;
817 } else {
818 list_for_each_entry(track, &album->track_head, node) {
819 rc = cb(data, tree_track_info(track));
820 if (rc)
821 break;
824 return rc;
827 static int artist_for_each_track(struct artist *artist, int (*cb)(void *data, struct track_info *ti),
828 void *data, int reverse)
830 struct album *album;
831 int rc = 0;
833 if (reverse) {
834 list_for_each_entry_reverse(album, &artist->album_head, node) {
835 rc = album_for_each_track(album, cb, data, 1);
836 if (rc)
837 break;
839 } else {
840 list_for_each_entry(album, &artist->album_head, node) {
841 rc = album_for_each_track(album, cb, data, 0);
842 if (rc)
843 break;
846 return rc;
849 int __tree_for_each_sel(int (*cb)(void *data, struct track_info *ti), void *data, int reverse)
851 int rc = 0;
853 if (lib_cur_win == lib_tree_win) {
854 struct artist *artist;
855 struct album *album;
857 tree_win_get_selected(&artist, &album);
858 if (artist) {
859 if (album == NULL) {
860 rc = artist_for_each_track(artist, cb, data, reverse);
861 } else {
862 rc = album_for_each_track(album, cb, data, reverse);
865 } else {
866 struct iter sel;
867 struct tree_track *track;
869 if (window_get_sel(lib_track_win, &sel)) {
870 track = iter_to_tree_track(&sel);
871 rc = cb(data, tree_track_info(track));
874 return rc;
877 int tree_for_each_sel(int (*cb)(void *data, struct track_info *ti), void *data, int reverse)
879 int rc = __tree_for_each_sel(cb, data, reverse);
881 window_down(lib_cur_win, 1);
882 return rc;