Simplify finding artist name which is displayed in the tree window
[cmus.git] / tree.c
blob19ede2d5e753ea6fc0c30ece672beae1c53f2cd8
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, *compilation, *artist_name_fancy;
540 struct artist *artist;
541 struct album *album;
542 int date;
544 album_name = keyvals_get_val(ti->comments, "album");
545 artist_name = keyvals_get_val(ti->comments, "artist");
546 compilation = keyvals_get_val(ti->comments, "compilation");
548 artist_name_fancy = keyvals_get_val(ti->comments, "albumartistsort");
549 if (!artist_name_fancy)
550 artist_name_fancy = keyvals_get_val(ti->comments, "albumartist");
551 if (!artist_name_fancy)
552 artist_name_fancy = keyvals_get_val(ti->comments, "artistsort");
554 if (is_url(ti->filename)) {
555 artist_name = "<Stream>";
556 album_name = "<Stream>";
559 if (artist_name == NULL)
560 artist_name = "<No Name>";
561 if (album_name == NULL)
562 album_name = "<No Name>";
564 if (compilation && (!strcasecmp(compilation, "1")
565 || !strcasecmp(compilation, "yes"))) {
566 /* Store all compilations under compilations */
567 artist_name = "<Compilations>";
570 find_artist_and_album(artist_name, album_name, &artist, &album);
572 /* update artist name if better one is available */
573 if (artist) {
574 if (artist_name_fancy &&
575 u_strcasecmp(artist->name, artist_name_fancy))
576 /* we've got a new fancy name */
577 update_artist_name(artist, xstrdup(artist_name_fancy));
578 else if (!artist_name_fancy) {
579 const char *artist_name_no_the = artist_name_skip_the(artist_name);
581 if (artist_name_no_the != artist_name &&
582 !u_strncasecmp(artist->name,
583 artist_name_no_the,
584 strlen(artist->name)))
585 /* same name, but starting with "The" */
586 update_artist_name(artist, xstrjoin(artist_name_no_the, ", The"));
590 if (album) {
591 album_add_track(album, track);
593 /* is the album where we added the track selected? */
594 if (album_selected(album)) {
595 /* update track window */
596 window_changed(lib_track_win);
598 } else if (artist) {
599 date = comments_get_date(ti->comments, "date");
600 album = artist_add_album(artist, album_name, date);
601 album_add_track(album, track);
603 if (artist->expanded) {
604 /* update tree window */
605 window_changed(lib_tree_win);
606 /* album is not selected => no need to update track_win */
608 } else {
609 const char *artist_name_no_the = artist_name_skip_the(artist_name);
611 if (artist_name_fancy)
612 artist = add_artist(artist_name_fancy, artist_name);
613 else if (artist_name_no_the == artist_name)
614 artist = add_artist(artist_name, artist_name);
615 else {
616 char *artist_name_full = xstrjoin(artist_name_no_the, ", The");
617 artist = add_artist(artist_name_full, artist_name);
618 free(artist_name_full);
621 date = comments_get_date(ti->comments, "date");
622 album = artist_add_album(artist, album_name, date);
623 album_add_track(album, track);
625 window_changed(lib_tree_win);
629 static void remove_sel_artist(struct artist *artist)
631 struct list_head *aitem, *ahead;
633 ahead = &artist->album_head;
634 aitem = ahead->next;
635 while (aitem != ahead) {
636 struct list_head *titem, *thead;
637 struct list_head *anext = aitem->next;
638 struct album *album = to_album(aitem);
640 thead = &album->track_head;
641 titem = thead->next;
642 while (titem != thead) {
643 struct list_head *tnext = titem->next;
644 struct tree_track *track = to_tree_track(titem);
646 editable_remove_track(&lib_editable, (struct simple_track *)track);
647 titem = tnext;
649 /* all tracks removed => album removed
650 * if the last album was removed then the artist was removed too
652 aitem = anext;
656 static void remove_sel_album(struct album *album)
658 struct list_head *item, *head;
660 head = &album->track_head;
661 item = head->next;
662 while (item != head) {
663 struct list_head *next = item->next;
664 struct tree_track *track = to_tree_track(item);
666 editable_remove_track(&lib_editable, (struct simple_track *)track);
667 item = next;
671 static void tree_win_remove_sel(void)
673 struct artist *artist;
674 struct album *album;
676 tree_win_get_selected(&artist, &album);
677 if (album) {
678 remove_sel_album(album);
679 } else if (artist) {
680 remove_sel_artist(artist);
684 static void track_win_remove_sel(void)
686 struct iter sel;
687 struct tree_track *track;
689 if (window_get_sel(lib_track_win, &sel)) {
690 track = iter_to_tree_track(&sel);
691 BUG_ON(track == NULL);
692 editable_remove_track(&lib_editable, (struct simple_track *)track);
696 void tree_toggle_active_window(void)
698 if (lib_cur_win == lib_tree_win) {
699 struct artist *artist;
700 struct album *album;
702 tree_win_get_selected(&artist, &album);
703 if (album) {
704 lib_cur_win = lib_track_win;
705 lib_tree_win->changed = 1;
706 lib_track_win->changed = 1;
708 } else if (lib_cur_win == lib_track_win) {
709 lib_cur_win = lib_tree_win;
710 lib_tree_win->changed = 1;
711 lib_track_win->changed = 1;
715 void tree_toggle_expand_artist(void)
717 struct iter sel;
718 struct artist *artist;
720 window_get_sel(lib_tree_win, &sel);
721 artist = iter_to_artist(&sel);
722 if (artist) {
723 if (artist->expanded) {
724 /* deselect album, select artist */
725 artist_to_iter(artist, &sel);
726 window_set_sel(lib_tree_win, &sel);
728 artist->expanded = 0;
729 lib_cur_win = lib_tree_win;
730 } else {
731 artist->expanded = 1;
733 window_changed(lib_tree_win);
737 static void remove_track(struct tree_track *track)
739 if (album_selected(track->album)) {
740 struct iter iter;
742 tree_track_to_iter(track, &iter);
743 window_row_vanishes(lib_track_win, &iter);
745 list_del(&track->node);
748 static void remove_album(struct album *album)
750 if (album->artist->expanded) {
751 struct iter iter;
753 album_to_iter(album, &iter);
754 window_row_vanishes(lib_tree_win, &iter);
756 list_del(&album->node);
759 static void remove_artist(struct artist *artist)
761 struct iter iter;
763 artist_to_iter(artist, &iter);
764 window_row_vanishes(lib_tree_win, &iter);
765 list_del(&artist->node);
768 void tree_remove(struct tree_track *track)
770 struct album *album = track->album;
771 struct artist *sel_artist;
772 struct album *sel_album;
774 tree_win_get_selected(&sel_artist, &sel_album);
776 remove_track(track);
777 /* don't free the track */
779 if (list_empty(&album->track_head)) {
780 struct artist *artist = album->artist;
782 if (sel_album == album)
783 lib_cur_win = lib_tree_win;
785 remove_album(album);
786 album_free(album);
788 if (list_empty(&artist->album_head)) {
789 artist->expanded = 0;
790 remove_artist(artist);
791 artist_free(artist);
796 void tree_remove_sel(void)
798 if (lib_cur_win == lib_tree_win) {
799 tree_win_remove_sel();
800 } else {
801 track_win_remove_sel();
805 void tree_sel_current(void)
807 if (lib_cur_track) {
808 struct iter iter;
810 CUR_ARTIST->expanded = 1;
812 if (lib_cur_win == lib_tree_win) {
813 lib_cur_win = lib_track_win;
814 lib_tree_win->changed = 1;
815 lib_track_win->changed = 1;
818 album_to_iter(CUR_ALBUM, &iter);
819 window_set_sel(lib_tree_win, &iter);
821 tree_track_to_iter(lib_cur_track, &iter);
822 window_set_sel(lib_track_win, &iter);
826 static int album_for_each_track(struct album *album, int (*cb)(void *data, struct track_info *ti),
827 void *data, int reverse)
829 struct tree_track *track;
830 int rc = 0;
832 if (reverse) {
833 list_for_each_entry_reverse(track, &album->track_head, node) {
834 rc = cb(data, tree_track_info(track));
835 if (rc)
836 break;
838 } else {
839 list_for_each_entry(track, &album->track_head, node) {
840 rc = cb(data, tree_track_info(track));
841 if (rc)
842 break;
845 return rc;
848 static int artist_for_each_track(struct artist *artist, int (*cb)(void *data, struct track_info *ti),
849 void *data, int reverse)
851 struct album *album;
852 int rc = 0;
854 if (reverse) {
855 list_for_each_entry_reverse(album, &artist->album_head, node) {
856 rc = album_for_each_track(album, cb, data, 1);
857 if (rc)
858 break;
860 } else {
861 list_for_each_entry(album, &artist->album_head, node) {
862 rc = album_for_each_track(album, cb, data, 0);
863 if (rc)
864 break;
867 return rc;
870 int __tree_for_each_sel(int (*cb)(void *data, struct track_info *ti), void *data, int reverse)
872 int rc = 0;
874 if (lib_cur_win == lib_tree_win) {
875 struct artist *artist;
876 struct album *album;
878 tree_win_get_selected(&artist, &album);
879 if (artist) {
880 if (album == NULL) {
881 rc = artist_for_each_track(artist, cb, data, reverse);
882 } else {
883 rc = album_for_each_track(album, cb, data, reverse);
886 } else {
887 struct iter sel;
888 struct tree_track *track;
890 if (window_get_sel(lib_track_win, &sel)) {
891 track = iter_to_tree_track(&sel);
892 rc = cb(data, tree_track_info(track));
895 return rc;
898 int tree_for_each_sel(int (*cb)(void *data, struct track_info *ti), void *data, int reverse)
900 int rc = __tree_for_each_sel(cb, data, reverse);
902 window_down(lib_cur_win, 1);
903 return rc;