2 * Copyright 2006 Timo Hirvonen
6 #include "search_mode.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
;
31 /* head, get last track */
32 if (head
->prev
== head
) {
33 /* empty, iter points to the head already */
36 artist
= to_artist(head
->prev
);
37 album
= to_album(artist
->album_head
.prev
);
38 iter
->data1
= to_tree_track(album
->track_head
.prev
);
42 if (track
->node
.prev
== &track
->album
->track_head
|| search_restricted
) {
44 if (track
->album
->node
.prev
== &track
->album
->artist
->album_head
) {
46 if (track
->album
->artist
->node
.prev
== &lib_artist_head
)
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
);
52 album
= to_album(track
->album
->node
.prev
);
53 track
= to_tree_track(album
->track_head
.prev
);
56 track
= to_tree_track(track
->node
.prev
);
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
;
73 /* head, get first track */
74 if (head
->next
== head
) {
75 /* empty, iter points to the head already */
78 artist
= to_artist(head
->next
);
79 album
= to_album(artist
->album_head
.next
);
80 iter
->data1
= to_tree_track(album
->track_head
.next
);
84 if (track
->node
.next
== &track
->album
->track_head
|| search_restricted
) {
86 if (track
->album
->node
.next
== &track
->album
->artist
->album_head
) {
88 if (track
->album
->artist
->node
.next
== &lib_artist_head
)
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
);
94 album
= to_album(track
->album
->node
.next
);
95 track
= to_tree_track(album
->track_head
.next
);
98 track
= to_tree_track(track
->node
.next
);
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 */
120 artist
= to_artist(head
->prev
);
121 if (artist
->expanded
) {
122 album
= to_album(artist
->album_head
.prev
);
126 iter
->data1
= artist
;
130 if (artist
->expanded
&& album
) {
132 if (album
->node
.prev
== &artist
->album_head
) {
136 iter
->data2
= to_album(album
->node
.prev
);
142 if (artist
->node
.prev
== &lib_artist_head
) {
147 artist
= to_artist(artist
->node
.prev
);
148 iter
->data1
= artist
;
150 if (artist
->expanded
) {
152 iter
->data2
= to_album(artist
->album_head
.prev
);
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 */
171 iter
->data1
= to_artist(head
->next
);
175 if (artist
->expanded
) {
179 iter
->data2
= to_album(artist
->album_head
.next
);
182 if (album
->node
.next
!= &artist
->album_head
) {
183 iter
->data2
= to_album(album
->node
.next
);
189 if (artist
->node
.next
== head
) {
194 iter
->data1
= to_artist(artist
->node
.next
);
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
;
210 static inline void album_to_iter(struct album
*album
, struct iter
*iter
)
212 iter
->data0
= &lib_artist_head
;
213 iter
->data1
= album
->artist
;
217 static inline void artist_to_iter(struct artist
*artist
, struct iter
*iter
)
219 iter
->data0
= &lib_artist_head
;
220 iter
->data1
= artist
;
224 static inline void tree_track_to_iter(struct tree_track
*track
, struct iter
*iter
)
226 iter
->data0
= &track
->album
->track_head
;
231 /* search (tree) {{{ */
232 static int tree_search_get_current(void *data
, struct iter
*iter
)
234 struct artist
*artist
;
236 struct tree_track
*track
;
239 if (list_empty(&lib_artist_head
))
241 if (window_get_sel(lib_track_win
, &tmpiter
)) {
242 track
= iter_to_tree_track(&tmpiter
);
243 tree_search_track_to_iter(track
, iter
);
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
);
257 static inline struct tree_track
*iter_to_tree_search_track(const struct iter
*iter
)
259 BUG_ON(iter
->data0
!= &lib_artist_head
);
263 static int tree_search_matches(void *data
, struct iter
*iter
, const char *text
)
265 struct tree_track
*track
;
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
))
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
);
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
)
295 if (window_get_sel(lib_tree_win
, &sel
))
296 return album
== iter_to_album(&sel
);
300 static void tree_sel_changed(void)
305 window_get_sel(lib_tree_win
, &sel
);
306 album
= iter_to_album(&sel
);
308 window_set_empty(lib_track_win
);
310 window_set_contents(lib_track_win
, &album
->track_head
);
314 static inline void tree_win_get_selected(struct artist
**artist
, struct album
**album
)
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
);
333 static void album_free(struct album
*album
)
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
;
357 tree_searchable
= searchable_new(NULL
, &iter
, &tree_search_ops
);
360 struct track_info
*tree_set_selected(void)
362 struct artist
*artist
;
364 struct track_info
*info
;
367 if (list_empty(&lib_artist_head
))
370 tree_win_get_selected(&artist
, &album
);
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
);
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
);
390 static const char *artist_name_skip_the(const char *a
)
392 if (!strncasecmp(a
, "the ", 4)) {
394 while (*a
== ' ' || *a
== '\t')
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
;
415 list_for_each_entry(artist
, &lib_artist_head
, node
) {
418 res
= artist_name_cmp(artist
->raw_name
, artist_raw_name
);
421 list_for_each_entry(album
, &artist
->album_head
, node
) {
422 res
= u_strcasecmp(album
->name
, album_name
);
437 static int special_name_cmp(const char *a
, const char *b
)
439 /* keep <Stream> etc. top */
440 int cmp
= (*a
!= '<') - (*b
!= '<');
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)
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
);
473 static struct album
*artist_add_album(struct artist
*artist
, const char *name
, int date
)
475 struct list_head
*item
;
478 album
= xnew(struct album
, 1);
479 album
->name
= xstrdup(name
);
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
);
491 if (special_name_cmp(name
, a
->name
) < 0)
494 /* add before item */
495 list_add_tail(&album
->node
, item
);
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
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)
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
)
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
;
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 */
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
,
584 strlen(artist
->name
)))
585 /* same name, but starting with "The" */
586 update_artist_name(artist
, xstrjoin(artist_name_no_the
, ", The"));
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
);
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 */
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
);
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
;
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
;
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
);
649 /* all tracks removed => album removed
650 * if the last album was removed then the artist was removed too
656 static void remove_sel_album(struct album
*album
)
658 struct list_head
*item
, *head
;
660 head
= &album
->track_head
;
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
);
671 static void tree_win_remove_sel(void)
673 struct artist
*artist
;
676 tree_win_get_selected(&artist
, &album
);
678 remove_sel_album(album
);
680 remove_sel_artist(artist
);
684 static void track_win_remove_sel(void)
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
;
702 tree_win_get_selected(&artist
, &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)
718 struct artist
*artist
;
720 window_get_sel(lib_tree_win
, &sel
);
721 artist
= iter_to_artist(&sel
);
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
;
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
)) {
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
) {
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
)
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
);
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
;
788 if (list_empty(&artist
->album_head
)) {
789 artist
->expanded
= 0;
790 remove_artist(artist
);
796 void tree_remove_sel(void)
798 if (lib_cur_win
== lib_tree_win
) {
799 tree_win_remove_sel();
801 track_win_remove_sel();
805 void tree_sel_current(void)
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
;
833 list_for_each_entry_reverse(track
, &album
->track_head
, node
) {
834 rc
= cb(data
, tree_track_info(track
));
839 list_for_each_entry(track
, &album
->track_head
, node
) {
840 rc
= cb(data
, tree_track_info(track
));
848 static int artist_for_each_track(struct artist
*artist
, int (*cb
)(void *data
, struct track_info
*ti
),
849 void *data
, int reverse
)
855 list_for_each_entry_reverse(album
, &artist
->album_head
, node
) {
856 rc
= album_for_each_track(album
, cb
, data
, 1);
861 list_for_each_entry(album
, &artist
->album_head
, node
) {
862 rc
= album_for_each_track(album
, cb
, data
, 0);
870 int __tree_for_each_sel(int (*cb
)(void *data
, struct track_info
*ti
), void *data
, int reverse
)
874 if (lib_cur_win
== lib_tree_win
) {
875 struct artist
*artist
;
878 tree_win_get_selected(&artist
, &album
);
881 rc
= artist_for_each_track(artist
, cb
, data
, reverse
);
883 rc
= album_for_each_track(album
, cb
, data
, reverse
);
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
));
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);