Handle streams separately in tree_add_track()
[cmus.git] / editable.c
blob2259978affe9cd5510745331dbb38728e9535366
1 /*
2 * Copyright 2006 Timo Hirvonen
3 */
5 #include "editable.h"
6 #include "search.h"
7 #include "track.h"
8 #include "track_info.h"
9 #include "expr.h"
10 #include "filters.h"
11 #include "locking.h"
12 #include "mergesort.h"
13 #include "xmalloc.h"
15 pthread_mutex_t editable_mutex = CMUS_MUTEX_INITIALIZER;
17 static const struct searchable_ops simple_search_ops = {
18 .get_prev = simple_track_get_prev,
19 .get_next = simple_track_get_next,
20 .get_current = simple_track_search_get_current,
21 .matches = simple_track_search_matches
24 static struct simple_track *get_selected(struct editable *e)
26 struct iter sel;
28 if (window_get_sel(e->win, &sel))
29 return iter_to_simple_track(&sel);
30 return NULL;
33 void editable_init(struct editable *e, void (*free_track)(struct list_head *item))
35 struct iter iter;
37 list_init(&e->head);
38 e->nr_tracks = 0;
39 e->nr_marked = 0;
40 e->total_time = 0;
41 e->sort_keys = xnew(const char *, 1);
42 e->sort_keys[0] = NULL;
43 e->sort_str[0] = 0;
44 e->free_track = free_track;
46 e->win = window_new(simple_track_get_prev, simple_track_get_next);
47 window_set_contents(e->win, &e->head);
49 iter.data0 = &e->head;
50 iter.data1 = NULL;
51 iter.data2 = NULL;
52 e->searchable = searchable_new(e->win, &iter, &simple_search_ops);
55 void editable_add(struct editable *e, struct simple_track *track)
57 sorted_list_add_track(&e->head, track, e->sort_keys);
58 e->nr_tracks++;
59 if (track->info->duration != -1)
60 e->total_time += track->info->duration;
61 window_changed(e->win);
64 void editable_remove_track(struct editable *e, struct simple_track *track)
66 struct track_info *ti = track->info;
67 struct iter iter;
69 editable_track_to_iter(e, track, &iter);
70 window_row_vanishes(e->win, &iter);
72 e->nr_tracks--;
73 e->nr_marked -= track->marked;
74 if (ti->duration != -1)
75 e->total_time -= ti->duration;
77 list_del(&track->node);
79 e->free_track(&track->node);
82 void editable_remove_sel(struct editable *e)
84 struct simple_track *t;
86 if (e->nr_marked) {
87 /* treat marked tracks as selected */
88 struct list_head *next, *item = e->head.next;
90 while (item != &e->head) {
91 next = item->next;
92 t = to_simple_track(item);
93 if (t->marked)
94 editable_remove_track(e, t);
95 item = next;
97 } else {
98 t = get_selected(e);
99 if (t)
100 editable_remove_track(e, t);
104 static const char **sort_keys;
106 static int list_cmp(const struct list_head *a_head, const struct list_head *b_head)
108 const struct simple_track *a = to_simple_track(a_head);
109 const struct simple_track *b = to_simple_track(b_head);
111 return track_info_cmp(a->info, b->info, sort_keys);
114 void editable_sort(struct editable *e)
116 sort_keys = e->sort_keys;
117 list_mergesort(&e->head, list_cmp);
118 window_changed(e->win);
119 window_goto_top(e->win);
122 static void keys_to_str(const char **keys, char *buf)
124 int i, pos = 0;
126 for (i = 0; keys[i]; i++) {
127 const char *key = keys[i];
128 int len = strlen(key);
130 if (sizeof(buf) - pos - len - 2 < 0)
131 break;
133 memcpy(buf + pos, key, len);
134 pos += len;
135 buf[pos++] = ' ';
137 if (pos > 0)
138 pos--;
139 buf[pos] = 0;
142 void editable_set_sort_keys(struct editable *e, const char **keys)
144 free(e->sort_keys);
145 e->sort_keys = keys;
146 editable_sort(e);
147 keys_to_str(keys, e->sort_str);
150 void editable_toggle_mark(struct editable *e)
152 struct simple_track *t;
154 t = get_selected(e);
155 if (t) {
156 e->nr_marked -= t->marked;
157 t->marked ^= 1;
158 e->nr_marked += t->marked;
159 e->win->changed = 1;
160 window_down(e->win, 1);
164 static void move_item(struct editable *e, struct list_head *head, struct list_head *item)
166 struct simple_track *t = to_simple_track(item);
167 struct iter iter;
169 editable_track_to_iter(e, t, &iter);
170 window_row_vanishes(e->win, &iter);
172 list_del(item);
173 list_add(item, head);
176 static void move_sel(struct editable *e, struct list_head *after)
178 struct simple_track *t;
179 struct list_head *item, *next;
180 struct iter iter;
181 LIST_HEAD(tmp_head);
183 if (e->nr_marked) {
184 /* collect marked */
185 item = e->head.next;
186 while (item != &e->head) {
187 t = to_simple_track(item);
188 next = item->next;
189 if (t->marked)
190 move_item(e, &tmp_head, item);
191 item = next;
193 } else {
194 /* collect the selected track */
195 t = get_selected(e);
196 move_item(e, &tmp_head, &t->node);
199 /* put them back to the list after @after */
200 item = tmp_head.next;
201 while (item != &tmp_head) {
202 next = item->next;
203 list_add(item, after);
204 item = next;
207 /* select top-most of the moved tracks */
208 editable_track_to_iter(e, to_simple_track(after->next), &iter);
209 window_set_sel(e->win, &iter);
210 window_changed(e->win);
213 static struct list_head *find_insert_after_point(struct editable *e, struct list_head *item)
215 if (e->nr_marked == 0) {
216 /* move the selected track down one row */
217 return item->next;
220 /* move marked after the selected
222 * if the selected track itself is marked we find the first unmarked
223 * track (or head) before the selected one
225 while (item != &e->head) {
226 struct simple_track *t = to_simple_track(item);
228 if (!t->marked)
229 break;
230 item = item->prev;
232 return item;
235 static struct list_head *find_insert_before_point(struct editable *e, struct list_head *item)
237 item = item->prev;
238 if (e->nr_marked == 0) {
239 /* move the selected track up one row */
240 return item->prev;
243 /* move marked before the selected
245 * if the selected track itself is marked we find the first unmarked
246 * track (or head) before the selected one
248 while (item != &e->head) {
249 struct simple_track *t = to_simple_track(item);
251 if (!t->marked)
252 break;
253 item = item->prev;
255 return item;
258 void editable_move_after(struct editable *e)
260 struct simple_track *sel;
262 if (e->nr_tracks <= 1 || e->sort_keys[0])
263 return;
265 sel = get_selected(e);
266 if (sel)
267 move_sel(e, find_insert_after_point(e, &sel->node));
270 void editable_move_before(struct editable *e)
272 struct simple_track *sel;
274 if (e->nr_tracks <= 1 || e->sort_keys[0])
275 return;
277 sel = get_selected(e);
278 if (sel)
279 move_sel(e, find_insert_before_point(e, &sel->node));
282 void editable_clear(struct editable *e)
284 struct list_head *item, *next;
286 item = e->head.next;
287 while (item != &e->head) {
288 next = item->next;
289 editable_remove_track(e, to_simple_track(item));
290 item = next;
294 void editable_mark(struct editable *e, const char *filter)
296 struct expr *expr = NULL;
297 struct simple_track *t;
299 if (filter) {
300 expr = parse_filter(filter);
301 if (expr == NULL)
302 return;
305 list_for_each_entry(t, &e->head, node) {
306 e->nr_marked -= t->marked;
307 t->marked = 0;
308 if (expr == NULL || expr_eval(expr, t->info)) {
309 t->marked = 1;
310 e->nr_marked++;
313 e->win->changed = 1;
316 void editable_unmark(struct editable *e)
318 struct simple_track *t;
320 list_for_each_entry(t, &e->head, node) {
321 e->nr_marked -= t->marked;
322 t->marked = 0;
324 e->win->changed = 1;
327 void editable_invert_marks(struct editable *e)
329 struct simple_track *t;
331 list_for_each_entry(t, &e->head, node) {
332 e->nr_marked -= t->marked;
333 t->marked ^= 1;
334 e->nr_marked += t->marked;
336 e->win->changed = 1;
339 int __editable_for_each_sel(struct editable *e, int (*cb)(void *data, struct track_info *ti),
340 void *data, int reverse)
342 int rc = 0;
344 if (e->nr_marked) {
345 /* treat marked tracks as selected */
346 rc = simple_list_for_each_marked(&e->head, cb, data, reverse);
347 } else {
348 struct simple_track *t = get_selected(e);
350 if (t)
351 rc = cb(data, t->info);
353 return rc;
356 int editable_for_each_sel(struct editable *e, int (*cb)(void *data, struct track_info *ti),
357 void *data, int reverse)
359 int rc;
361 rc = __editable_for_each_sel(e, cb, data, reverse);
362 if (e->nr_marked == 0)
363 window_down(e->win, 1);
364 return rc;