2 * Copyright 2004-2006 Timo Hirvonen
14 struct editable lib_editable
;
15 LIST_HEAD(lib_shuffle_head
);
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 struct expr
*filter
= NULL
;
21 static int remove_from_hash
= 1;
23 static inline struct tree_track
*to_sorted(const struct list_head
*item
)
25 return (struct tree_track
*)container_of(item
, struct simple_track
, node
);
28 static inline void sorted_track_to_iter(struct tree_track
*track
, struct iter
*iter
)
30 iter
->data0
= &lib_editable
.head
;
35 static void all_wins_changed(void)
37 lib_tree_win
->changed
= 1;
38 lib_track_win
->changed
= 1;
39 lib_editable
.win
->changed
= 1;
42 static void shuffle_add(struct tree_track
*track
)
44 shuffle_list_add_track(&lib_shuffle_head
, &track
->shuffle_track
.node
, lib_editable
.nr_tracks
);
47 static void views_add_track(struct track_info
*ti
)
49 struct tree_track
*track
= xnew(struct tree_track
, 1);
51 /* NOTE: does not ref ti */
52 simple_track_init((struct simple_track
*)track
, ti
);
54 /* both the hash table and views have refs */
57 tree_add_track(track
);
59 editable_add(&lib_editable
, (struct simple_track
*)track
);
63 struct fh_entry
*next
;
65 /* ref count is increased when added to this hash */
66 struct track_info
*ti
;
69 #define FH_SIZE (1024)
70 static struct fh_entry
*ti_hash
[FH_SIZE
] = { NULL
, };
72 /* this is from glib */
73 static unsigned int str_hash(const char *str
)
75 unsigned int hash
= 0;
78 for (i
= 0; str
[i
]; i
++)
79 hash
= (hash
<< 5) - hash
+ str
[i
];
83 static int hash_insert(struct track_info
*ti
)
85 const char *filename
= ti
->filename
;
86 unsigned int pos
= str_hash(filename
) % FH_SIZE
;
87 struct fh_entry
**entryp
;
90 entryp
= &ti_hash
[pos
];
93 if (strcmp(e
->ti
->filename
, filename
) == 0) {
94 /* found, don't insert */
100 e
= xnew(struct fh_entry
, 1);
108 static void hash_remove(struct track_info
*ti
)
110 const char *filename
= ti
->filename
;
111 unsigned int pos
= str_hash(filename
) % FH_SIZE
;
112 struct fh_entry
**entryp
;
114 entryp
= &ti_hash
[pos
];
116 struct fh_entry
*e
= *entryp
;
119 if (strcmp(e
->ti
->filename
, filename
) == 0) {
121 track_info_unref(e
->ti
);
129 void lib_add_track(struct track_info
*ti
)
131 if (!hash_insert(ti
)) {
132 /* duplicate files not allowed */
135 if (filter
== NULL
|| expr_eval(filter
, ti
))
139 static struct tree_track
*album_first_track(const struct album
*album
)
141 return to_tree_track(album
->track_head
.next
);
144 static struct tree_track
*artist_first_track(const struct artist
*artist
)
146 return album_first_track(to_album(artist
->album_head
.next
));
149 static struct tree_track
*normal_get_first(void)
151 return artist_first_track(to_artist(lib_artist_head
.next
));
154 static struct tree_track
*album_last_track(const struct album
*album
)
156 return to_tree_track(album
->track_head
.prev
);
159 static struct tree_track
*artist_last_track(const struct artist
*artist
)
161 return album_last_track(to_album(artist
->album_head
.prev
));
164 static int aaa_mode_filter(const struct simple_track
*track
)
166 const struct album
*album
= ((struct tree_track
*)track
)->album
;
168 if (aaa_mode
== AAA_MODE_ALBUM
)
169 return CUR_ALBUM
== album
;
171 if (aaa_mode
== AAA_MODE_ARTIST
)
172 return CUR_ARTIST
== album
->artist
;
178 /* set next/prev (tree) {{{ */
180 static struct tree_track
*normal_get_next(void)
182 if (lib_cur_track
== NULL
)
183 return normal_get_first();
185 /* not last track of the album? */
186 if (lib_cur_track
->node
.next
!= &CUR_ALBUM
->track_head
) {
187 /* next track of the current album */
188 return to_tree_track(lib_cur_track
->node
.next
);
191 if (aaa_mode
== AAA_MODE_ALBUM
) {
194 /* first track of the current album */
195 return album_first_track(CUR_ALBUM
);
198 /* not last album of the artist? */
199 if (CUR_ALBUM
->node
.next
!= &CUR_ARTIST
->album_head
) {
200 /* first track of the next album */
201 return album_first_track(to_album(CUR_ALBUM
->node
.next
));
204 if (aaa_mode
== AAA_MODE_ARTIST
) {
207 /* first track of the first album of the current artist */
208 return artist_first_track(CUR_ARTIST
);
211 /* not last artist of the library? */
212 if (CUR_ARTIST
->node
.next
!= &lib_artist_head
) {
213 /* first track of the next artist */
214 return artist_first_track(to_artist(CUR_ARTIST
->node
.next
));
221 return normal_get_first();
224 static struct tree_track
*normal_get_prev(void)
226 if (lib_cur_track
== NULL
)
227 return normal_get_first();
229 /* not first track of the album? */
230 if (lib_cur_track
->node
.prev
!= &CUR_ALBUM
->track_head
) {
231 /* prev track of the album */
232 return to_tree_track(lib_cur_track
->node
.prev
);
235 if (aaa_mode
== AAA_MODE_ALBUM
) {
238 /* last track of the album */
239 return to_tree_track(CUR_ALBUM
->track_head
.prev
);
242 /* not first album of the artist? */
243 if (CUR_ALBUM
->node
.prev
!= &CUR_ARTIST
->album_head
) {
244 /* last track of the prev album of the artist */
245 return album_last_track(to_album(CUR_ALBUM
->node
.prev
));
248 if (aaa_mode
== AAA_MODE_ARTIST
) {
251 /* last track of the last album of the artist */
252 return album_last_track(to_album(CUR_ARTIST
->album_head
.prev
));
255 /* not first artist of the library? */
256 if (CUR_ARTIST
->node
.prev
!= &lib_artist_head
) {
257 /* last track of the last album of the prev artist */
258 return artist_last_track(to_artist(CUR_ARTIST
->node
.prev
));
265 return artist_last_track(to_artist(lib_artist_head
.prev
));
268 /* set next/prev (tree) }}} */
270 void lib_reshuffle(void)
272 reshuffle(&lib_shuffle_head
);
275 static void free_lib_track(struct list_head
*item
)
277 struct tree_track
*track
= (struct tree_track
*)to_simple_track(item
);
278 struct track_info
*ti
= tree_track_info(track
);
280 if (track
== lib_cur_track
)
281 lib_cur_track
= NULL
;
283 if (remove_from_hash
)
286 list_del(&track
->shuffle_track
.node
);
289 track_info_unref(ti
);
295 editable_init(&lib_editable
, free_lib_track
);
300 struct track_info
*lib_set_next(void)
302 struct tree_track
*track
;
303 struct track_info
*ti
= NULL
;
305 if (list_empty(&lib_artist_head
)) {
306 BUG_ON(lib_cur_track
!= NULL
);
310 track
= (struct tree_track
*)shuffle_list_get_next(&lib_shuffle_head
,
311 (struct shuffle_track
*)lib_cur_track
, aaa_mode_filter
);
312 } else if (play_sorted
) {
313 track
= (struct tree_track
*)simple_list_get_next(&lib_editable
.head
,
314 (struct simple_track
*)lib_cur_track
, aaa_mode_filter
);
316 track
= normal_get_next();
319 lib_cur_track
= track
;
320 ti
= tree_track_info(track
);
328 struct track_info
*lib_set_prev(void)
330 struct tree_track
*track
;
331 struct track_info
*ti
= NULL
;
333 if (list_empty(&lib_artist_head
)) {
334 BUG_ON(lib_cur_track
!= NULL
);
338 track
= (struct tree_track
*)shuffle_list_get_prev(&lib_shuffle_head
,
339 (struct shuffle_track
*)lib_cur_track
, aaa_mode_filter
);
340 } else if (play_sorted
) {
341 track
= (struct tree_track
*)simple_list_get_prev(&lib_editable
.head
,
342 (struct simple_track
*)lib_cur_track
, aaa_mode_filter
);
344 track
= normal_get_prev();
347 lib_cur_track
= track
;
348 ti
= tree_track_info(track
);
356 struct track_info
*sorted_set_selected(void)
358 struct track_info
*info
;
361 if (list_empty(&lib_editable
.head
))
364 window_get_sel(lib_editable
.win
, &sel
);
365 lib_cur_track
= iter_to_sorted_track(&sel
);
366 info
= tree_track_info(lib_cur_track
);
367 track_info_ref(info
);
372 void lib_set_filter(struct expr
*expr
)
374 static const char *tmp_keys
[1] = { NULL
};
375 struct track_info
*cur_ti
= NULL
;
376 const char **sort_keys
;
379 /* try to save cur_track */
381 cur_ti
= tree_track_info(lib_cur_track
);
382 track_info_ref(cur_ti
);
385 remove_from_hash
= 0;
386 editable_clear(&lib_editable
);
387 remove_from_hash
= 1;
393 /* disable sorting temporarily */
394 sort_keys
= lib_editable
.sort_keys
;
395 lib_editable
.sort_keys
= tmp_keys
;
397 for (i
= 0; i
< FH_SIZE
; i
++) {
402 struct track_info
*ti
= e
->ti
;
404 if (filter
== NULL
|| expr_eval(filter
, ti
))
411 lib_editable
.sort_keys
= sort_keys
;
412 editable_sort(&lib_editable
);
414 lib_cur_win
= lib_tree_win
;
415 window_goto_top(lib_tree_win
);
417 /* restore cur_track */
419 struct simple_track
*track
;
421 list_for_each_entry(track
, &lib_editable
.head
, node
) {
422 if (strcmp(track
->info
->filename
, cur_ti
->filename
) == 0) {
423 struct tree_track
*tt
= (struct tree_track
*)track
;
429 track_info_unref(cur_ti
);
433 void lib_remove(struct track_info
*ti
)
435 struct simple_track
*track
;
437 list_for_each_entry(track
, &lib_editable
.head
, node
) {
438 if (track
->info
== ti
) {
439 editable_remove_track(&lib_editable
, track
);
445 void lib_clear_store(void)
449 for (i
= 0; i
< FH_SIZE
; i
++) {
450 struct fh_entry
*e
, *next
;
455 track_info_unref(e
->ti
);
463 void sorted_sel_current(void)
468 sorted_track_to_iter(lib_cur_track
, &iter
);
469 window_set_sel(lib_editable
.win
, &iter
);
473 static int ti_filename_cmp(const void *a
, const void *b
)
475 const struct track_info
*tia
= *(const struct track_info
**)a
;
476 const struct track_info
*tib
= *(const struct track_info
**)b
;
478 return strcmp(tia
->filename
, tib
->filename
);
481 int lib_for_each(int (*cb
)(void *data
, struct track_info
*ti
), void *data
)
483 int i
, rc
= 0, count
= 0, size
= 1024;
484 struct track_info
**tis
;
486 tis
= xnew(struct track_info
*, size
);
488 /* collect all track_infos */
489 for (i
= 0; i
< FH_SIZE
; i
++) {
496 tis
= xrenew(struct track_info
*, tis
, size
);
498 tis
[count
++] = e
->ti
;
503 /* sort them by filename and call cb for each */
504 qsort(tis
, count
, sizeof(struct track_info
*), ti_filename_cmp
);
505 for (i
= 0; i
< count
; i
++) {
506 rc
= cb(data
, tis
[i
]);