2 * Copyright 2004-2006 Timo Hirvonen
7 #include "track_info.h"
15 struct editable lib_editable
;
16 struct tree_track
*lib_cur_track
= NULL
;
17 unsigned int play_sorted
= 0;
18 enum aaa_mode aaa_mode
= AAA_MODE_ALL
;
20 static LIST_HEAD(lib_shuffle_head
);
21 static struct expr
*filter
= NULL
;
22 static int remove_from_hash
= 1;
24 static inline struct tree_track
*to_sorted(const struct list_head
*item
)
26 return (struct tree_track
*)container_of(item
, struct simple_track
, node
);
29 static inline void sorted_track_to_iter(struct tree_track
*track
, struct iter
*iter
)
31 iter
->data0
= &lib_editable
.head
;
36 static void all_wins_changed(void)
38 lib_tree_win
->changed
= 1;
39 lib_track_win
->changed
= 1;
40 lib_editable
.win
->changed
= 1;
43 static void shuffle_add(struct tree_track
*track
)
45 list_add_rand(&lib_shuffle_head
, &track
->shuffle_track
.node
, lib_editable
.nr_tracks
);
48 static void views_add_track(struct track_info
*ti
)
50 struct tree_track
*track
= xnew(struct tree_track
, 1);
52 /* NOTE: does not ref ti */
53 simple_track_init((struct simple_track
*)track
, ti
);
55 /* both the hash table and views have refs */
58 tree_add_track(track
);
60 editable_add(&lib_editable
, (struct simple_track
*)track
);
64 struct fh_entry
*next
;
66 /* ref count is increased when added to this hash */
67 struct track_info
*ti
;
70 #define FH_SIZE (1024)
71 static struct fh_entry
*ti_hash
[FH_SIZE
] = { NULL
, };
73 /* this is from glib */
74 static unsigned int str_hash(const char *str
)
76 unsigned int hash
= 0;
79 for (i
= 0; str
[i
]; i
++)
80 hash
= (hash
<< 5) - hash
+ str
[i
];
84 static int hash_insert(struct track_info
*ti
)
86 const char *filename
= ti
->filename
;
87 unsigned int pos
= str_hash(filename
) % FH_SIZE
;
88 struct fh_entry
**entryp
;
91 entryp
= &ti_hash
[pos
];
94 if (strcmp(e
->ti
->filename
, filename
) == 0) {
95 /* found, don't insert */
101 e
= xnew(struct fh_entry
, 1);
109 static void hash_remove(struct track_info
*ti
)
111 const char *filename
= ti
->filename
;
112 unsigned int pos
= str_hash(filename
) % FH_SIZE
;
113 struct fh_entry
**entryp
;
115 entryp
= &ti_hash
[pos
];
117 struct fh_entry
*e
= *entryp
;
120 if (strcmp(e
->ti
->filename
, filename
) == 0) {
122 track_info_unref(e
->ti
);
130 void lib_add_track(struct track_info
*ti
)
132 if (!hash_insert(ti
)) {
133 /* duplicate files not allowed */
136 if (filter
== NULL
|| expr_eval(filter
, ti
))
140 static struct tree_track
*album_first_track(const struct album
*album
)
142 return to_tree_track(album
->track_head
.next
);
145 static struct tree_track
*artist_first_track(const struct artist
*artist
)
147 return album_first_track(to_album(artist
->album_head
.next
));
150 static struct tree_track
*normal_get_first(void)
152 return artist_first_track(to_artist(lib_artist_head
.next
));
155 static struct tree_track
*album_last_track(const struct album
*album
)
157 return to_tree_track(album
->track_head
.prev
);
160 static struct tree_track
*artist_last_track(const struct artist
*artist
)
162 return album_last_track(to_album(artist
->album_head
.prev
));
165 static int aaa_mode_filter(const struct simple_track
*track
)
167 const struct album
*album
= ((struct tree_track
*)track
)->album
;
169 if (aaa_mode
== AAA_MODE_ALBUM
)
170 return CUR_ALBUM
== album
;
172 if (aaa_mode
== AAA_MODE_ARTIST
)
173 return CUR_ARTIST
== album
->artist
;
179 /* set next/prev (tree) {{{ */
181 static struct tree_track
*normal_get_next(void)
183 if (lib_cur_track
== NULL
)
184 return normal_get_first();
186 /* not last track of the album? */
187 if (lib_cur_track
->node
.next
!= &CUR_ALBUM
->track_head
) {
188 /* next track of the current album */
189 return to_tree_track(lib_cur_track
->node
.next
);
192 if (aaa_mode
== AAA_MODE_ALBUM
) {
195 /* first track of the current album */
196 return album_first_track(CUR_ALBUM
);
199 /* not last album of the artist? */
200 if (CUR_ALBUM
->node
.next
!= &CUR_ARTIST
->album_head
) {
201 /* first track of the next album */
202 return album_first_track(to_album(CUR_ALBUM
->node
.next
));
205 if (aaa_mode
== AAA_MODE_ARTIST
) {
208 /* first track of the first album of the current artist */
209 return artist_first_track(CUR_ARTIST
);
212 /* not last artist of the library? */
213 if (CUR_ARTIST
->node
.next
!= &lib_artist_head
) {
214 /* first track of the next artist */
215 return artist_first_track(to_artist(CUR_ARTIST
->node
.next
));
222 return normal_get_first();
225 static struct tree_track
*normal_get_prev(void)
227 if (lib_cur_track
== NULL
)
228 return normal_get_first();
230 /* not first track of the album? */
231 if (lib_cur_track
->node
.prev
!= &CUR_ALBUM
->track_head
) {
232 /* prev track of the album */
233 return to_tree_track(lib_cur_track
->node
.prev
);
236 if (aaa_mode
== AAA_MODE_ALBUM
) {
239 /* last track of the album */
240 return to_tree_track(CUR_ALBUM
->track_head
.prev
);
243 /* not first album of the artist? */
244 if (CUR_ALBUM
->node
.prev
!= &CUR_ARTIST
->album_head
) {
245 /* last track of the prev album of the artist */
246 return album_last_track(to_album(CUR_ALBUM
->node
.prev
));
249 if (aaa_mode
== AAA_MODE_ARTIST
) {
252 /* last track of the last album of the artist */
253 return album_last_track(to_album(CUR_ARTIST
->album_head
.prev
));
256 /* not first artist of the library? */
257 if (CUR_ARTIST
->node
.prev
!= &lib_artist_head
) {
258 /* last track of the last album of the prev artist */
259 return artist_last_track(to_artist(CUR_ARTIST
->node
.prev
));
266 return artist_last_track(to_artist(lib_artist_head
.prev
));
269 /* set next/prev (tree) }}} */
271 void lib_reshuffle(void)
273 reshuffle(&lib_shuffle_head
);
276 static void free_lib_track(struct list_head
*item
)
278 struct tree_track
*track
= (struct tree_track
*)to_simple_track(item
);
279 struct track_info
*ti
= tree_track_info(track
);
281 if (track
== lib_cur_track
)
282 lib_cur_track
= NULL
;
284 if (remove_from_hash
)
287 list_del(&track
->shuffle_track
.node
);
290 track_info_unref(ti
);
296 editable_init(&lib_editable
, free_lib_track
);
301 static struct track_info
*lib_set_track(struct tree_track
*track
)
303 struct track_info
*ti
= NULL
;
306 lib_cur_track
= track
;
307 ti
= tree_track_info(track
);
314 struct track_info
*lib_set_next(void)
316 struct tree_track
*track
;
318 if (list_empty(&lib_artist_head
)) {
319 BUG_ON(lib_cur_track
!= NULL
);
323 track
= (struct tree_track
*)shuffle_list_get_next(&lib_shuffle_head
,
324 (struct shuffle_track
*)lib_cur_track
, aaa_mode_filter
);
325 } else if (play_sorted
) {
326 track
= (struct tree_track
*)simple_list_get_next(&lib_editable
.head
,
327 (struct simple_track
*)lib_cur_track
, aaa_mode_filter
);
329 track
= normal_get_next();
331 return lib_set_track(track
);
334 struct track_info
*lib_set_prev(void)
336 struct tree_track
*track
;
338 if (list_empty(&lib_artist_head
)) {
339 BUG_ON(lib_cur_track
!= NULL
);
343 track
= (struct tree_track
*)shuffle_list_get_prev(&lib_shuffle_head
,
344 (struct shuffle_track
*)lib_cur_track
, aaa_mode_filter
);
345 } else if (play_sorted
) {
346 track
= (struct tree_track
*)simple_list_get_prev(&lib_editable
.head
,
347 (struct simple_track
*)lib_cur_track
, aaa_mode_filter
);
349 track
= normal_get_prev();
351 return lib_set_track(track
);
354 struct track_info
*sorted_set_selected(void)
358 if (list_empty(&lib_editable
.head
))
361 window_get_sel(lib_editable
.win
, &sel
);
362 return lib_set_track(iter_to_sorted_track(&sel
));
365 void lib_set_filter(struct expr
*expr
)
367 static const char *tmp_keys
[1] = { NULL
};
368 struct track_info
*cur_ti
= NULL
;
369 const char **sort_keys
;
372 /* try to save cur_track */
374 cur_ti
= tree_track_info(lib_cur_track
);
375 track_info_ref(cur_ti
);
378 remove_from_hash
= 0;
379 editable_clear(&lib_editable
);
380 remove_from_hash
= 1;
386 /* disable sorting temporarily */
387 sort_keys
= lib_editable
.sort_keys
;
388 lib_editable
.sort_keys
= tmp_keys
;
390 for (i
= 0; i
< FH_SIZE
; i
++) {
395 struct track_info
*ti
= e
->ti
;
397 if (filter
== NULL
|| expr_eval(filter
, ti
))
404 lib_editable
.sort_keys
= sort_keys
;
405 editable_sort(&lib_editable
);
407 lib_cur_win
= lib_tree_win
;
408 window_goto_top(lib_tree_win
);
410 /* restore cur_track */
412 struct simple_track
*track
;
414 list_for_each_entry(track
, &lib_editable
.head
, node
) {
415 if (strcmp(track
->info
->filename
, cur_ti
->filename
) == 0) {
416 struct tree_track
*tt
= (struct tree_track
*)track
;
422 track_info_unref(cur_ti
);
426 int lib_remove(struct track_info
*ti
)
428 struct simple_track
*track
;
430 list_for_each_entry(track
, &lib_editable
.head
, node
) {
431 if (track
->info
== ti
) {
432 editable_remove_track(&lib_editable
, track
);
439 void lib_clear_store(void)
443 for (i
= 0; i
< FH_SIZE
; i
++) {
444 struct fh_entry
*e
, *next
;
449 track_info_unref(e
->ti
);
457 void sorted_sel_current(void)
462 sorted_track_to_iter(lib_cur_track
, &iter
);
463 window_set_sel(lib_editable
.win
, &iter
);
467 static int ti_cmp(const void *a
, const void *b
)
469 const struct track_info
*ai
= *(const struct track_info
**)a
;
470 const struct track_info
*bi
= *(const struct track_info
**)b
;
472 return track_info_cmp(ai
, bi
, lib_editable
.sort_keys
);
475 int lib_for_each(int (*cb
)(void *data
, struct track_info
*ti
), void *data
)
477 int i
, rc
= 0, count
= 0, size
= 1024;
478 struct track_info
**tis
;
480 tis
= xnew(struct track_info
*, size
);
482 /* collect all track_infos */
483 for (i
= 0; i
< FH_SIZE
; i
++) {
490 tis
= xrenew(struct track_info
*, tis
, size
);
492 tis
[count
++] = e
->ti
;
497 /* sort to speed up playlist loading */
498 qsort(tis
, count
, sizeof(struct track_info
*), ti_cmp
);
499 for (i
= 0; i
< count
; i
++) {
500 rc
= cb(data
, tis
[i
]);