Handle streams separately in tree_add_track()
[cmus.git] / tree.c
blob4b7359d95b8a5d5481da9af38b220a355de3b70a
1 /*
2 * Copyright 2006 Timo Hirvonen
3 */
5 #include "lib.h"
6 #include "search_mode.h"
7 #include "xmalloc.h"
8 #include "xstrjoin.h"
9 #include "comment.h"
10 #include "utils.h"
11 #include "debug.h"
13 struct searchable *tree_searchable;
14 struct window *lib_tree_win;
15 struct window *lib_track_win;
16 struct window *lib_cur_win;
17 LIST_HEAD(lib_artist_head);
19 /* tree (search) iterators {{{ */
20 static int tree_search_get_prev(struct iter *iter)
22 struct list_head *head = iter->data0;
23 struct tree_track *track = iter->data1;
24 struct artist *artist;
25 struct album *album;
27 BUG_ON(iter->data2);
28 if (head == NULL)
29 return 0;
30 if (track == NULL) {
31 /* head, get last track */
32 if (head->prev == head) {
33 /* empty, iter points to the head already */
34 return 0;
36 artist = to_artist(head->prev);
37 album = to_album(artist->album_head.prev);
38 iter->data1 = to_tree_track(album->track_head.prev);
39 return 1;
41 /* prev track */
42 if (track->node.prev == &track->album->track_head || search_restricted) {
43 /* prev album */
44 if (track->album->node.prev == &track->album->artist->album_head) {
45 /* prev artist */
46 if (track->album->artist->node.prev == &lib_artist_head)
47 return 0;
48 artist = to_artist(track->album->artist->node.prev);
49 album = to_album(artist->album_head.prev);
50 track = to_tree_track(album->track_head.prev);
51 } else {
52 album = to_album(track->album->node.prev);
53 track = to_tree_track(album->track_head.prev);
55 } else {
56 track = to_tree_track(track->node.prev);
58 iter->data1 = track;
59 return 1;
62 static int tree_search_get_next(struct iter *iter)
64 struct list_head *head = iter->data0;
65 struct tree_track *track = iter->data1;
66 struct artist *artist;
67 struct album *album;
69 BUG_ON(iter->data2);
70 if (head == NULL)
71 return 0;
72 if (track == NULL) {
73 /* head, get first track */
74 if (head->next == head) {
75 /* empty, iter points to the head already */
76 return 0;
78 artist = to_artist(head->next);
79 album = to_album(artist->album_head.next);
80 iter->data1 = to_tree_track(album->track_head.next);
81 return 1;
83 /* next track */
84 if (track->node.next == &track->album->track_head || search_restricted) {
85 /* next album */
86 if (track->album->node.next == &track->album->artist->album_head) {
87 /* next artist */
88 if (track->album->artist->node.next == &lib_artist_head)
89 return 0;
90 artist = to_artist(track->album->artist->node.next);
91 album = to_album(artist->album_head.next);
92 track = to_tree_track(album->track_head.next);
93 } else {
94 album = to_album(track->album->node.next);
95 track = to_tree_track(album->track_head.next);
97 } else {
98 track = to_tree_track(track->node.next);
100 iter->data1 = track;
101 return 1;
103 /* }}} */
105 /* tree window iterators {{{ */
106 static int tree_get_prev(struct iter *iter)
108 struct list_head *head = iter->data0;
109 struct artist *artist = iter->data1;
110 struct album *album = iter->data2;
112 BUG_ON(head == NULL);
113 BUG_ON(artist == NULL && album != NULL);
114 if (artist == NULL) {
115 /* head, get last artist and/or album */
116 if (head->prev == head) {
117 /* empty, iter points to the head already */
118 return 0;
120 artist = to_artist(head->prev);
121 if (artist->expanded) {
122 album = to_album(artist->album_head.prev);
123 } else {
124 album = NULL;
126 iter->data1 = artist;
127 iter->data2 = album;
128 return 1;
130 if (artist->expanded && album) {
131 /* prev album */
132 if (album->node.prev == &artist->album_head) {
133 iter->data2 = NULL;
134 return 1;
135 } else {
136 iter->data2 = to_album(album->node.prev);
137 return 1;
141 /* prev artist */
142 if (artist->node.prev == &lib_artist_head) {
143 iter->data1 = NULL;
144 iter->data2 = NULL;
145 return 0;
147 artist = to_artist(artist->node.prev);
148 iter->data1 = artist;
149 iter->data2 = NULL;
150 if (artist->expanded) {
151 /* last album */
152 iter->data2 = to_album(artist->album_head.prev);
154 return 1;
157 static int tree_get_next(struct iter *iter)
159 struct list_head *head = iter->data0;
160 struct artist *artist = iter->data1;
161 struct album *album = iter->data2;
163 BUG_ON(head == NULL);
164 BUG_ON(artist == NULL && album != NULL);
165 if (artist == NULL) {
166 /* head, get first artist */
167 if (head->next == head) {
168 /* empty, iter points to the head already */
169 return 0;
171 iter->data1 = to_artist(head->next);
172 iter->data2 = NULL;
173 return 1;
175 if (artist->expanded) {
176 /* next album */
177 if (album == NULL) {
178 /* first album */
179 iter->data2 = to_album(artist->album_head.next);
180 return 1;
182 if (album->node.next != &artist->album_head) {
183 iter->data2 = to_album(album->node.next);
184 return 1;
188 /* next artist */
189 if (artist->node.next == head) {
190 iter->data1 = NULL;
191 iter->data2 = NULL;
192 return 0;
194 iter->data1 = to_artist(artist->node.next);
195 iter->data2 = NULL;
196 return 1;
198 /* }}} */
200 static GENERIC_ITER_PREV(tree_track_get_prev, struct tree_track, node)
201 static GENERIC_ITER_NEXT(tree_track_get_next, struct tree_track, node)
203 static inline void tree_search_track_to_iter(struct tree_track *track, struct iter *iter)
205 iter->data0 = &lib_artist_head;
206 iter->data1 = track;
207 iter->data2 = NULL;
210 static inline void album_to_iter(struct album *album, struct iter *iter)
212 iter->data0 = &lib_artist_head;
213 iter->data1 = album->artist;
214 iter->data2 = album;
217 static inline void artist_to_iter(struct artist *artist, struct iter *iter)
219 iter->data0 = &lib_artist_head;
220 iter->data1 = artist;
221 iter->data2 = NULL;
224 static inline void tree_track_to_iter(struct tree_track *track, struct iter *iter)
226 iter->data0 = &track->album->track_head;
227 iter->data1 = track;
228 iter->data2 = NULL;
231 /* search (tree) {{{ */
232 static int tree_search_get_current(void *data, struct iter *iter)
234 struct artist *artist;
235 struct album *album;
236 struct tree_track *track;
237 struct iter tmpiter;
239 if (list_empty(&lib_artist_head))
240 return 0;
241 if (window_get_sel(lib_track_win, &tmpiter)) {
242 track = iter_to_tree_track(&tmpiter);
243 tree_search_track_to_iter(track, iter);
244 return 1;
247 /* artist not expanded. track_win is empty
248 * set tmp to the first track of the selected artist */
249 window_get_sel(lib_tree_win, &tmpiter);
250 artist = iter_to_artist(&tmpiter);
251 album = to_album(artist->album_head.next);
252 track = to_tree_track(album->track_head.next);
253 tree_search_track_to_iter(track, iter);
254 return 1;
257 static inline struct tree_track *iter_to_tree_search_track(const struct iter *iter)
259 BUG_ON(iter->data0 != &lib_artist_head);
260 return iter->data1;
263 static int tree_search_matches(void *data, struct iter *iter, const char *text)
265 struct tree_track *track;
266 struct iter tmpiter;
267 unsigned int flags = TI_MATCH_ARTIST | TI_MATCH_ALBUM;
269 if (!search_restricted)
270 flags |= TI_MATCH_TITLE;
271 track = iter_to_tree_search_track(iter);
272 if (!track_info_matches(tree_track_info(track), text, flags))
273 return 0;
274 track->album->artist->expanded = 1;
275 album_to_iter(track->album, &tmpiter);
276 window_set_sel(lib_tree_win, &tmpiter);
278 tree_track_to_iter(track, &tmpiter);
279 window_set_sel(lib_track_win, &tmpiter);
280 return 1;
283 static const struct searchable_ops tree_search_ops = {
284 .get_prev = tree_search_get_prev,
285 .get_next = tree_search_get_next,
286 .get_current = tree_search_get_current,
287 .matches = tree_search_matches
289 /* search (tree) }}} */
291 static inline int album_selected(struct album *album)
293 struct iter sel;
295 if (window_get_sel(lib_tree_win, &sel))
296 return album == iter_to_album(&sel);
297 return 0;
300 static void tree_sel_changed(void)
302 struct iter sel;
303 struct album *album;
305 window_get_sel(lib_tree_win, &sel);
306 album = iter_to_album(&sel);
307 if (album == NULL) {
308 window_set_empty(lib_track_win);
309 } else {
310 window_set_contents(lib_track_win, &album->track_head);
314 static inline void tree_win_get_selected(struct artist **artist, struct album **album)
316 struct iter sel;
318 *artist = NULL;
319 *album = NULL;
320 if (window_get_sel(lib_tree_win, &sel)) {
321 *artist = iter_to_artist(&sel);
322 *album = iter_to_album(&sel);
326 static void artist_free(struct artist *artist)
328 free(artist->raw_name);
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 int artist_name_cmp(const char *a, const char *b)
402 a = artist_name_skip_the(a);
403 b = artist_name_skip_the(b);
405 return u_strcasecmp(a, b);
408 static void find_artist_and_album(const char *artist_raw_name,
409 const char *album_name, struct artist **_artist,
410 struct album **_album)
412 struct artist *artist;
413 struct album *album;
415 list_for_each_entry(artist, &lib_artist_head, node) {
416 int res;
418 res = artist_name_cmp(artist->raw_name, artist_raw_name);
419 if (res == 0) {
420 *_artist = artist;
421 list_for_each_entry(album, &artist->album_head, node) {
422 res = u_strcasecmp(album->name, album_name);
423 if (res == 0) {
424 *_album = album;
425 return;
428 *_album = NULL;
429 return;
432 *_artist = NULL;
433 *_album = NULL;
434 return;
437 static int special_name_cmp(const char *a, const char *b)
439 /* keep <Stream> etc. top */
440 int cmp = (*a != '<') - (*b != '<');
442 if (cmp)
443 return cmp;
444 return u_strcasecmp(a, b);
447 static void insert_artist(struct artist *artist)
449 struct list_head *item;
451 list_for_each(item, &lib_artist_head) {
452 if (special_name_cmp(artist->name, to_artist(item)->name) < 0)
453 break;
455 /* add before item */
456 list_add_tail(&artist->node, item);
459 static struct artist *add_artist(const char *name, const char *raw_name)
461 struct artist *artist;
463 artist = xnew(struct artist, 1);
464 artist->name = xstrdup(name);
465 artist->raw_name = xstrdup(raw_name);
466 list_init(&artist->album_head);
467 artist->expanded = 0;
469 insert_artist(artist);
470 return artist;
473 static struct album *artist_add_album(struct artist *artist, const char *name, int date)
475 struct list_head *item;
476 struct album *album;
478 album = xnew(struct album, 1);
479 album->name = xstrdup(name);
480 album->date = date;
481 list_init(&album->track_head);
482 album->artist = artist;
484 list_for_each(item, &artist->album_head) {
485 struct album *a = to_album(item);
487 if (date < a->date)
488 break;
489 if (date > a->date)
490 continue;
491 if (special_name_cmp(name, a->name) < 0)
492 break;
494 /* add before item */
495 list_add_tail(&album->node, item);
496 return album;
499 static void album_add_track(struct album *album, struct tree_track *track)
502 * NOTE: This is not perfect. You should ignore track numbers if
503 * either is unset and use filename instead, but usually you
504 * have all track numbers set or all unset (within one album
505 * of course).
507 static const char * const album_track_sort_keys[] = {
508 "discnumber", "tracknumber", "filename", NULL
510 struct list_head *item;
512 track->album = album;
513 list_for_each(item, &album->track_head) {
514 const struct simple_track *a = (const struct simple_track *)track;
515 const struct simple_track *b = (const struct simple_track *)to_tree_track(item);
517 if (track_info_cmp(a->info, b->info, album_track_sort_keys) < 0)
518 break;
520 /* add before item */
521 list_add_tail(&track->node, item);
524 static void remove_artist(struct artist *artist);
526 static void update_artist_name(struct artist *artist, char *new_name)
528 free(artist->name);
529 artist->name = new_name; /* no need to make a copy here */
531 list_del(&artist->node);
532 insert_artist(artist);
533 window_changed(lib_tree_win);
536 void tree_add_track(struct tree_track *track)
538 const struct track_info *ti = tree_track_info(track);
539 const char *album_name, *artist_name, *artist_name_fancy = NULL;
540 struct artist *artist;
541 struct album *album;
542 int date;
544 if (is_url(ti->filename)) {
545 artist_name = "<Stream>";
546 album_name = "<Stream>";
547 } else {
548 const char *compilation;
550 artist_name = keyvals_get_val(ti->comments, "artist");
551 album_name = keyvals_get_val(ti->comments, "album");
553 artist_name_fancy = keyvals_get_val(ti->comments, "albumartistsort");
554 if (!artist_name_fancy)
555 artist_name_fancy = keyvals_get_val(ti->comments, "albumartist");
556 if (!artist_name_fancy)
557 artist_name_fancy = keyvals_get_val(ti->comments, "artistsort");
559 if (artist_name == NULL)
560 artist_name = "<No Name>";
561 if (album_name == NULL)
562 album_name = "<No Name>";
564 compilation = keyvals_get_val(ti->comments, "compilation");
565 if (compilation && (!strcasecmp(compilation, "1")
566 || !strcasecmp(compilation, "yes"))) {
567 /* Store all compilations under compilations */
568 artist_name = "<Compilations>";
572 find_artist_and_album(artist_name, album_name, &artist, &album);
574 /* update artist name if better one is available */
575 if (artist) {
576 if (artist_name_fancy &&
577 u_strcasecmp(artist->name, artist_name_fancy))
578 /* we've got a new fancy name */
579 update_artist_name(artist, xstrdup(artist_name_fancy));
580 else if (!artist_name_fancy) {
581 const char *artist_name_no_the = artist_name_skip_the(artist_name);
583 if (artist_name_no_the != artist_name &&
584 !u_strncasecmp(artist->name,
585 artist_name_no_the,
586 strlen(artist->name)))
587 /* same name, but starting with "The" */
588 update_artist_name(artist, xstrjoin(artist_name_no_the, ", The"));
592 if (album) {
593 album_add_track(album, track);
595 /* is the album where we added the track selected? */
596 if (album_selected(album)) {
597 /* update track window */
598 window_changed(lib_track_win);
600 } else if (artist) {
601 date = comments_get_date(ti->comments, "date");
602 album = artist_add_album(artist, album_name, date);
603 album_add_track(album, track);
605 if (artist->expanded) {
606 /* update tree window */
607 window_changed(lib_tree_win);
608 /* album is not selected => no need to update track_win */
610 } else {
611 const char *artist_name_no_the = artist_name_skip_the(artist_name);
613 if (artist_name_fancy)
614 artist = add_artist(artist_name_fancy, artist_name);
615 else if (artist_name_no_the == artist_name)
616 artist = add_artist(artist_name, artist_name);
617 else {
618 char *artist_name_full = xstrjoin(artist_name_no_the, ", The");
619 artist = add_artist(artist_name_full, artist_name);
620 free(artist_name_full);
623 date = comments_get_date(ti->comments, "date");
624 album = artist_add_album(artist, album_name, date);
625 album_add_track(album, track);
627 window_changed(lib_tree_win);
631 static void remove_sel_artist(struct artist *artist)
633 struct list_head *aitem, *ahead;
635 ahead = &artist->album_head;
636 aitem = ahead->next;
637 while (aitem != ahead) {
638 struct list_head *titem, *thead;
639 struct list_head *anext = aitem->next;
640 struct album *album = to_album(aitem);
642 thead = &album->track_head;
643 titem = thead->next;
644 while (titem != thead) {
645 struct list_head *tnext = titem->next;
646 struct tree_track *track = to_tree_track(titem);
648 editable_remove_track(&lib_editable, (struct simple_track *)track);
649 titem = tnext;
651 /* all tracks removed => album removed
652 * if the last album was removed then the artist was removed too
654 aitem = anext;
658 static void remove_sel_album(struct album *album)
660 struct list_head *item, *head;
662 head = &album->track_head;
663 item = head->next;
664 while (item != head) {
665 struct list_head *next = item->next;
666 struct tree_track *track = to_tree_track(item);
668 editable_remove_track(&lib_editable, (struct simple_track *)track);
669 item = next;
673 static void tree_win_remove_sel(void)
675 struct artist *artist;
676 struct album *album;
678 tree_win_get_selected(&artist, &album);
679 if (album) {
680 remove_sel_album(album);
681 } else if (artist) {
682 remove_sel_artist(artist);
686 static void track_win_remove_sel(void)
688 struct iter sel;
689 struct tree_track *track;
691 if (window_get_sel(lib_track_win, &sel)) {
692 track = iter_to_tree_track(&sel);
693 BUG_ON(track == NULL);
694 editable_remove_track(&lib_editable, (struct simple_track *)track);
698 void tree_toggle_active_window(void)
700 if (lib_cur_win == lib_tree_win) {
701 struct artist *artist;
702 struct album *album;
704 tree_win_get_selected(&artist, &album);
705 if (album) {
706 lib_cur_win = lib_track_win;
707 lib_tree_win->changed = 1;
708 lib_track_win->changed = 1;
710 } else if (lib_cur_win == lib_track_win) {
711 lib_cur_win = lib_tree_win;
712 lib_tree_win->changed = 1;
713 lib_track_win->changed = 1;
717 void tree_toggle_expand_artist(void)
719 struct iter sel;
720 struct artist *artist;
722 window_get_sel(lib_tree_win, &sel);
723 artist = iter_to_artist(&sel);
724 if (artist) {
725 if (artist->expanded) {
726 /* deselect album, select artist */
727 artist_to_iter(artist, &sel);
728 window_set_sel(lib_tree_win, &sel);
730 artist->expanded = 0;
731 lib_cur_win = lib_tree_win;
732 } else {
733 artist->expanded = 1;
735 window_changed(lib_tree_win);
739 static void remove_track(struct tree_track *track)
741 if (album_selected(track->album)) {
742 struct iter iter;
744 tree_track_to_iter(track, &iter);
745 window_row_vanishes(lib_track_win, &iter);
747 list_del(&track->node);
750 static void remove_album(struct album *album)
752 if (album->artist->expanded) {
753 struct iter iter;
755 album_to_iter(album, &iter);
756 window_row_vanishes(lib_tree_win, &iter);
758 list_del(&album->node);
761 static void remove_artist(struct artist *artist)
763 struct iter iter;
765 artist_to_iter(artist, &iter);
766 window_row_vanishes(lib_tree_win, &iter);
767 list_del(&artist->node);
770 void tree_remove(struct tree_track *track)
772 struct album *album = track->album;
773 struct artist *sel_artist;
774 struct album *sel_album;
776 tree_win_get_selected(&sel_artist, &sel_album);
778 remove_track(track);
779 /* don't free the track */
781 if (list_empty(&album->track_head)) {
782 struct artist *artist = album->artist;
784 if (sel_album == album)
785 lib_cur_win = lib_tree_win;
787 remove_album(album);
788 album_free(album);
790 if (list_empty(&artist->album_head)) {
791 artist->expanded = 0;
792 remove_artist(artist);
793 artist_free(artist);
798 void tree_remove_sel(void)
800 if (lib_cur_win == lib_tree_win) {
801 tree_win_remove_sel();
802 } else {
803 track_win_remove_sel();
807 void tree_sel_current(void)
809 if (lib_cur_track) {
810 struct iter iter;
812 CUR_ARTIST->expanded = 1;
814 if (lib_cur_win == lib_tree_win) {
815 lib_cur_win = lib_track_win;
816 lib_tree_win->changed = 1;
817 lib_track_win->changed = 1;
820 album_to_iter(CUR_ALBUM, &iter);
821 window_set_sel(lib_tree_win, &iter);
823 tree_track_to_iter(lib_cur_track, &iter);
824 window_set_sel(lib_track_win, &iter);
828 static int album_for_each_track(struct album *album, int (*cb)(void *data, struct track_info *ti),
829 void *data, int reverse)
831 struct tree_track *track;
832 int rc = 0;
834 if (reverse) {
835 list_for_each_entry_reverse(track, &album->track_head, node) {
836 rc = cb(data, tree_track_info(track));
837 if (rc)
838 break;
840 } else {
841 list_for_each_entry(track, &album->track_head, node) {
842 rc = cb(data, tree_track_info(track));
843 if (rc)
844 break;
847 return rc;
850 static int artist_for_each_track(struct artist *artist, int (*cb)(void *data, struct track_info *ti),
851 void *data, int reverse)
853 struct album *album;
854 int rc = 0;
856 if (reverse) {
857 list_for_each_entry_reverse(album, &artist->album_head, node) {
858 rc = album_for_each_track(album, cb, data, 1);
859 if (rc)
860 break;
862 } else {
863 list_for_each_entry(album, &artist->album_head, node) {
864 rc = album_for_each_track(album, cb, data, 0);
865 if (rc)
866 break;
869 return rc;
872 int __tree_for_each_sel(int (*cb)(void *data, struct track_info *ti), void *data, int reverse)
874 int rc = 0;
876 if (lib_cur_win == lib_tree_win) {
877 struct artist *artist;
878 struct album *album;
880 tree_win_get_selected(&artist, &album);
881 if (artist) {
882 if (album == NULL) {
883 rc = artist_for_each_track(artist, cb, data, reverse);
884 } else {
885 rc = album_for_each_track(album, cb, data, reverse);
888 } else {
889 struct iter sel;
890 struct tree_track *track;
892 if (window_get_sel(lib_track_win, &sel)) {
893 track = iter_to_tree_track(&sel);
894 rc = cb(data, tree_track_info(track));
897 return rc;
900 int tree_for_each_sel(int (*cb)(void *data, struct track_info *ti), void *data, int reverse)
902 int rc = __tree_for_each_sel(cb, data, reverse);
904 window_down(lib_cur_win, 1);
905 return rc;