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
, *artist_name_fancy
= NULL
;
540 struct artist
*artist
;
544 if (is_url(ti
->filename
)) {
545 artist_name
= "<Stream>";
546 album_name
= "<Stream>";
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 */
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
,
586 strlen(artist
->name
)))
587 /* same name, but starting with "The" */
588 update_artist_name(artist
, xstrjoin(artist_name_no_the
, ", The"));
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
);
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 */
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
);
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
;
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
;
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
);
651 /* all tracks removed => album removed
652 * if the last album was removed then the artist was removed too
658 static void remove_sel_album(struct album
*album
)
660 struct list_head
*item
, *head
;
662 head
= &album
->track_head
;
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
);
673 static void tree_win_remove_sel(void)
675 struct artist
*artist
;
678 tree_win_get_selected(&artist
, &album
);
680 remove_sel_album(album
);
682 remove_sel_artist(artist
);
686 static void track_win_remove_sel(void)
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
;
704 tree_win_get_selected(&artist
, &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)
720 struct artist
*artist
;
722 window_get_sel(lib_tree_win
, &sel
);
723 artist
= iter_to_artist(&sel
);
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
;
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
)) {
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
) {
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
)
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
);
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
;
790 if (list_empty(&artist
->album_head
)) {
791 artist
->expanded
= 0;
792 remove_artist(artist
);
798 void tree_remove_sel(void)
800 if (lib_cur_win
== lib_tree_win
) {
801 tree_win_remove_sel();
803 track_win_remove_sel();
807 void tree_sel_current(void)
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
;
835 list_for_each_entry_reverse(track
, &album
->track_head
, node
) {
836 rc
= cb(data
, tree_track_info(track
));
841 list_for_each_entry(track
, &album
->track_head
, node
) {
842 rc
= cb(data
, tree_track_info(track
));
850 static int artist_for_each_track(struct artist
*artist
, int (*cb
)(void *data
, struct track_info
*ti
),
851 void *data
, int reverse
)
857 list_for_each_entry_reverse(album
, &artist
->album_head
, node
) {
858 rc
= album_for_each_track(album
, cb
, data
, 1);
863 list_for_each_entry(album
, &artist
->album_head
, node
) {
864 rc
= album_for_each_track(album
, cb
, data
, 0);
872 int __tree_for_each_sel(int (*cb
)(void *data
, struct track_info
*ti
), void *data
, int reverse
)
876 if (lib_cur_win
== lib_tree_win
) {
877 struct artist
*artist
;
880 tree_win_get_selected(&artist
, &album
);
883 rc
= artist_for_each_track(artist
, cb
, data
, reverse
);
885 rc
= album_for_each_track(album
, cb
, data
, reverse
);
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
));
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);