2 * Copyright 2006 Timo Hirvonen
6 #include "search_mode.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
;
29 /* head, get last track */
30 if (head
->prev
== head
) {
31 /* empty, iter points to the head already */
34 artist
= to_artist(head
->prev
);
35 album
= to_album(artist
->album_head
.prev
);
36 iter
->data1
= to_tree_track(album
->track_head
.prev
);
40 if (track
->node
.prev
== &track
->album
->track_head
|| search_restricted
) {
42 if (track
->album
->node
.prev
== &track
->album
->artist
->album_head
) {
44 if (track
->album
->artist
->node
.prev
== &lib_artist_head
)
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
);
50 album
= to_album(track
->album
->node
.prev
);
51 track
= to_tree_track(album
->track_head
.prev
);
54 track
= to_tree_track(track
->node
.prev
);
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
;
71 /* head, get first track */
72 if (head
->next
== head
) {
73 /* empty, iter points to the head already */
76 artist
= to_artist(head
->next
);
77 album
= to_album(artist
->album_head
.next
);
78 iter
->data1
= to_tree_track(album
->track_head
.next
);
82 if (track
->node
.next
== &track
->album
->track_head
|| search_restricted
) {
84 if (track
->album
->node
.next
== &track
->album
->artist
->album_head
) {
86 if (track
->album
->artist
->node
.next
== &lib_artist_head
)
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
);
92 album
= to_album(track
->album
->node
.next
);
93 track
= to_tree_track(album
->track_head
.next
);
96 track
= to_tree_track(track
->node
.next
);
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 */
118 artist
= to_artist(head
->prev
);
119 if (artist
->expanded
) {
120 album
= to_album(artist
->album_head
.prev
);
124 iter
->data1
= artist
;
128 if (artist
->expanded
&& album
) {
130 if (album
->node
.prev
== &artist
->album_head
) {
134 iter
->data2
= to_album(album
->node
.prev
);
140 if (artist
->node
.prev
== &lib_artist_head
) {
145 artist
= to_artist(artist
->node
.prev
);
146 iter
->data1
= artist
;
148 if (artist
->expanded
) {
150 iter
->data2
= to_album(artist
->album_head
.prev
);
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 */
169 iter
->data1
= to_artist(head
->next
);
173 if (artist
->expanded
) {
177 iter
->data2
= to_album(artist
->album_head
.next
);
180 if (album
->node
.next
!= &artist
->album_head
) {
181 iter
->data2
= to_album(album
->node
.next
);
187 if (artist
->node
.next
== head
) {
192 iter
->data1
= to_artist(artist
->node
.next
);
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
;
208 static inline void album_to_iter(struct album
*album
, struct iter
*iter
)
210 iter
->data0
= &lib_artist_head
;
211 iter
->data1
= album
->artist
;
215 static inline void artist_to_iter(struct artist
*artist
, struct iter
*iter
)
217 iter
->data0
= &lib_artist_head
;
218 iter
->data1
= artist
;
222 static inline void tree_track_to_iter(struct tree_track
*track
, struct iter
*iter
)
224 iter
->data0
= &track
->album
->track_head
;
229 /* search (tree) {{{ */
230 static int tree_search_get_current(void *data
, struct iter
*iter
)
232 struct artist
*artist
;
234 struct tree_track
*track
;
237 if (list_empty(&lib_artist_head
))
239 if (window_get_sel(lib_track_win
, &tmpiter
)) {
240 track
= iter_to_tree_track(&tmpiter
);
241 tree_search_track_to_iter(track
, iter
);
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
);
255 static inline struct tree_track
*iter_to_tree_search_track(const struct iter
*iter
)
257 BUG_ON(iter
->data0
!= &lib_artist_head
);
261 static int tree_search_matches(void *data
, struct iter
*iter
, const char *text
)
263 struct tree_track
*track
;
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
))
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
);
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
)
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
)
332 static void album_free(struct album
*album
)
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
;
356 tree_searchable
= searchable_new(NULL
, &iter
, &tree_search_ops
);
359 struct track_info
*tree_set_selected(void)
361 struct artist
*artist
;
363 struct track_info
*info
;
366 if (list_empty(&lib_artist_head
))
369 tree_win_get_selected(&artist
, &album
);
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
);
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
);
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
;
396 list_for_each_entry(artist
, &lib_artist_head
, node
) {
399 res
= xstrcasecmp(artist
->name
, artist_name
);
402 list_for_each_entry(album
, &artist
->album_head
, node
) {
403 res
= xstrcasecmp(album
->name
, album_name
);
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)
433 /* add before item */
434 list_add_tail(&artist
->node
, item
);
438 static struct album
*artist_add_album(struct artist
*artist
, const char *name
, int date
)
440 struct list_head
*item
;
443 album
= xnew(struct album
, 1);
444 album
->name
= xxstrdup(name
);
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
);
455 if (xstrcasecmp(name
, a
->name
) < 0)
458 /* add before item */
459 list_add_tail(&album
->node
, item
);
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
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)
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
;
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
);
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
);
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 */
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
;
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
;
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
);
553 /* all tracks removed => album removed
554 * if the last album was removed then the artist was removed too
560 static void remove_sel_album(struct album
*album
)
562 struct list_head
*item
, *head
;
564 head
= &album
->track_head
;
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
);
575 static void tree_win_remove_sel(void)
577 struct artist
*artist
;
580 tree_win_get_selected(&artist
, &album
);
582 remove_sel_album(album
);
584 remove_sel_artist(artist
);
588 static void track_win_remove_sel(void)
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
;
606 tree_win_get_selected(&artist
, &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)
622 struct artist
*artist
;
624 window_get_sel(lib_tree_win
, &sel
);
625 artist
= iter_to_artist(&sel
);
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
;
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
)) {
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
) {
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
)
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
);
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
;
692 if (list_empty(&artist
->album_head
)) {
693 artist
->expanded
= 0;
694 remove_artist(artist
);
700 void tree_remove_sel(void)
702 if (lib_cur_win
== lib_tree_win
) {
703 tree_win_remove_sel();
705 track_win_remove_sel();
709 void tree_sel_current(void)
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
;
737 list_for_each_entry_reverse(track
, &album
->track_head
, node
) {
738 rc
= cb(data
, tree_track_info(track
));
743 list_for_each_entry(track
, &album
->track_head
, node
) {
744 rc
= cb(data
, tree_track_info(track
));
752 static int artist_for_each_track(struct artist
*artist
, int (*cb
)(void *data
, struct track_info
*ti
),
753 void *data
, int reverse
)
759 list_for_each_entry_reverse(album
, &artist
->album_head
, node
) {
760 rc
= album_for_each_track(album
, cb
, data
, 1);
765 list_for_each_entry(album
, &artist
->album_head
, node
) {
766 rc
= album_for_each_track(album
, cb
, data
, 0);
774 int __tree_for_each_sel(int (*cb
)(void *data
, struct track_info
*ti
), void *data
, int reverse
)
778 if (lib_cur_win
== lib_tree_win
) {
779 struct artist
*artist
;
782 tree_win_get_selected(&artist
, &album
);
785 rc
= artist_for_each_track(artist
, cb
, data
, reverse
);
787 rc
= album_for_each_track(album
, cb
, data
, reverse
);
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
));
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);