Sort playlist file for library views in same order as view 2
[cmus.git] / editable.c
blobae0dce0d1a0f5b4f88bd9fe86162f6882d408681
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 return simple_track_cmp(a_head, b_head, sort_keys);
111 void editable_sort(struct editable *e)
113 sort_keys = e->sort_keys;
114 list_mergesort(&e->head, list_cmp);
115 window_changed(e->win);
116 window_goto_top(e->win);
119 static void keys_to_str(const char **keys, char *buf)
121 int i, pos = 0;
123 for (i = 0; keys[i]; i++) {
124 const char *key = keys[i];
125 int len = strlen(key);
127 if (sizeof(buf) - pos - len - 2 < 0)
128 break;
130 memcpy(buf + pos, key, len);
131 pos += len;
132 buf[pos++] = ' ';
134 if (pos > 0)
135 pos--;
136 buf[pos] = 0;
139 void editable_set_sort_keys(struct editable *e, const char **keys)
141 free(e->sort_keys);
142 e->sort_keys = keys;
143 editable_sort(e);
144 keys_to_str(keys, e->sort_str);
147 void editable_toggle_mark(struct editable *e)
149 struct simple_track *t;
151 t = get_selected(e);
152 if (t) {
153 e->nr_marked -= t->marked;
154 t->marked ^= 1;
155 e->nr_marked += t->marked;
156 e->win->changed = 1;
157 window_down(e->win, 1);
161 static void move_item(struct editable *e, struct list_head *head, struct list_head *item)
163 struct simple_track *t = to_simple_track(item);
164 struct iter iter;
166 editable_track_to_iter(e, t, &iter);
167 window_row_vanishes(e->win, &iter);
169 list_del(item);
170 list_add(item, head);
173 static void move_sel(struct editable *e, struct list_head *after)
175 struct simple_track *t;
176 struct list_head *item, *next;
177 struct iter iter;
178 LIST_HEAD(tmp_head);
180 if (e->nr_marked) {
181 /* collect marked */
182 item = e->head.next;
183 while (item != &e->head) {
184 t = to_simple_track(item);
185 next = item->next;
186 if (t->marked)
187 move_item(e, &tmp_head, item);
188 item = next;
190 } else {
191 /* collect the selected track */
192 t = get_selected(e);
193 move_item(e, &tmp_head, &t->node);
196 /* put them back to the list after @after */
197 item = tmp_head.next;
198 while (item != &tmp_head) {
199 next = item->next;
200 list_add(item, after);
201 item = next;
204 /* select top-most of the moved tracks */
205 editable_track_to_iter(e, to_simple_track(after->next), &iter);
206 window_set_sel(e->win, &iter);
207 window_changed(e->win);
210 static struct list_head *find_insert_after_point(struct editable *e, struct list_head *item)
212 if (e->nr_marked == 0) {
213 /* move the selected track down one row */
214 return item->next;
217 /* move marked after the selected
219 * if the selected track itself is marked we find the first unmarked
220 * track (or head) before the selected one
222 while (item != &e->head) {
223 struct simple_track *t = to_simple_track(item);
225 if (!t->marked)
226 break;
227 item = item->prev;
229 return item;
232 static struct list_head *find_insert_before_point(struct editable *e, struct list_head *item)
234 item = item->prev;
235 if (e->nr_marked == 0) {
236 /* move the selected track up one row */
237 return item->prev;
240 /* move marked before the selected
242 * if the selected track itself is marked we find the first unmarked
243 * track (or head) before the selected one
245 while (item != &e->head) {
246 struct simple_track *t = to_simple_track(item);
248 if (!t->marked)
249 break;
250 item = item->prev;
252 return item;
255 void editable_move_after(struct editable *e)
257 struct simple_track *sel;
259 if (e->nr_tracks <= 1)
260 return;
262 if (e->sort_keys[0] == NULL && (sel = get_selected(e)))
263 move_sel(e, find_insert_after_point(e, &sel->node));
266 void editable_move_before(struct editable *e)
268 struct simple_track *sel;
270 if (e->nr_tracks <= 1)
271 return;
273 if (e->sort_keys[0] == NULL && (sel = get_selected(e)))
274 move_sel(e, find_insert_before_point(e, &sel->node));
277 void editable_clear(struct editable *e)
279 struct list_head *item, *next;
281 item = e->head.next;
282 while (item != &e->head) {
283 next = item->next;
284 editable_remove_track(e, to_simple_track(item));
285 item = next;
289 void editable_mark(struct editable *e, const char *filter)
291 struct expr *expr = NULL;
292 struct simple_track *t;
294 if (filter) {
295 expr = parse_filter(filter);
296 if (expr == NULL)
297 return;
300 list_for_each_entry(t, &e->head, node) {
301 e->nr_marked -= t->marked;
302 t->marked = 0;
303 if (expr == NULL || expr_eval(expr, t->info)) {
304 t->marked = 1;
305 e->nr_marked++;
308 e->win->changed = 1;
311 void editable_unmark(struct editable *e)
313 struct simple_track *t;
315 list_for_each_entry(t, &e->head, node) {
316 e->nr_marked -= t->marked;
317 t->marked = 0;
319 e->win->changed = 1;
322 void editable_invert_marks(struct editable *e)
324 struct simple_track *t;
326 list_for_each_entry(t, &e->head, node) {
327 e->nr_marked -= t->marked;
328 t->marked ^= 1;
329 e->nr_marked += t->marked;
331 e->win->changed = 1;
334 int __editable_for_each_sel(struct editable *e, int (*cb)(void *data, struct track_info *ti),
335 void *data, int reverse)
337 int rc = 0;
339 if (e->nr_marked) {
340 /* treat marked tracks as selected */
341 rc = simple_list_for_each_marked(&e->head, cb, data, reverse);
342 } else {
343 struct simple_track *t = get_selected(e);
345 if (t)
346 rc = cb(data, t->info);
348 return rc;
351 int editable_for_each_sel(struct editable *e, int (*cb)(void *data, struct track_info *ti),
352 void *data, int reverse)
354 int rc;
356 rc = __editable_for_each_sel(e, cb, data, reverse);
357 if (e->nr_marked == 0)
358 window_down(e->win, 1);
359 return rc;