Add cyan.theme
[cmus.git] / tree.c
blob7ee151db4b621b01687ccc6b2d47d765fb911210
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"
11 struct searchable *tree_searchable;
12 struct window *lib_tree_win;
13 struct window *lib_track_win;
14 struct window *lib_cur_win;
15 LIST_HEAD(lib_artist_head);
17 /* tree (search) iterators {{{ */
18 static int tree_search_get_prev(struct iter *iter)
20 struct list_head *head = iter->data0;
21 struct tree_track *track = iter->data1;
22 struct artist *artist;
23 struct album *album;
25 BUG_ON(iter->data2);
26 if (head == NULL)
27 return 0;
28 if (track == NULL) {
29 /* head, get last track */
30 if (head->prev == head) {
31 /* empty, iter points to the head already */
32 return 0;
34 artist = to_artist(head->prev);
35 album = to_album(artist->album_head.prev);
36 iter->data1 = to_tree_track(album->track_head.prev);
37 return 1;
39 /* prev track */
40 if (track->node.prev == &track->album->track_head || search_restricted) {
41 /* prev album */
42 if (track->album->node.prev == &track->album->artist->album_head) {
43 /* prev artist */
44 if (track->album->artist->node.prev == &lib_artist_head)
45 return 0;
46 artist = to_artist(track->album->artist->node.prev);
47 album = to_album(artist->album_head.prev);
48 track = to_tree_track(album->track_head.prev);
49 } else {
50 album = to_album(track->album->node.prev);
51 track = to_tree_track(album->track_head.prev);
53 } else {
54 track = to_tree_track(track->node.prev);
56 iter->data1 = track;
57 return 1;
60 static int tree_search_get_next(struct iter *iter)
62 struct list_head *head = iter->data0;
63 struct tree_track *track = iter->data1;
64 struct artist *artist;
65 struct album *album;
67 BUG_ON(iter->data2);
68 if (head == NULL)
69 return 0;
70 if (track == NULL) {
71 /* head, get first track */
72 if (head->next == head) {
73 /* empty, iter points to the head already */
74 return 0;
76 artist = to_artist(head->next);
77 album = to_album(artist->album_head.next);
78 iter->data1 = to_tree_track(album->track_head.next);
79 return 1;
81 /* next track */
82 if (track->node.next == &track->album->track_head || search_restricted) {
83 /* next album */
84 if (track->album->node.next == &track->album->artist->album_head) {
85 /* next artist */
86 if (track->album->artist->node.next == &lib_artist_head)
87 return 0;
88 artist = to_artist(track->album->artist->node.next);
89 album = to_album(artist->album_head.next);
90 track = to_tree_track(album->track_head.next);
91 } else {
92 album = to_album(track->album->node.next);
93 track = to_tree_track(album->track_head.next);
95 } else {
96 track = to_tree_track(track->node.next);
98 iter->data1 = track;
99 return 1;
101 /* }}} */
103 /* tree window iterators {{{ */
104 static int tree_get_prev(struct iter *iter)
106 struct list_head *head = iter->data0;
107 struct artist *artist = iter->data1;
108 struct album *album = iter->data2;
110 BUG_ON(head == NULL);
111 BUG_ON(artist == NULL && album != NULL);
112 if (artist == NULL) {
113 /* head, get last artist and/or album */
114 if (head->prev == head) {
115 /* empty, iter points to the head already */
116 return 0;
118 artist = to_artist(head->prev);
119 if (artist->expanded) {
120 album = to_album(artist->album_head.prev);
121 } else {
122 album = NULL;
124 iter->data1 = artist;
125 iter->data2 = album;
126 return 1;
128 if (artist->expanded && album) {
129 /* prev album */
130 if (album->node.prev == &artist->album_head) {
131 iter->data2 = NULL;
132 return 1;
133 } else {
134 iter->data2 = to_album(album->node.prev);
135 return 1;
139 /* prev artist */
140 if (artist->node.prev == &lib_artist_head) {
141 iter->data1 = NULL;
142 iter->data2 = NULL;
143 return 0;
145 artist = to_artist(artist->node.prev);
146 iter->data1 = artist;
147 iter->data2 = NULL;
148 if (artist->expanded) {
149 /* last album */
150 iter->data2 = to_album(artist->album_head.prev);
152 return 1;
155 static int tree_get_next(struct iter *iter)
157 struct list_head *head = iter->data0;
158 struct artist *artist = iter->data1;
159 struct album *album = iter->data2;
161 BUG_ON(head == NULL);
162 BUG_ON(artist == NULL && album != NULL);
163 if (artist == NULL) {
164 /* head, get first artist */
165 if (head->next == head) {
166 /* empty, iter points to the head already */
167 return 0;
169 iter->data1 = to_artist(head->next);
170 iter->data2 = NULL;
171 return 1;
173 if (artist->expanded) {
174 /* next album */
175 if (album == NULL) {
176 /* first album */
177 iter->data2 = to_album(artist->album_head.next);
178 return 1;
180 if (album->node.next != &artist->album_head) {
181 iter->data2 = to_album(album->node.next);
182 return 1;
186 /* next artist */
187 if (artist->node.next == head) {
188 iter->data1 = NULL;
189 iter->data2 = NULL;
190 return 0;
192 iter->data1 = to_artist(artist->node.next);
193 iter->data2 = NULL;
194 return 1;
196 /* }}} */
198 static GENERIC_ITER_PREV(tree_track_get_prev, struct tree_track, node)
199 static GENERIC_ITER_NEXT(tree_track_get_next, struct tree_track, node)
201 static inline void tree_search_track_to_iter(struct tree_track *track, struct iter *iter)
203 iter->data0 = &lib_artist_head;
204 iter->data1 = track;
205 iter->data2 = NULL;
208 static inline void album_to_iter(struct album *album, struct iter *iter)
210 iter->data0 = &lib_artist_head;
211 iter->data1 = album->artist;
212 iter->data2 = album;
215 static inline void artist_to_iter(struct artist *artist, struct iter *iter)
217 iter->data0 = &lib_artist_head;
218 iter->data1 = artist;
219 iter->data2 = NULL;
222 static inline void tree_track_to_iter(struct tree_track *track, struct iter *iter)
224 iter->data0 = &track->album->track_head;
225 iter->data1 = track;
226 iter->data2 = NULL;
229 /* search (tree) {{{ */
230 static int tree_search_get_current(void *data, struct iter *iter)
232 struct artist *artist;
233 struct album *album;
234 struct tree_track *track;
235 struct iter tmpiter;
237 if (list_empty(&lib_artist_head))
238 return 0;
239 if (window_get_sel(lib_track_win, &tmpiter)) {
240 track = iter_to_tree_track(&tmpiter);
241 tree_search_track_to_iter(track, iter);
242 return 1;
245 /* artist not expanded. track_win is empty
246 * set tmp to the first track of the selected artist */
247 window_get_sel(lib_tree_win, &tmpiter);
248 artist = iter_to_artist(&tmpiter);
249 album = to_album(artist->album_head.next);
250 track = to_tree_track(album->track_head.next);
251 tree_search_track_to_iter(track, iter);
252 return 1;
255 static inline struct tree_track *iter_to_tree_search_track(const struct iter *iter)
257 BUG_ON(iter->data0 != &lib_artist_head);
258 return iter->data1;
261 static int tree_search_matches(void *data, struct iter *iter, const char *text)
263 struct tree_track *track;
264 struct iter tmpiter;
265 unsigned int flags = TI_MATCH_ARTIST | TI_MATCH_ALBUM;
267 if (!search_restricted)
268 flags |= TI_MATCH_TITLE;
269 track = iter_to_tree_search_track(iter);
270 if (!track_info_matches(tree_track_info(track), text, flags))
271 return 0;
272 track->album->artist->expanded = 1;
273 album_to_iter(track->album, &tmpiter);
274 window_set_sel(lib_tree_win, &tmpiter);
276 tree_track_to_iter(track, &tmpiter);
277 window_set_sel(lib_track_win, &tmpiter);
278 return 1;
281 static const struct searchable_ops tree_search_ops = {
282 .lock = editable_search_lock,
283 .unlock = editable_search_unlock,
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->name);
329 free(artist);
332 static void album_free(struct album *album)
334 free(album->name);
335 free(album);
338 void tree_init(void)
340 struct iter iter;
342 list_init(&lib_artist_head);
344 lib_tree_win = window_new(tree_get_prev, tree_get_next);
345 lib_track_win = window_new(tree_track_get_prev, tree_track_get_next);
346 lib_cur_win = lib_tree_win;
348 lib_tree_win->sel_changed = tree_sel_changed;
350 window_set_empty(lib_track_win);
351 window_set_contents(lib_tree_win, &lib_artist_head);
353 iter.data0 = &lib_artist_head;
354 iter.data1 = NULL;
355 iter.data2 = NULL;
356 tree_searchable = searchable_new(NULL, &iter, &tree_search_ops);
359 struct track_info *tree_set_selected(void)
361 struct artist *artist;
362 struct album *album;
363 struct track_info *info;
364 struct iter sel;
366 if (list_empty(&lib_artist_head))
367 return NULL;
369 tree_win_get_selected(&artist, &album);
370 if (album == NULL) {
371 /* only artist selected, track window is empty
372 * => get first album of the selected artist and first track of that album
374 album = to_album(artist->album_head.next);
375 lib_cur_track = to_tree_track(album->track_head.next);
376 } else {
377 window_get_sel(lib_track_win, &sel);
378 lib_cur_track = iter_to_tree_track(&sel);
381 lib_tree_win->changed = 1;
382 lib_track_win->changed = 1;
384 info = tree_track_info(lib_cur_track);
385 track_info_ref(info);
386 return info;
389 static void find_artist_and_album(const char *artist_name,
390 const char *album_name, struct artist **_artist,
391 struct album **_album)
393 struct artist *artist;
394 struct album *album;
396 list_for_each_entry(artist, &lib_artist_head, node) {
397 int res;
399 res = xstrcasecmp(artist->name, artist_name);
400 if (res == 0) {
401 *_artist = artist;
402 list_for_each_entry(album, &artist->album_head, node) {
403 res = xstrcasecmp(album->name, album_name);
404 if (res == 0) {
405 *_album = album;
406 return;
409 *_album = NULL;
410 return;
413 *_artist = NULL;
414 *_album = NULL;
415 return;
418 static struct artist *add_artist(const char *name)
420 struct list_head *item;
421 struct artist *artist;
423 artist = xnew(struct artist, 1);
424 artist->name = xxstrdup(name);
425 list_init(&artist->album_head);
426 artist->expanded = 0;
427 list_for_each(item, &lib_artist_head) {
428 struct artist *a = to_artist(item);
430 if (xstrcasecmp(name, a->name) < 0)
431 break;
433 /* add before item */
434 list_add_tail(&artist->node, item);
435 return artist;
438 static struct album *artist_add_album(struct artist *artist, const char *name, int date)
440 struct list_head *item;
441 struct album *album;
443 album = xnew(struct album, 1);
444 album->name = xxstrdup(name);
445 album->date = date;
446 list_init(&album->track_head);
447 album->artist = artist;
448 list_for_each(item, &artist->album_head) {
449 struct album *a = to_album(item);
451 if (date < a->date)
452 break;
453 if (date > a->date)
454 continue;
455 if (xstrcasecmp(name, a->name) < 0)
456 break;
458 /* add before item */
459 list_add_tail(&album->node, item);
460 return album;
463 static void album_add_track(struct album *album, struct tree_track *track)
466 * NOTE: This is not perfect. You should ignore track numbers if
467 * either is unset and use filename instead, but usually you
468 * have all track numbers set or all unset (within one album
469 * of course).
471 static const char * const album_track_sort_keys[] = {
472 "discnumber", "tracknumber", "filename", NULL
474 struct list_head *item;
476 track->album = album;
477 list_for_each(item, &album->track_head) {
478 const struct simple_track *a = (const struct simple_track *)track;
479 const struct simple_track *b = (const struct simple_track *)to_tree_track(item);
481 if (simple_track_cmp(&a->node, &b->node, album_track_sort_keys) < 0)
482 break;
484 /* add before item */
485 list_add_tail(&track->node, item);
488 void tree_add_track(struct tree_track *track)
490 const struct track_info *ti = tree_track_info(track);
491 const char *album_name, *artist_name;
492 struct artist *artist;
493 struct album *album;
494 int date;
496 album_name = comments_get_val(ti->comments, "album");
497 artist_name = comments_get_val(ti->comments, "artist");
499 if (artist_name == NULL && album_name == NULL && is_url(ti->filename)) {
500 artist_name = "<Stream>";
501 album_name = "<Stream>";
504 find_artist_and_album(artist_name, album_name, &artist, &album);
505 if (album) {
506 album_add_track(album, track);
508 /* is the album where we added the track selected? */
509 if (album_selected(album)) {
510 /* update track window */
511 window_changed(lib_track_win);
513 } else if (artist) {
514 date = comments_get_int(ti->comments, "date");
515 album = artist_add_album(artist, album_name, date);
516 album_add_track(album, track);
518 if (artist->expanded) {
519 /* update tree window */
520 window_changed(lib_tree_win);
521 /* album is not selected => no need to update track_win */
523 } else {
524 date = comments_get_int(ti->comments, "date");
525 artist = add_artist(artist_name);
526 album = artist_add_album(artist, album_name, date);
527 album_add_track(album, track);
529 window_changed(lib_tree_win);
533 static void remove_sel_artist(struct artist *artist)
535 struct list_head *aitem, *ahead;
537 ahead = &artist->album_head;
538 aitem = ahead->next;
539 while (aitem != ahead) {
540 struct list_head *titem, *thead;
541 struct list_head *anext = aitem->next;
542 struct album *album = to_album(aitem);
544 thead = &album->track_head;
545 titem = thead->next;
546 while (titem != thead) {
547 struct list_head *tnext = titem->next;
548 struct tree_track *track = to_tree_track(titem);
550 editable_remove_track(&lib_editable, (struct simple_track *)track);
551 titem = tnext;
553 /* all tracks removed => album removed
554 * if the last album was removed then the artist was removed too
556 aitem = anext;
560 static void remove_sel_album(struct album *album)
562 struct list_head *item, *head;
564 head = &album->track_head;
565 item = head->next;
566 while (item != head) {
567 struct list_head *next = item->next;
568 struct tree_track *track = to_tree_track(item);
570 editable_remove_track(&lib_editable, (struct simple_track *)track);
571 item = next;
575 static void tree_win_remove_sel(void)
577 struct artist *artist;
578 struct album *album;
580 tree_win_get_selected(&artist, &album);
581 if (album) {
582 remove_sel_album(album);
583 } else if (artist) {
584 remove_sel_artist(artist);
588 static void track_win_remove_sel(void)
590 struct iter sel;
591 struct tree_track *track;
593 if (window_get_sel(lib_track_win, &sel)) {
594 track = iter_to_tree_track(&sel);
595 BUG_ON(track == NULL);
596 editable_remove_track(&lib_editable, (struct simple_track *)track);
600 void tree_toggle_active_window(void)
602 if (lib_cur_win == lib_tree_win) {
603 struct artist *artist;
604 struct album *album;
606 tree_win_get_selected(&artist, &album);
607 if (album) {
608 lib_cur_win = lib_track_win;
609 lib_tree_win->changed = 1;
610 lib_track_win->changed = 1;
612 } else if (lib_cur_win == lib_track_win) {
613 lib_cur_win = lib_tree_win;
614 lib_tree_win->changed = 1;
615 lib_track_win->changed = 1;
619 void tree_toggle_expand_artist(void)
621 struct iter sel;
622 struct artist *artist;
624 window_get_sel(lib_tree_win, &sel);
625 artist = iter_to_artist(&sel);
626 if (artist) {
627 if (artist->expanded) {
628 /* deselect album, select artist */
629 artist_to_iter(artist, &sel);
630 window_set_sel(lib_tree_win, &sel);
632 artist->expanded = 0;
633 lib_cur_win = lib_tree_win;
634 } else {
635 artist->expanded = 1;
637 window_changed(lib_tree_win);
641 static void remove_track(struct tree_track *track)
643 if (album_selected(track->album)) {
644 struct iter iter;
646 tree_track_to_iter(track, &iter);
647 window_row_vanishes(lib_track_win, &iter);
649 list_del(&track->node);
652 static void remove_album(struct album *album)
654 if (album->artist->expanded) {
655 struct iter iter;
657 album_to_iter(album, &iter);
658 window_row_vanishes(lib_tree_win, &iter);
660 list_del(&album->node);
663 static void remove_artist(struct artist *artist)
665 struct iter iter;
667 artist_to_iter(artist, &iter);
668 window_row_vanishes(lib_tree_win, &iter);
669 list_del(&artist->node);
672 void tree_remove(struct tree_track *track)
674 struct album *album = track->album;
675 struct artist *sel_artist;
676 struct album *sel_album;
678 tree_win_get_selected(&sel_artist, &sel_album);
680 remove_track(track);
681 /* don't free the track */
683 if (list_empty(&album->track_head)) {
684 struct artist *artist = album->artist;
686 if (sel_album == album)
687 lib_cur_win = lib_tree_win;
689 remove_album(album);
690 album_free(album);
692 if (list_empty(&artist->album_head)) {
693 artist->expanded = 0;
694 remove_artist(artist);
695 artist_free(artist);
700 void tree_remove_sel(void)
702 if (lib_cur_win == lib_tree_win) {
703 tree_win_remove_sel();
704 } else {
705 track_win_remove_sel();
709 void tree_sel_current(void)
711 if (lib_cur_track) {
712 struct iter iter;
714 CUR_ARTIST->expanded = 1;
716 if (lib_cur_win == lib_tree_win) {
717 lib_cur_win = lib_track_win;
718 lib_tree_win->changed = 1;
719 lib_track_win->changed = 1;
722 album_to_iter(CUR_ALBUM, &iter);
723 window_set_sel(lib_tree_win, &iter);
725 tree_track_to_iter(lib_cur_track, &iter);
726 window_set_sel(lib_track_win, &iter);
730 static int album_for_each_track(struct album *album, int (*cb)(void *data, struct track_info *ti),
731 void *data, int reverse)
733 struct tree_track *track;
734 int rc = 0;
736 if (reverse) {
737 list_for_each_entry_reverse(track, &album->track_head, node) {
738 rc = cb(data, tree_track_info(track));
739 if (rc)
740 break;
742 } else {
743 list_for_each_entry(track, &album->track_head, node) {
744 rc = cb(data, tree_track_info(track));
745 if (rc)
746 break;
749 return rc;
752 static int artist_for_each_track(struct artist *artist, int (*cb)(void *data, struct track_info *ti),
753 void *data, int reverse)
755 struct album *album;
756 int rc = 0;
758 if (reverse) {
759 list_for_each_entry_reverse(album, &artist->album_head, node) {
760 rc = album_for_each_track(album, cb, data, 1);
761 if (rc)
762 break;
764 } else {
765 list_for_each_entry(album, &artist->album_head, node) {
766 rc = album_for_each_track(album, cb, data, 0);
767 if (rc)
768 break;
771 return rc;
774 int __tree_for_each_sel(int (*cb)(void *data, struct track_info *ti), void *data, int reverse)
776 int rc = 0;
778 if (lib_cur_win == lib_tree_win) {
779 struct artist *artist;
780 struct album *album;
782 tree_win_get_selected(&artist, &album);
783 if (artist) {
784 if (album == NULL) {
785 rc = artist_for_each_track(artist, cb, data, reverse);
786 } else {
787 rc = album_for_each_track(album, cb, data, reverse);
790 } else {
791 struct iter sel;
792 struct tree_track *track;
794 if (window_get_sel(lib_track_win, &sel)) {
795 track = iter_to_tree_track(&sel);
796 rc = cb(data, tree_track_info(track));
799 return rc;
802 int tree_for_each_sel(int (*cb)(void *data, struct track_info *ti), void *data, int reverse)
804 int rc = __tree_for_each_sel(cb, data, reverse);
806 window_down(lib_cur_win, 1);
807 return rc;