2 * Copyright 2006 Timo Hirvonen
6 #include "search_mode.h"
12 struct searchable
*tree_searchable
;
13 struct window
*lib_tree_win
;
14 struct window
*lib_track_win
;
15 struct window
*lib_cur_win
;
16 LIST_HEAD(lib_artist_head
);
18 /* tree (search) iterators {{{ */
19 static int tree_search_get_prev(struct iter
*iter
)
21 struct list_head
*head
= iter
->data0
;
22 struct tree_track
*track
= iter
->data1
;
23 struct artist
*artist
;
30 /* head, get last track */
31 if (head
->prev
== head
) {
32 /* empty, iter points to the head already */
35 artist
= to_artist(head
->prev
);
36 album
= to_album(artist
->album_head
.prev
);
37 iter
->data1
= to_tree_track(album
->track_head
.prev
);
41 if (track
->node
.prev
== &track
->album
->track_head
|| search_restricted
) {
43 if (track
->album
->node
.prev
== &track
->album
->artist
->album_head
) {
45 if (track
->album
->artist
->node
.prev
== &lib_artist_head
)
47 artist
= to_artist(track
->album
->artist
->node
.prev
);
48 album
= to_album(artist
->album_head
.prev
);
49 track
= to_tree_track(album
->track_head
.prev
);
51 album
= to_album(track
->album
->node
.prev
);
52 track
= to_tree_track(album
->track_head
.prev
);
55 track
= to_tree_track(track
->node
.prev
);
61 static int tree_search_get_next(struct iter
*iter
)
63 struct list_head
*head
= iter
->data0
;
64 struct tree_track
*track
= iter
->data1
;
65 struct artist
*artist
;
72 /* head, get first track */
73 if (head
->next
== head
) {
74 /* empty, iter points to the head already */
77 artist
= to_artist(head
->next
);
78 album
= to_album(artist
->album_head
.next
);
79 iter
->data1
= to_tree_track(album
->track_head
.next
);
83 if (track
->node
.next
== &track
->album
->track_head
|| search_restricted
) {
85 if (track
->album
->node
.next
== &track
->album
->artist
->album_head
) {
87 if (track
->album
->artist
->node
.next
== &lib_artist_head
)
89 artist
= to_artist(track
->album
->artist
->node
.next
);
90 album
= to_album(artist
->album_head
.next
);
91 track
= to_tree_track(album
->track_head
.next
);
93 album
= to_album(track
->album
->node
.next
);
94 track
= to_tree_track(album
->track_head
.next
);
97 track
= to_tree_track(track
->node
.next
);
104 /* tree window iterators {{{ */
105 static int tree_get_prev(struct iter
*iter
)
107 struct list_head
*head
= iter
->data0
;
108 struct artist
*artist
= iter
->data1
;
109 struct album
*album
= iter
->data2
;
111 BUG_ON(head
== NULL
);
112 BUG_ON(artist
== NULL
&& album
!= NULL
);
113 if (artist
== NULL
) {
114 /* head, get last artist and/or album */
115 if (head
->prev
== head
) {
116 /* empty, iter points to the head already */
119 artist
= to_artist(head
->prev
);
120 if (artist
->expanded
) {
121 album
= to_album(artist
->album_head
.prev
);
125 iter
->data1
= artist
;
129 if (artist
->expanded
&& album
) {
131 if (album
->node
.prev
== &artist
->album_head
) {
135 iter
->data2
= to_album(album
->node
.prev
);
141 if (artist
->node
.prev
== &lib_artist_head
) {
146 artist
= to_artist(artist
->node
.prev
);
147 iter
->data1
= artist
;
149 if (artist
->expanded
) {
151 iter
->data2
= to_album(artist
->album_head
.prev
);
156 static int tree_get_next(struct iter
*iter
)
158 struct list_head
*head
= iter
->data0
;
159 struct artist
*artist
= iter
->data1
;
160 struct album
*album
= iter
->data2
;
162 BUG_ON(head
== NULL
);
163 BUG_ON(artist
== NULL
&& album
!= NULL
);
164 if (artist
== NULL
) {
165 /* head, get first artist */
166 if (head
->next
== head
) {
167 /* empty, iter points to the head already */
170 iter
->data1
= to_artist(head
->next
);
174 if (artist
->expanded
) {
178 iter
->data2
= to_album(artist
->album_head
.next
);
181 if (album
->node
.next
!= &artist
->album_head
) {
182 iter
->data2
= to_album(album
->node
.next
);
188 if (artist
->node
.next
== head
) {
193 iter
->data1
= to_artist(artist
->node
.next
);
199 static GENERIC_ITER_PREV(tree_track_get_prev
, struct tree_track
, node
)
200 static GENERIC_ITER_NEXT(tree_track_get_next
, struct tree_track
, node
)
202 static inline void tree_search_track_to_iter(struct tree_track
*track
, struct iter
*iter
)
204 iter
->data0
= &lib_artist_head
;
209 static inline void album_to_iter(struct album
*album
, struct iter
*iter
)
211 iter
->data0
= &lib_artist_head
;
212 iter
->data1
= album
->artist
;
216 static inline void artist_to_iter(struct artist
*artist
, struct iter
*iter
)
218 iter
->data0
= &lib_artist_head
;
219 iter
->data1
= artist
;
223 static inline void tree_track_to_iter(struct tree_track
*track
, struct iter
*iter
)
225 iter
->data0
= &track
->album
->track_head
;
230 /* search (tree) {{{ */
231 static int tree_search_get_current(void *data
, struct iter
*iter
)
233 struct artist
*artist
;
235 struct tree_track
*track
;
238 if (list_empty(&lib_artist_head
))
240 if (window_get_sel(lib_track_win
, &tmpiter
)) {
241 track
= iter_to_tree_track(&tmpiter
);
242 tree_search_track_to_iter(track
, iter
);
246 /* artist not expanded. track_win is empty
247 * set tmp to the first track of the selected artist */
248 window_get_sel(lib_tree_win
, &tmpiter
);
249 artist
= iter_to_artist(&tmpiter
);
250 album
= to_album(artist
->album_head
.next
);
251 track
= to_tree_track(album
->track_head
.next
);
252 tree_search_track_to_iter(track
, iter
);
256 static inline struct tree_track
*iter_to_tree_search_track(const struct iter
*iter
)
258 BUG_ON(iter
->data0
!= &lib_artist_head
);
262 static int tree_search_matches(void *data
, struct iter
*iter
, const char *text
)
264 struct tree_track
*track
;
266 unsigned int flags
= TI_MATCH_ARTIST
| TI_MATCH_ALBUM
;
268 if (!search_restricted
)
269 flags
|= TI_MATCH_TITLE
;
270 track
= iter_to_tree_search_track(iter
);
271 if (!track_info_matches(tree_track_info(track
), text
, flags
))
273 track
->album
->artist
->expanded
= 1;
274 album_to_iter(track
->album
, &tmpiter
);
275 window_set_sel(lib_tree_win
, &tmpiter
);
277 tree_track_to_iter(track
, &tmpiter
);
278 window_set_sel(lib_track_win
, &tmpiter
);
282 static const struct searchable_ops tree_search_ops
= {
283 .get_prev
= tree_search_get_prev
,
284 .get_next
= tree_search_get_next
,
285 .get_current
= tree_search_get_current
,
286 .matches
= tree_search_matches
288 /* search (tree) }}} */
290 static inline int album_selected(struct album
*album
)
294 if (window_get_sel(lib_tree_win
, &sel
))
295 return album
== iter_to_album(&sel
);
299 static void tree_sel_changed(void)
304 window_get_sel(lib_tree_win
, &sel
);
305 album
= iter_to_album(&sel
);
307 window_set_empty(lib_track_win
);
309 window_set_contents(lib_track_win
, &album
->track_head
);
313 static inline void tree_win_get_selected(struct artist
**artist
, struct album
**album
)
319 if (window_get_sel(lib_tree_win
, &sel
)) {
320 *artist
= iter_to_artist(&sel
);
321 *album
= iter_to_album(&sel
);
325 static void artist_free(struct artist
*artist
)
331 static void album_free(struct album
*album
)
341 list_init(&lib_artist_head
);
343 lib_tree_win
= window_new(tree_get_prev
, tree_get_next
);
344 lib_track_win
= window_new(tree_track_get_prev
, tree_track_get_next
);
345 lib_cur_win
= lib_tree_win
;
347 lib_tree_win
->sel_changed
= tree_sel_changed
;
349 window_set_empty(lib_track_win
);
350 window_set_contents(lib_tree_win
, &lib_artist_head
);
352 iter
.data0
= &lib_artist_head
;
355 tree_searchable
= searchable_new(NULL
, &iter
, &tree_search_ops
);
358 struct track_info
*tree_set_selected(void)
360 struct artist
*artist
;
362 struct track_info
*info
;
365 if (list_empty(&lib_artist_head
))
368 tree_win_get_selected(&artist
, &album
);
370 /* only artist selected, track window is empty
371 * => get first album of the selected artist and first track of that album
373 album
= to_album(artist
->album_head
.next
);
374 lib_cur_track
= to_tree_track(album
->track_head
.next
);
376 window_get_sel(lib_track_win
, &sel
);
377 lib_cur_track
= iter_to_tree_track(&sel
);
380 lib_tree_win
->changed
= 1;
381 lib_track_win
->changed
= 1;
383 info
= tree_track_info(lib_cur_track
);
384 track_info_ref(info
);
388 static void find_artist_and_album(const char *artist_name
,
389 const char *album_name
, struct artist
**_artist
,
390 struct album
**_album
)
392 struct artist
*artist
;
395 list_for_each_entry(artist
, &lib_artist_head
, node
) {
398 res
= u_strcasecmp(artist
->name
, artist_name
);
401 list_for_each_entry(album
, &artist
->album_head
, node
) {
402 res
= u_strcasecmp(album
->name
, album_name
);
417 static int special_name_cmp(const char *a
, const char *b
)
419 /* keep <Stream> etc. top */
420 int cmp
= (*a
!= '<') - (*b
!= '<');
424 return u_strcasecmp(a
, b
);
427 static struct artist
*add_artist(const char *name
)
429 struct list_head
*item
;
430 struct artist
*artist
;
432 artist
= xnew(struct artist
, 1);
433 artist
->name
= xstrdup(name
);
434 list_init(&artist
->album_head
);
435 artist
->expanded
= 0;
436 list_for_each(item
, &lib_artist_head
) {
437 if (special_name_cmp(name
, to_artist(item
)->name
) < 0)
440 /* add before item */
441 list_add_tail(&artist
->node
, item
);
445 static struct album
*artist_add_album(struct artist
*artist
, const char *name
, int date
)
447 struct list_head
*item
;
450 album
= xnew(struct album
, 1);
451 album
->name
= xstrdup(name
);
453 list_init(&album
->track_head
);
454 album
->artist
= artist
;
455 list_for_each(item
, &artist
->album_head
) {
456 struct album
*a
= to_album(item
);
462 if (special_name_cmp(name
, a
->name
) < 0)
465 /* add before item */
466 list_add_tail(&album
->node
, item
);
470 static void album_add_track(struct album
*album
, struct tree_track
*track
)
473 * NOTE: This is not perfect. You should ignore track numbers if
474 * either is unset and use filename instead, but usually you
475 * have all track numbers set or all unset (within one album
478 static const char * const album_track_sort_keys
[] = {
479 "discnumber", "tracknumber", "filename", NULL
481 struct list_head
*item
;
483 track
->album
= album
;
484 list_for_each(item
, &album
->track_head
) {
485 const struct simple_track
*a
= (const struct simple_track
*)track
;
486 const struct simple_track
*b
= (const struct simple_track
*)to_tree_track(item
);
488 if (track_info_cmp(a
->info
, b
->info
, album_track_sort_keys
) < 0)
491 /* add before item */
492 list_add_tail(&track
->node
, item
);
495 void tree_add_track(struct tree_track
*track
)
497 const struct track_info
*ti
= tree_track_info(track
);
498 const char *album_name
, *artist_name
, *albumartist
,
499 *artist_sort
, *albumartist_sort
;
500 struct artist
*artist
;
504 album_name
= comments_get_val(ti
->comments
, "album");
505 artist_name
= comments_get_val(ti
->comments
, "artist");
506 albumartist
= comments_get_val(ti
->comments
, "albumartist");
507 artist_sort
= comments_get_val(ti
->comments
, "artistsort");
508 albumartist_sort
= comments_get_val(ti
->comments
, "albumartistsort");
510 if (is_url(ti
->filename
)) {
511 artist_name
= "<Stream>";
512 album_name
= "<Stream>";
515 if (artist_name
== NULL
)
516 artist_name
= "<No Name>";
517 if (album_name
== NULL
)
518 album_name
= "<No Name>";
520 if (albumartist_sort
)
521 artist_name
= albumartist_sort
;
522 else if (albumartist
)
523 artist_name
= albumartist
;
524 else if (artist_sort
)
525 artist_name
= artist_sort
;
527 const char *compilation
= comments_get_val(ti
->comments
, "compilation");
529 if (compilation
&& (!strcasecmp(compilation
, "1")
530 || !strcasecmp(compilation
, "yes"))) {
531 /* Store all compilations under compilations */
532 artist_name
= "<Compilations>";
536 find_artist_and_album(artist_name
, album_name
, &artist
, &album
);
538 album_add_track(album
, track
);
540 /* is the album where we added the track selected? */
541 if (album_selected(album
)) {
542 /* update track window */
543 window_changed(lib_track_win
);
546 date
= comments_get_date(ti
->comments
, "date");
547 album
= artist_add_album(artist
, album_name
, date
);
548 album_add_track(album
, track
);
550 if (artist
->expanded
) {
551 /* update tree window */
552 window_changed(lib_tree_win
);
553 /* album is not selected => no need to update track_win */
556 date
= comments_get_date(ti
->comments
, "date");
557 artist
= add_artist(artist_name
);
558 album
= artist_add_album(artist
, album_name
, date
);
559 album_add_track(album
, track
);
561 window_changed(lib_tree_win
);
565 static void remove_sel_artist(struct artist
*artist
)
567 struct list_head
*aitem
, *ahead
;
569 ahead
= &artist
->album_head
;
571 while (aitem
!= ahead
) {
572 struct list_head
*titem
, *thead
;
573 struct list_head
*anext
= aitem
->next
;
574 struct album
*album
= to_album(aitem
);
576 thead
= &album
->track_head
;
578 while (titem
!= thead
) {
579 struct list_head
*tnext
= titem
->next
;
580 struct tree_track
*track
= to_tree_track(titem
);
582 editable_remove_track(&lib_editable
, (struct simple_track
*)track
);
585 /* all tracks removed => album removed
586 * if the last album was removed then the artist was removed too
592 static void remove_sel_album(struct album
*album
)
594 struct list_head
*item
, *head
;
596 head
= &album
->track_head
;
598 while (item
!= head
) {
599 struct list_head
*next
= item
->next
;
600 struct tree_track
*track
= to_tree_track(item
);
602 editable_remove_track(&lib_editable
, (struct simple_track
*)track
);
607 static void tree_win_remove_sel(void)
609 struct artist
*artist
;
612 tree_win_get_selected(&artist
, &album
);
614 remove_sel_album(album
);
616 remove_sel_artist(artist
);
620 static void track_win_remove_sel(void)
623 struct tree_track
*track
;
625 if (window_get_sel(lib_track_win
, &sel
)) {
626 track
= iter_to_tree_track(&sel
);
627 BUG_ON(track
== NULL
);
628 editable_remove_track(&lib_editable
, (struct simple_track
*)track
);
632 void tree_toggle_active_window(void)
634 if (lib_cur_win
== lib_tree_win
) {
635 struct artist
*artist
;
638 tree_win_get_selected(&artist
, &album
);
640 lib_cur_win
= lib_track_win
;
641 lib_tree_win
->changed
= 1;
642 lib_track_win
->changed
= 1;
644 } else if (lib_cur_win
== lib_track_win
) {
645 lib_cur_win
= lib_tree_win
;
646 lib_tree_win
->changed
= 1;
647 lib_track_win
->changed
= 1;
651 void tree_toggle_expand_artist(void)
654 struct artist
*artist
;
656 window_get_sel(lib_tree_win
, &sel
);
657 artist
= iter_to_artist(&sel
);
659 if (artist
->expanded
) {
660 /* deselect album, select artist */
661 artist_to_iter(artist
, &sel
);
662 window_set_sel(lib_tree_win
, &sel
);
664 artist
->expanded
= 0;
665 lib_cur_win
= lib_tree_win
;
667 artist
->expanded
= 1;
669 window_changed(lib_tree_win
);
673 static void remove_track(struct tree_track
*track
)
675 if (album_selected(track
->album
)) {
678 tree_track_to_iter(track
, &iter
);
679 window_row_vanishes(lib_track_win
, &iter
);
681 list_del(&track
->node
);
684 static void remove_album(struct album
*album
)
686 if (album
->artist
->expanded
) {
689 album_to_iter(album
, &iter
);
690 window_row_vanishes(lib_tree_win
, &iter
);
692 list_del(&album
->node
);
695 static void remove_artist(struct artist
*artist
)
699 artist_to_iter(artist
, &iter
);
700 window_row_vanishes(lib_tree_win
, &iter
);
701 list_del(&artist
->node
);
704 void tree_remove(struct tree_track
*track
)
706 struct album
*album
= track
->album
;
707 struct artist
*sel_artist
;
708 struct album
*sel_album
;
710 tree_win_get_selected(&sel_artist
, &sel_album
);
713 /* don't free the track */
715 if (list_empty(&album
->track_head
)) {
716 struct artist
*artist
= album
->artist
;
718 if (sel_album
== album
)
719 lib_cur_win
= lib_tree_win
;
724 if (list_empty(&artist
->album_head
)) {
725 artist
->expanded
= 0;
726 remove_artist(artist
);
732 void tree_remove_sel(void)
734 if (lib_cur_win
== lib_tree_win
) {
735 tree_win_remove_sel();
737 track_win_remove_sel();
741 void tree_sel_current(void)
746 CUR_ARTIST
->expanded
= 1;
748 if (lib_cur_win
== lib_tree_win
) {
749 lib_cur_win
= lib_track_win
;
750 lib_tree_win
->changed
= 1;
751 lib_track_win
->changed
= 1;
754 album_to_iter(CUR_ALBUM
, &iter
);
755 window_set_sel(lib_tree_win
, &iter
);
757 tree_track_to_iter(lib_cur_track
, &iter
);
758 window_set_sel(lib_track_win
, &iter
);
762 static int album_for_each_track(struct album
*album
, int (*cb
)(void *data
, struct track_info
*ti
),
763 void *data
, int reverse
)
765 struct tree_track
*track
;
769 list_for_each_entry_reverse(track
, &album
->track_head
, node
) {
770 rc
= cb(data
, tree_track_info(track
));
775 list_for_each_entry(track
, &album
->track_head
, node
) {
776 rc
= cb(data
, tree_track_info(track
));
784 static int artist_for_each_track(struct artist
*artist
, int (*cb
)(void *data
, struct track_info
*ti
),
785 void *data
, int reverse
)
791 list_for_each_entry_reverse(album
, &artist
->album_head
, node
) {
792 rc
= album_for_each_track(album
, cb
, data
, 1);
797 list_for_each_entry(album
, &artist
->album_head
, node
) {
798 rc
= album_for_each_track(album
, cb
, data
, 0);
806 int __tree_for_each_sel(int (*cb
)(void *data
, struct track_info
*ti
), void *data
, int reverse
)
810 if (lib_cur_win
== lib_tree_win
) {
811 struct artist
*artist
;
814 tree_win_get_selected(&artist
, &album
);
817 rc
= artist_for_each_track(artist
, cb
, data
, reverse
);
819 rc
= album_for_each_track(album
, cb
, data
, reverse
);
824 struct tree_track
*track
;
826 if (window_get_sel(lib_track_win
, &sel
)) {
827 track
= iter_to_tree_track(&sel
);
828 rc
= cb(data
, tree_track_info(track
));
834 int tree_for_each_sel(int (*cb
)(void *data
, struct track_info
*ti
), void *data
, int reverse
)
836 int rc
= __tree_for_each_sel(cb
, data
, reverse
);
838 window_down(lib_cur_win
, 1);