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 (simple_track_cmp(&a
->node
, &b
->node
, 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 struct artist
*artist
;
503 album_name
= comments_get_val(ti
->comments
, "album");
504 artist_name
= comments_get_val(ti
->comments
, "artist");
505 albumartist
= comments_get_val(ti
->comments
, "albumartist");
507 if (is_url(ti
->filename
)) {
508 artist_name
= "<Stream>";
509 album_name
= "<Stream>";
511 if (artist_name
== NULL
)
512 artist_name
= "<No Name>";
513 if (album_name
== NULL
)
514 album_name
= "<No Name>";
517 /* FIXME: user might want to create custom categories */
518 artist_name
= "<Compilations>";
520 const char *compilation
= comments_get_val(ti
->comments
, "compilation");
522 if (compilation
&& (!strcasecmp(compilation
, "1")
523 || !strcasecmp(compilation
, "yes"))) {
524 /* Store all compilations under compilations */
525 artist_name
= "<Compilations>";
529 find_artist_and_album(artist_name
, album_name
, &artist
, &album
);
531 album_add_track(album
, track
);
533 /* is the album where we added the track selected? */
534 if (album_selected(album
)) {
535 /* update track window */
536 window_changed(lib_track_win
);
539 date
= comments_get_int(ti
->comments
, "date");
540 album
= artist_add_album(artist
, album_name
, date
);
541 album_add_track(album
, track
);
543 if (artist
->expanded
) {
544 /* update tree window */
545 window_changed(lib_tree_win
);
546 /* album is not selected => no need to update track_win */
549 date
= comments_get_int(ti
->comments
, "date");
550 artist
= add_artist(artist_name
);
551 album
= artist_add_album(artist
, album_name
, date
);
552 album_add_track(album
, track
);
554 window_changed(lib_tree_win
);
558 static void remove_sel_artist(struct artist
*artist
)
560 struct list_head
*aitem
, *ahead
;
562 ahead
= &artist
->album_head
;
564 while (aitem
!= ahead
) {
565 struct list_head
*titem
, *thead
;
566 struct list_head
*anext
= aitem
->next
;
567 struct album
*album
= to_album(aitem
);
569 thead
= &album
->track_head
;
571 while (titem
!= thead
) {
572 struct list_head
*tnext
= titem
->next
;
573 struct tree_track
*track
= to_tree_track(titem
);
575 editable_remove_track(&lib_editable
, (struct simple_track
*)track
);
578 /* all tracks removed => album removed
579 * if the last album was removed then the artist was removed too
585 static void remove_sel_album(struct album
*album
)
587 struct list_head
*item
, *head
;
589 head
= &album
->track_head
;
591 while (item
!= head
) {
592 struct list_head
*next
= item
->next
;
593 struct tree_track
*track
= to_tree_track(item
);
595 editable_remove_track(&lib_editable
, (struct simple_track
*)track
);
600 static void tree_win_remove_sel(void)
602 struct artist
*artist
;
605 tree_win_get_selected(&artist
, &album
);
607 remove_sel_album(album
);
609 remove_sel_artist(artist
);
613 static void track_win_remove_sel(void)
616 struct tree_track
*track
;
618 if (window_get_sel(lib_track_win
, &sel
)) {
619 track
= iter_to_tree_track(&sel
);
620 BUG_ON(track
== NULL
);
621 editable_remove_track(&lib_editable
, (struct simple_track
*)track
);
625 void tree_toggle_active_window(void)
627 if (lib_cur_win
== lib_tree_win
) {
628 struct artist
*artist
;
631 tree_win_get_selected(&artist
, &album
);
633 lib_cur_win
= lib_track_win
;
634 lib_tree_win
->changed
= 1;
635 lib_track_win
->changed
= 1;
637 } else if (lib_cur_win
== lib_track_win
) {
638 lib_cur_win
= lib_tree_win
;
639 lib_tree_win
->changed
= 1;
640 lib_track_win
->changed
= 1;
644 void tree_toggle_expand_artist(void)
647 struct artist
*artist
;
649 window_get_sel(lib_tree_win
, &sel
);
650 artist
= iter_to_artist(&sel
);
652 if (artist
->expanded
) {
653 /* deselect album, select artist */
654 artist_to_iter(artist
, &sel
);
655 window_set_sel(lib_tree_win
, &sel
);
657 artist
->expanded
= 0;
658 lib_cur_win
= lib_tree_win
;
660 artist
->expanded
= 1;
662 window_changed(lib_tree_win
);
666 static void remove_track(struct tree_track
*track
)
668 if (album_selected(track
->album
)) {
671 tree_track_to_iter(track
, &iter
);
672 window_row_vanishes(lib_track_win
, &iter
);
674 list_del(&track
->node
);
677 static void remove_album(struct album
*album
)
679 if (album
->artist
->expanded
) {
682 album_to_iter(album
, &iter
);
683 window_row_vanishes(lib_tree_win
, &iter
);
685 list_del(&album
->node
);
688 static void remove_artist(struct artist
*artist
)
692 artist_to_iter(artist
, &iter
);
693 window_row_vanishes(lib_tree_win
, &iter
);
694 list_del(&artist
->node
);
697 void tree_remove(struct tree_track
*track
)
699 struct album
*album
= track
->album
;
700 struct artist
*sel_artist
;
701 struct album
*sel_album
;
703 tree_win_get_selected(&sel_artist
, &sel_album
);
706 /* don't free the track */
708 if (list_empty(&album
->track_head
)) {
709 struct artist
*artist
= album
->artist
;
711 if (sel_album
== album
)
712 lib_cur_win
= lib_tree_win
;
717 if (list_empty(&artist
->album_head
)) {
718 artist
->expanded
= 0;
719 remove_artist(artist
);
725 void tree_remove_sel(void)
727 if (lib_cur_win
== lib_tree_win
) {
728 tree_win_remove_sel();
730 track_win_remove_sel();
734 void tree_sel_current(void)
739 CUR_ARTIST
->expanded
= 1;
741 if (lib_cur_win
== lib_tree_win
) {
742 lib_cur_win
= lib_track_win
;
743 lib_tree_win
->changed
= 1;
744 lib_track_win
->changed
= 1;
747 album_to_iter(CUR_ALBUM
, &iter
);
748 window_set_sel(lib_tree_win
, &iter
);
750 tree_track_to_iter(lib_cur_track
, &iter
);
751 window_set_sel(lib_track_win
, &iter
);
755 static int album_for_each_track(struct album
*album
, int (*cb
)(void *data
, struct track_info
*ti
),
756 void *data
, int reverse
)
758 struct tree_track
*track
;
762 list_for_each_entry_reverse(track
, &album
->track_head
, node
) {
763 rc
= cb(data
, tree_track_info(track
));
768 list_for_each_entry(track
, &album
->track_head
, node
) {
769 rc
= cb(data
, tree_track_info(track
));
777 static int artist_for_each_track(struct artist
*artist
, int (*cb
)(void *data
, struct track_info
*ti
),
778 void *data
, int reverse
)
784 list_for_each_entry_reverse(album
, &artist
->album_head
, node
) {
785 rc
= album_for_each_track(album
, cb
, data
, 1);
790 list_for_each_entry(album
, &artist
->album_head
, node
) {
791 rc
= album_for_each_track(album
, cb
, data
, 0);
799 int __tree_for_each_sel(int (*cb
)(void *data
, struct track_info
*ti
), void *data
, int reverse
)
803 if (lib_cur_win
== lib_tree_win
) {
804 struct artist
*artist
;
807 tree_win_get_selected(&artist
, &album
);
810 rc
= artist_for_each_track(artist
, cb
, data
, reverse
);
812 rc
= album_for_each_track(album
, cb
, data
, reverse
);
817 struct tree_track
*track
;
819 if (window_get_sel(lib_track_win
, &sel
)) {
820 track
= iter_to_tree_track(&sel
);
821 rc
= cb(data
, tree_track_info(track
));
827 int tree_for_each_sel(int (*cb
)(void *data
, struct track_info
*ti
), void *data
, int reverse
)
829 int rc
= __tree_for_each_sel(cb
, data
, reverse
);
831 window_down(lib_cur_win
, 1);