add :refresh command
[cmus.git] / track.c
blob87773015507d23c3fb7025018e671c7bc5491ba3
1 /*
2 * Copyright 2006 Timo Hirvonen
3 */
5 #include "track.h"
6 #include "iter.h"
7 #include "track_info.h"
8 #include "search_mode.h"
9 #include "window.h"
10 #include "options.h"
11 #include "comment.h"
12 #include "uchar.h"
13 #include "xmalloc.h"
15 #include <string.h>
17 int xstrcasecmp(const char *a, const char *b)
19 if (a == NULL) {
20 if (b == NULL)
21 return 0;
22 return -1;
23 } else if (b == NULL) {
24 return 1;
26 return u_strcasecmp(a, b);
29 void simple_track_init(struct simple_track *track, struct track_info *ti)
31 track->info = ti;
32 track->marked = 0;
33 track->disc = comments_get_int(ti->comments, "discnumber");
34 track->num = comments_get_int(ti->comments, "tracknumber");
37 struct simple_track *simple_track_new(struct track_info *ti)
39 struct simple_track *t = xnew(struct simple_track, 1);
41 track_info_ref(ti);
42 simple_track_init(t, ti);
43 return t;
46 GENERIC_ITER_PREV(simple_track_get_prev, struct simple_track, node)
47 GENERIC_ITER_NEXT(simple_track_get_next, struct simple_track, node)
49 int simple_track_search_get_current(void *data, struct iter *iter)
51 return window_get_sel(data, iter);
54 int simple_track_search_matches(void *data, struct iter *iter, const char *text)
56 unsigned int flags = TI_MATCH_TITLE;
57 struct simple_track *track = iter_to_simple_track(iter);
59 if (!search_restricted)
60 flags |= TI_MATCH_ARTIST | TI_MATCH_ALBUM;
62 if (!track_info_matches(track->info, text, flags))
63 return 0;
65 window_set_sel(data, iter);
66 return 1;
69 struct shuffle_track *shuffle_list_get_next(struct list_head *head, struct shuffle_track *cur,
70 int (*filter)(const struct simple_track *))
72 struct list_head *item;
74 if (cur == NULL)
75 return to_shuffle_track(head->next);
77 item = cur->node.next;
78 again:
79 while (item != head) {
80 struct shuffle_track *track = to_shuffle_track(item);
82 if (filter((struct simple_track *)track))
83 return track;
84 item = item->next;
86 if (repeat) {
87 if (auto_reshuffle)
88 reshuffle(head);
89 item = head->next;
90 goto again;
92 return NULL;
95 struct shuffle_track *shuffle_list_get_prev(struct list_head *head, struct shuffle_track *cur,
96 int (*filter)(const struct simple_track *))
98 struct list_head *item;
100 if (cur == NULL)
101 return to_shuffle_track(head->next);
103 item = cur->node.prev;
104 again:
105 while (item != head) {
106 struct shuffle_track *track = to_shuffle_track(item);
108 if (filter((struct simple_track *)track))
109 return track;
110 item = item->prev;
112 if (repeat) {
113 if (auto_reshuffle)
114 reshuffle(head);
115 item = head->prev;
116 goto again;
118 return NULL;
121 struct simple_track *simple_list_get_next(struct list_head *head, struct simple_track *cur,
122 int (*filter)(const struct simple_track *))
124 struct list_head *item;
126 if (cur == NULL)
127 return to_simple_track(head->next);
129 item = cur->node.next;
130 again:
131 while (item != head) {
132 struct simple_track *track = to_simple_track(item);
134 if (filter(track))
135 return track;
136 item = item->next;
138 item = head->next;
139 if (repeat)
140 goto again;
141 return NULL;
144 struct simple_track *simple_list_get_prev(struct list_head *head, struct simple_track *cur,
145 int (*filter)(const struct simple_track *))
147 struct list_head *item;
149 if (cur == NULL)
150 return to_simple_track(head->next);
152 item = cur->node.prev;
153 again:
154 while (item != head) {
155 struct simple_track *track = to_simple_track(item);
157 if (filter(track))
158 return track;
159 item = item->prev;
161 item = head->prev;
162 if (repeat)
163 goto again;
164 return NULL;
167 int simple_track_cmp(const struct list_head *a_head, const struct list_head *b_head, const char * const *keys)
169 const struct simple_track *a = to_simple_track(a_head);
170 const struct simple_track *b = to_simple_track(b_head);
171 int i, res = 0;
173 for (i = 0; keys[i]; i++) {
174 const char *key = keys[i];
175 const char *av, *bv;
176 const struct track_info *ai, *bi;
178 /* numeric compare for tracknumber and discnumber */
179 if (strcmp(key, "tracknumber") == 0) {
180 res = a->num - b->num;
181 if (res)
182 break;
184 if (strcmp(key, "discnumber") == 0) {
185 res = a->disc - b->disc;
186 if (res)
187 break;
189 ai = a->info;
190 bi = b->info;
191 if (strcmp(key, "filename") == 0) {
192 /* NOTE: filenames are not necessarily UTF-8 */
193 res = strcasecmp(ai->filename, bi->filename);
194 if (res)
195 break;
197 av = comments_get_val(ai->comments, key);
198 bv = comments_get_val(bi->comments, key);
199 res = xstrcasecmp(av, bv);
200 if (res)
201 break;
203 return res;
206 void sorted_list_add_track(struct list_head *head, struct simple_track *track, const char * const *keys)
208 struct list_head *item;
210 /* It is _much_ faster to iterate in reverse order because playlist
211 * file is usually sorted.
213 item = head->prev;
214 while (item != head) {
215 if (simple_track_cmp(&track->node, item, keys) >= 0)
216 break;
217 item = item->prev;
219 /* add after item */
220 list_add(&track->node, item);
223 void shuffle_list_add_track(struct list_head *head, struct list_head *node, int nr)
225 struct list_head *item;
226 int pos;
228 pos = rand() % (nr + 1);
229 item = head;
230 if (pos <= nr / 2) {
231 while (pos) {
232 item = item->next;
233 pos--;
235 /* add after item */
236 list_add(node, item);
237 } else {
238 pos = nr - pos;
239 while (pos) {
240 item = item->prev;
241 pos--;
243 /* add before item */
244 list_add_tail(node, item);
248 void reshuffle(struct list_head *head)
250 struct list_head *item, *last;
251 int i = 0;
253 if (list_empty(head))
254 return;
256 last = head->prev;
257 item = head->next;
258 list_init(head);
260 while (1) {
261 struct list_head *next = item->next;
263 shuffle_list_add_track(head, item, i++);
264 if (item == last)
265 break;
266 item = next;
270 int simple_list_for_each_marked(struct list_head *head,
271 int (*cb)(void *data, struct track_info *ti), void *data, int reverse)
273 struct simple_track *t;
274 int rc = 0;
276 if (reverse) {
277 list_for_each_entry_reverse(t, head, node) {
278 if (t->marked) {
279 rc = cb(data, t->info);
280 if (rc)
281 break;
284 } else {
285 list_for_each_entry(t, head, node) {
286 if (t->marked) {
287 rc = cb(data, t->info);
288 if (rc)
289 break;
293 return rc;