2 * Copyright 2006 Timo Hirvonen
6 #include "search_mode.h"
11 #include "mergesort.h"
14 struct searchable
*tree_searchable
;
15 struct window
*lib_tree_win
;
16 struct window
*lib_track_win
;
17 struct window
*lib_cur_win
;
18 LIST_HEAD(lib_artist_head
);
20 /* tree (search) iterators {{{ */
21 static int tree_search_get_prev(struct iter
*iter
)
23 struct list_head
*head
= iter
->data0
;
24 struct tree_track
*track
= iter
->data1
;
25 struct artist
*artist
;
32 /* head, get last track */
33 if (head
->prev
== head
) {
34 /* empty, iter points to the head already */
37 artist
= to_artist(head
->prev
);
38 album
= to_album(artist
->album_head
.prev
);
39 iter
->data1
= to_tree_track(album
->track_head
.prev
);
43 if (track
->node
.prev
== &track
->album
->track_head
|| search_restricted
) {
45 if (track
->album
->node
.prev
== &track
->album
->artist
->album_head
) {
47 if (track
->album
->artist
->node
.prev
== &lib_artist_head
)
49 artist
= to_artist(track
->album
->artist
->node
.prev
);
50 album
= to_album(artist
->album_head
.prev
);
51 track
= to_tree_track(album
->track_head
.prev
);
53 album
= to_album(track
->album
->node
.prev
);
54 track
= to_tree_track(album
->track_head
.prev
);
57 track
= to_tree_track(track
->node
.prev
);
63 static int tree_search_get_next(struct iter
*iter
)
65 struct list_head
*head
= iter
->data0
;
66 struct tree_track
*track
= iter
->data1
;
67 struct artist
*artist
;
74 /* head, get first track */
75 if (head
->next
== head
) {
76 /* empty, iter points to the head already */
79 artist
= to_artist(head
->next
);
80 album
= to_album(artist
->album_head
.next
);
81 iter
->data1
= to_tree_track(album
->track_head
.next
);
85 if (track
->node
.next
== &track
->album
->track_head
|| search_restricted
) {
87 if (track
->album
->node
.next
== &track
->album
->artist
->album_head
) {
89 if (track
->album
->artist
->node
.next
== &lib_artist_head
)
91 artist
= to_artist(track
->album
->artist
->node
.next
);
92 album
= to_album(artist
->album_head
.next
);
93 track
= to_tree_track(album
->track_head
.next
);
95 album
= to_album(track
->album
->node
.next
);
96 track
= to_tree_track(album
->track_head
.next
);
99 track
= to_tree_track(track
->node
.next
);
106 /* tree window iterators {{{ */
107 static int tree_get_prev(struct iter
*iter
)
109 struct list_head
*head
= iter
->data0
;
110 struct artist
*artist
= iter
->data1
;
111 struct album
*album
= iter
->data2
;
113 BUG_ON(head
== NULL
);
114 BUG_ON(artist
== NULL
&& album
!= NULL
);
115 if (artist
== NULL
) {
116 /* head, get last artist and/or album */
117 if (head
->prev
== head
) {
118 /* empty, iter points to the head already */
121 artist
= to_artist(head
->prev
);
122 if (artist
->expanded
) {
123 album
= to_album(artist
->album_head
.prev
);
127 iter
->data1
= artist
;
131 if (artist
->expanded
&& album
) {
133 if (album
->node
.prev
== &artist
->album_head
) {
137 iter
->data2
= to_album(album
->node
.prev
);
143 if (artist
->node
.prev
== &lib_artist_head
) {
148 artist
= to_artist(artist
->node
.prev
);
149 iter
->data1
= artist
;
151 if (artist
->expanded
) {
153 iter
->data2
= to_album(artist
->album_head
.prev
);
158 static int tree_get_next(struct iter
*iter
)
160 struct list_head
*head
= iter
->data0
;
161 struct artist
*artist
= iter
->data1
;
162 struct album
*album
= iter
->data2
;
164 BUG_ON(head
== NULL
);
165 BUG_ON(artist
== NULL
&& album
!= NULL
);
166 if (artist
== NULL
) {
167 /* head, get first artist */
168 if (head
->next
== head
) {
169 /* empty, iter points to the head already */
172 iter
->data1
= to_artist(head
->next
);
176 if (artist
->expanded
) {
180 iter
->data2
= to_album(artist
->album_head
.next
);
183 if (album
->node
.next
!= &artist
->album_head
) {
184 iter
->data2
= to_album(album
->node
.next
);
190 if (artist
->node
.next
== head
) {
195 iter
->data1
= to_artist(artist
->node
.next
);
201 static GENERIC_ITER_PREV(tree_track_get_prev
, struct tree_track
, node
)
202 static GENERIC_ITER_NEXT(tree_track_get_next
, struct tree_track
, node
)
204 static inline void tree_search_track_to_iter(struct tree_track
*track
, struct iter
*iter
)
206 iter
->data0
= &lib_artist_head
;
211 static inline void album_to_iter(struct album
*album
, struct iter
*iter
)
213 iter
->data0
= &lib_artist_head
;
214 iter
->data1
= album
->artist
;
218 static inline void artist_to_iter(struct artist
*artist
, struct iter
*iter
)
220 iter
->data0
= &lib_artist_head
;
221 iter
->data1
= artist
;
225 static inline void tree_track_to_iter(struct tree_track
*track
, struct iter
*iter
)
227 iter
->data0
= &track
->album
->track_head
;
232 /* search (tree) {{{ */
233 static int tree_search_get_current(void *data
, struct iter
*iter
)
235 struct artist
*artist
;
237 struct tree_track
*track
;
240 if (list_empty(&lib_artist_head
))
242 if (window_get_sel(lib_track_win
, &tmpiter
)) {
243 track
= iter_to_tree_track(&tmpiter
);
244 tree_search_track_to_iter(track
, iter
);
248 /* artist not expanded. track_win is empty
249 * set tmp to the first track of the selected artist */
250 window_get_sel(lib_tree_win
, &tmpiter
);
251 artist
= iter_to_artist(&tmpiter
);
252 album
= to_album(artist
->album_head
.next
);
253 track
= to_tree_track(album
->track_head
.next
);
254 tree_search_track_to_iter(track
, iter
);
258 static inline struct tree_track
*iter_to_tree_search_track(const struct iter
*iter
)
260 BUG_ON(iter
->data0
!= &lib_artist_head
);
264 static int tree_search_matches(void *data
, struct iter
*iter
, const char *text
)
266 struct tree_track
*track
;
268 unsigned int flags
= TI_MATCH_ARTIST
| TI_MATCH_ALBUM
;
270 if (!search_restricted
)
271 flags
|= TI_MATCH_TITLE
;
272 track
= iter_to_tree_search_track(iter
);
273 if (!track_info_matches(tree_track_info(track
), text
, flags
))
275 track
->album
->artist
->expanded
= 1;
276 album_to_iter(track
->album
, &tmpiter
);
277 window_set_sel(lib_tree_win
, &tmpiter
);
279 tree_track_to_iter(track
, &tmpiter
);
280 window_set_sel(lib_track_win
, &tmpiter
);
284 static const struct searchable_ops tree_search_ops
= {
285 .get_prev
= tree_search_get_prev
,
286 .get_next
= tree_search_get_next
,
287 .get_current
= tree_search_get_current
,
288 .matches
= tree_search_matches
290 /* search (tree) }}} */
292 static inline int album_selected(struct album
*album
)
296 if (window_get_sel(lib_tree_win
, &sel
))
297 return album
== iter_to_album(&sel
);
301 static void tree_sel_changed(void)
306 window_get_sel(lib_tree_win
, &sel
);
307 album
= iter_to_album(&sel
);
309 window_set_empty(lib_track_win
);
311 window_set_contents(lib_track_win
, &album
->track_head
);
315 static inline void tree_win_get_selected(struct artist
**artist
, struct album
**album
)
321 if (window_get_sel(lib_tree_win
, &sel
)) {
322 *artist
= iter_to_artist(&sel
);
323 *album
= iter_to_album(&sel
);
327 static void artist_free(struct artist
*artist
)
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 void find_artist_and_album(const char *artist_name
,
401 const char *album_name
, struct artist
**_artist
,
402 struct album
**_album
)
404 struct artist
*artist
;
407 list_for_each_entry(artist
, &lib_artist_head
, node
) {
410 res
= u_strcasecmp(artist
->name
, artist_name
);
413 list_for_each_entry(album
, &artist
->album_head
, node
) {
414 res
= u_strcasecmp(album
->name
, album_name
);
429 static int special_name_cmp(const char *a
, const char *b
)
431 /* keep <Stream> etc. top */
432 int cmp
= (*a
!= '<') - (*b
!= '<');
436 return u_strcasecmp(a
, b
);
439 static void insert_artist(struct artist
*artist
)
441 const char *a
= artist
->name
;
442 struct list_head
*item
;
444 if (fuzzy_artist_sort
)
445 a
= artist_name_skip_the(a
);
447 list_for_each(item
, &lib_artist_head
) {
448 const char *b
= to_artist(item
)->name
;
450 if (fuzzy_artist_sort
)
451 b
= artist_name_skip_the(b
);
453 if (special_name_cmp(a
, b
) < 0)
456 /* add before item */
457 list_add_tail(&artist
->node
, item
);
460 static int artist_cmp(const struct list_head
*a
, const struct list_head
*b
)
462 return special_name_cmp(to_artist(a
)->name
, to_artist(b
)->name
);
465 static int fuzzy_artist_cmp(const struct list_head
*a
, const struct list_head
*b
)
467 return special_name_cmp(artist_name_skip_the(to_artist(a
)->name
),
468 artist_name_skip_the(to_artist(b
)->name
));
471 void tree_sort_artists(void)
473 if (fuzzy_artist_sort
)
474 list_mergesort(&lib_artist_head
, fuzzy_artist_cmp
);
476 list_mergesort(&lib_artist_head
, artist_cmp
);
477 window_changed(lib_tree_win
);
480 static struct artist
*add_artist(const char *name
)
482 struct artist
*artist
;
484 artist
= xnew(struct artist
, 1);
485 artist
->name
= xstrdup(name
);
486 list_init(&artist
->album_head
);
487 artist
->expanded
= 0;
489 insert_artist(artist
);
493 static struct album
*artist_add_album(struct artist
*artist
, const char *name
, int date
)
495 struct list_head
*item
;
498 album
= xnew(struct album
, 1);
499 album
->name
= xstrdup(name
);
501 list_init(&album
->track_head
);
502 album
->artist
= artist
;
504 list_for_each(item
, &artist
->album_head
) {
505 struct album
*a
= to_album(item
);
511 if (special_name_cmp(name
, a
->name
) < 0)
514 /* add before item */
515 list_add_tail(&album
->node
, item
);
519 static void album_add_track(struct album
*album
, struct tree_track
*track
)
522 * NOTE: This is not perfect. You should ignore track numbers if
523 * either is unset and use filename instead, but usually you
524 * have all track numbers set or all unset (within one album
527 static const char * const album_track_sort_keys
[] = {
528 "discnumber", "tracknumber", "filename", NULL
530 struct list_head
*item
;
532 track
->album
= album
;
533 list_for_each(item
, &album
->track_head
) {
534 const struct simple_track
*a
= (const struct simple_track
*)track
;
535 const struct simple_track
*b
= (const struct simple_track
*)to_tree_track(item
);
537 if (track_info_cmp(a
->info
, b
->info
, album_track_sort_keys
) < 0)
540 /* add before item */
541 list_add_tail(&track
->node
, item
);
544 void tree_add_track(struct tree_track
*track
)
546 const struct track_info
*ti
= tree_track_info(track
);
547 const char *album_name
, *artist_name
;
548 struct artist
*artist
;
552 if (is_url(ti
->filename
)) {
553 artist_name
= "<Stream>";
554 album_name
= "<Stream>";
556 album_name
= keyvals_get_val(ti
->comments
, "album");
558 artist_name
= keyvals_get_val(ti
->comments
, "albumartistsort");
560 artist_name
= keyvals_get_val(ti
->comments
, "albumartist");
562 artist_name
= keyvals_get_val(ti
->comments
, "artistsort");
564 const char *compilation
= keyvals_get_val(ti
->comments
, "compilation");
565 if (compilation
&& (!strcasecmp(compilation
, "1") ||
566 !strcasecmp(compilation
, "yes")))
567 artist_name
= "<Compilations>";
570 artist_name
= keyvals_get_val(ti
->comments
, "artist");
572 if (artist_name
== NULL
)
573 artist_name
= "<No Name>";
574 if (album_name
== NULL
)
575 album_name
= "<No Name>";
579 find_artist_and_album(artist_name
, album_name
, &artist
, &album
);
581 album_add_track(album
, track
);
583 /* is the album where we added the track selected? */
584 if (album_selected(album
)) {
585 /* update track window */
586 window_changed(lib_track_win
);
589 date
= comments_get_date(ti
->comments
, "date");
590 album
= artist_add_album(artist
, album_name
, date
);
591 album_add_track(album
, track
);
593 if (artist
->expanded
) {
594 /* update tree window */
595 window_changed(lib_tree_win
);
596 /* album is not selected => no need to update track_win */
599 date
= comments_get_date(ti
->comments
, "date");
600 artist
= add_artist(artist_name
);
601 album
= artist_add_album(artist
, album_name
, date
);
602 album_add_track(album
, track
);
604 window_changed(lib_tree_win
);
608 static void remove_sel_artist(struct artist
*artist
)
610 struct list_head
*aitem
, *ahead
;
612 ahead
= &artist
->album_head
;
614 while (aitem
!= ahead
) {
615 struct list_head
*titem
, *thead
;
616 struct list_head
*anext
= aitem
->next
;
617 struct album
*album
= to_album(aitem
);
619 thead
= &album
->track_head
;
621 while (titem
!= thead
) {
622 struct list_head
*tnext
= titem
->next
;
623 struct tree_track
*track
= to_tree_track(titem
);
625 editable_remove_track(&lib_editable
, (struct simple_track
*)track
);
628 /* all tracks removed => album removed
629 * if the last album was removed then the artist was removed too
635 static void remove_sel_album(struct album
*album
)
637 struct list_head
*item
, *head
;
639 head
= &album
->track_head
;
641 while (item
!= head
) {
642 struct list_head
*next
= item
->next
;
643 struct tree_track
*track
= to_tree_track(item
);
645 editable_remove_track(&lib_editable
, (struct simple_track
*)track
);
650 static void tree_win_remove_sel(void)
652 struct artist
*artist
;
655 tree_win_get_selected(&artist
, &album
);
657 remove_sel_album(album
);
659 remove_sel_artist(artist
);
663 static void track_win_remove_sel(void)
666 struct tree_track
*track
;
668 if (window_get_sel(lib_track_win
, &sel
)) {
669 track
= iter_to_tree_track(&sel
);
670 BUG_ON(track
== NULL
);
671 editable_remove_track(&lib_editable
, (struct simple_track
*)track
);
675 void tree_toggle_active_window(void)
677 if (lib_cur_win
== lib_tree_win
) {
678 struct artist
*artist
;
681 tree_win_get_selected(&artist
, &album
);
683 lib_cur_win
= lib_track_win
;
684 lib_tree_win
->changed
= 1;
685 lib_track_win
->changed
= 1;
687 } else if (lib_cur_win
== lib_track_win
) {
688 lib_cur_win
= lib_tree_win
;
689 lib_tree_win
->changed
= 1;
690 lib_track_win
->changed
= 1;
694 void tree_toggle_expand_artist(void)
697 struct artist
*artist
;
699 window_get_sel(lib_tree_win
, &sel
);
700 artist
= iter_to_artist(&sel
);
702 if (artist
->expanded
) {
703 /* deselect album, select artist */
704 artist_to_iter(artist
, &sel
);
705 window_set_sel(lib_tree_win
, &sel
);
707 artist
->expanded
= 0;
708 lib_cur_win
= lib_tree_win
;
710 artist
->expanded
= 1;
712 window_changed(lib_tree_win
);
716 static void remove_track(struct tree_track
*track
)
718 if (album_selected(track
->album
)) {
721 tree_track_to_iter(track
, &iter
);
722 window_row_vanishes(lib_track_win
, &iter
);
724 list_del(&track
->node
);
727 static void remove_album(struct album
*album
)
729 if (album
->artist
->expanded
) {
732 album_to_iter(album
, &iter
);
733 window_row_vanishes(lib_tree_win
, &iter
);
735 list_del(&album
->node
);
738 static void remove_artist(struct artist
*artist
)
742 artist_to_iter(artist
, &iter
);
743 window_row_vanishes(lib_tree_win
, &iter
);
744 list_del(&artist
->node
);
747 void tree_remove(struct tree_track
*track
)
749 struct album
*album
= track
->album
;
750 struct artist
*sel_artist
;
751 struct album
*sel_album
;
753 tree_win_get_selected(&sel_artist
, &sel_album
);
756 /* don't free the track */
758 if (list_empty(&album
->track_head
)) {
759 struct artist
*artist
= album
->artist
;
761 if (sel_album
== album
)
762 lib_cur_win
= lib_tree_win
;
767 if (list_empty(&artist
->album_head
)) {
768 artist
->expanded
= 0;
769 remove_artist(artist
);
775 void tree_remove_sel(void)
777 if (lib_cur_win
== lib_tree_win
) {
778 tree_win_remove_sel();
780 track_win_remove_sel();
784 void tree_sel_current(void)
789 CUR_ARTIST
->expanded
= 1;
791 if (lib_cur_win
== lib_tree_win
) {
792 lib_cur_win
= lib_track_win
;
793 lib_tree_win
->changed
= 1;
794 lib_track_win
->changed
= 1;
797 album_to_iter(CUR_ALBUM
, &iter
);
798 window_set_sel(lib_tree_win
, &iter
);
800 tree_track_to_iter(lib_cur_track
, &iter
);
801 window_set_sel(lib_track_win
, &iter
);
805 static int album_for_each_track(struct album
*album
, int (*cb
)(void *data
, struct track_info
*ti
),
806 void *data
, int reverse
)
808 struct tree_track
*track
;
812 list_for_each_entry_reverse(track
, &album
->track_head
, node
) {
813 rc
= cb(data
, tree_track_info(track
));
818 list_for_each_entry(track
, &album
->track_head
, node
) {
819 rc
= cb(data
, tree_track_info(track
));
827 static int artist_for_each_track(struct artist
*artist
, int (*cb
)(void *data
, struct track_info
*ti
),
828 void *data
, int reverse
)
834 list_for_each_entry_reverse(album
, &artist
->album_head
, node
) {
835 rc
= album_for_each_track(album
, cb
, data
, 1);
840 list_for_each_entry(album
, &artist
->album_head
, node
) {
841 rc
= album_for_each_track(album
, cb
, data
, 0);
849 int __tree_for_each_sel(int (*cb
)(void *data
, struct track_info
*ti
), void *data
, int reverse
)
853 if (lib_cur_win
== lib_tree_win
) {
854 struct artist
*artist
;
857 tree_win_get_selected(&artist
, &album
);
860 rc
= artist_for_each_track(artist
, cb
, data
, reverse
);
862 rc
= album_for_each_track(album
, cb
, data
, reverse
);
867 struct tree_track
*track
;
869 if (window_get_sel(lib_track_win
, &sel
)) {
870 track
= iter_to_tree_track(&sel
);
871 rc
= cb(data
, tree_track_info(track
));
877 int tree_for_each_sel(int (*cb
)(void *data
, struct track_info
*ti
), void *data
, int reverse
)
879 int rc
= __tree_for_each_sel(cb
, data
, reverse
);
881 window_down(lib_cur_win
, 1);