1 /*
2 * Copyright 2008-2013 Various Authors
3 * Copyright 2006 Timo Hirvonen
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "editable.h"
20 #include "search.h"
21 #include "track.h"
22 #include "track_info.h"
23 #include "expr.h"
24 #include "filters.h"
25 #include "locking.h"
26 #include "mergesort.h"
27 #include "xmalloc.h"
28
29 static const struct searchable_ops simple_search_ops = {
30 .get_prev = simple_track_get_prev,
31 .get_next = simple_track_get_next,
32 .get_current = simple_track_search_get_current,
33 .matches = simple_track_search_matches
34 };
35
get_selected(struct editable * e)36 static struct simple_track *get_selected(struct editable *e)
37 {
38 struct iter sel;
39
40 if (window_get_sel(e->shared->win, &sel))
41 return iter_to_simple_track(&sel);
42 return NULL;
43 }
44
editable_shared_init(struct editable_shared * shared,editable_free_track free_track)45 void editable_shared_init(struct editable_shared *shared,
46 editable_free_track free_track)
47 {
48 shared->win = window_new(simple_track_get_prev, simple_track_get_next);
49 shared->sort_keys = xnew(sort_key_t, 1);
50 shared->sort_keys[0] = SORT_INVALID;
51 shared->sort_str[0] = 0;
52 shared->free_track = free_track;
53 shared->owner = NULL;
54
55 struct iter iter = { 0 };
56 shared->searchable = searchable_new(shared->win, &iter,
57 &simple_search_ops);
58 }
59
editable_init(struct editable * e,struct editable_shared * shared,int take_ownership)60 void editable_init(struct editable *e, struct editable_shared *shared,
61 int take_ownership)
62 {
63 list_init(&e->head);
64 e->tree_root = RB_ROOT;
65 e->nr_tracks = 0;
66 e->nr_marked = 0;
67 e->total_time = 0;
68 e->shared = shared;
69
70
71 if (take_ownership)
72 editable_take_ownership(e);
73 }
74
editable_owns_shared(struct editable * e)75 static int editable_owns_shared(struct editable *e)
76 {
77 return e->shared->owner == e;
78 }
79
editable_take_ownership(struct editable * e)80 void editable_take_ownership(struct editable *e)
81 {
82 if (!editable_owns_shared(e)) {
83 e->shared->owner = e;
84 window_set_contents(e->shared->win, &e->head);
85 e->shared->win->changed = 1;
86
87 struct iter iter = { .data0 = &e->head };
88 searchable_set_head(e->shared->searchable, &iter);
89 }
90 }
91
do_editable_add(struct editable * e,struct simple_track * track,int tiebreak)92 static void do_editable_add(struct editable *e, struct simple_track *track, int tiebreak)
93 {
94 sorted_list_add_track(&e->head, &e->tree_root, track,
95 e->shared->sort_keys, tiebreak);
96 e->nr_tracks++;
97 if (track->info->duration != -1)
98 e->total_time += track->info->duration;
99 if (editable_owns_shared(e))
100 window_changed(e->shared->win);
101 }
102
editable_add(struct editable * e,struct simple_track * track)103 void editable_add(struct editable *e, struct simple_track *track)
104 {
105 do_editable_add(e, track, +1);
106 }
107
editable_add_before(struct editable * e,struct simple_track * track)108 void editable_add_before(struct editable *e, struct simple_track *track)
109 {
110 do_editable_add(e, track, -1);
111 }
112
editable_remove_track(struct editable * e,struct simple_track * track)113 void editable_remove_track(struct editable *e, struct simple_track *track)
114 {
115 struct track_info *ti = track->info;
116 struct iter iter;
117
118 editable_track_to_iter(e, track, &iter);
119 if (editable_owns_shared(e))
120 window_row_vanishes(e->shared->win, &iter);
121
122 e->nr_tracks--;
123 e->nr_marked -= track->marked;
124 if (ti->duration != -1)
125 e->total_time -= ti->duration;
126
127 sorted_list_remove_track(&e->head, &e->tree_root, track);
128 e->shared->free_track(e, &track->node);
129 }
130
editable_remove_sel(struct editable * e)131 void editable_remove_sel(struct editable *e)
132 {
133 struct simple_track *t;
134
135 if (e->nr_marked) {
136 /* treat marked tracks as selected */
137 struct list_head *next, *item = e->head.next;
138
139 while (item != &e->head) {
140 next = item->next;
141 t = to_simple_track(item);
142 if (t->marked)
143 editable_remove_track(e, t);
144 item = next;
145 }
146 } else {
147 t = get_selected(e);
148 if (t)
149 editable_remove_track(e, t);
150 }
151 }
152
editable_sort(struct editable * e)153 void editable_sort(struct editable *e)
154 {
155 if (e->nr_tracks <= 1)
156 return;
157 sorted_list_rebuild(&e->head, &e->tree_root, e->shared->sort_keys);
158
159 if (editable_owns_shared(e)) {
160 window_changed(e->shared->win);
161 window_goto_top(e->shared->win);
162 }
163 }
164
editable_shared_set_sort_keys(struct editable_shared * shared,sort_key_t * keys)165 void editable_shared_set_sort_keys(struct editable_shared *shared,
166 sort_key_t *keys)
167 {
168 free(shared->sort_keys);
169 shared->sort_keys = keys;
170 }
171
editable_toggle_mark(struct editable * e)172 void editable_toggle_mark(struct editable *e)
173 {
174 struct simple_track *t;
175
176 t = get_selected(e);
177 if (t) {
178 e->nr_marked -= t->marked;
179 t->marked ^= 1;
180 e->nr_marked += t->marked;
181 if (editable_owns_shared(e)) {
182 e->shared->win->changed = 1;
183 window_down(e->shared->win, 1);
184 }
185 }
186 }
187
move_item(struct editable * e,struct list_head * head,struct list_head * item)188 static void move_item(struct editable *e, struct list_head *head, struct list_head *item)
189 {
190 struct simple_track *t = to_simple_track(item);
191 struct iter iter;
192
193 editable_track_to_iter(e, t, &iter);
194 if (editable_owns_shared(e))
195 window_row_vanishes(e->shared->win, &iter);
196
197 list_del(item);
198 list_add(item, head);
199 }
200
reset_tree(struct editable * e)201 static void reset_tree(struct editable *e)
202 {
203 struct simple_track *old, *first_track;
204
205 old = tree_node_to_simple_track(rb_first(&e->tree_root));
206 first_track = to_simple_track(e->head.next);
207 if (old != first_track) {
208 rb_replace_node(&old->tree_node, &first_track->tree_node, &e->tree_root);
209 RB_CLEAR_NODE(&old->tree_node);
210 }
211 }
212
move_sel(struct editable * e,struct list_head * after)213 static void move_sel(struct editable *e, struct list_head *after)
214 {
215 struct simple_track *t;
216 struct list_head *item, *next;
217 struct iter iter;
218 LIST_HEAD(tmp_head);
219
220 if (e->nr_marked) {
221 /* collect marked */
222 item = e->head.next;
223 while (item != &e->head) {
224 t = to_simple_track(item);
225 next = item->next;
226 if (t->marked)
227 move_item(e, &tmp_head, item);
228 item = next;
229 }
230 } else {
231 /* collect the selected track */
232 t = get_selected(e);
233 if (t)
234 move_item(e, &tmp_head, &t->node);
235 }
236
237 /* put them back to the list after @after */
238 item = tmp_head.next;
239 while (item != &tmp_head) {
240 next = item->next;
241 list_add(item, after);
242 item = next;
243 }
244 reset_tree(e);
245
246 /* select top-most of the moved tracks */
247 editable_track_to_iter(e, to_simple_track(after->next), &iter);
248
249 if (editable_owns_shared(e)) {
250 window_changed(e->shared->win);
251 window_set_sel(e->shared->win, &iter);
252 }
253 }
254
find_insert_after_point(struct editable * e,struct list_head * item)255 static struct list_head *find_insert_after_point(struct editable *e, struct list_head *item)
256 {
257 if (e->nr_marked == 0) {
258 /* move the selected track down one row */
259 return item->next;
260 }
261
262 /* move marked after the selected
263 *
264 * if the selected track itself is marked we find the first unmarked
265 * track (or head) before the selected one
266 */
267 while (item != &e->head) {
268 struct simple_track *t = to_simple_track(item);
269
270 if (!t->marked)
271 break;
272 item = item->prev;
273 }
274 return item;
275 }
276
find_insert_before_point(struct editable * e,struct list_head * item)277 static struct list_head *find_insert_before_point(struct editable *e, struct list_head *item)
278 {
279 item = item->prev;
280 if (e->nr_marked == 0) {
281 /* move the selected track up one row */
282 return item->prev;
283 }
284
285 /* move marked before the selected
286 *
287 * if the selected track itself is marked we find the first unmarked
288 * track (or head) before the selected one
289 */
290 while (item != &e->head) {
291 struct simple_track *t = to_simple_track(item);
292
293 if (!t->marked)
294 break;
295 item = item->prev;
296 }
297 return item;
298 }
299
editable_move_after(struct editable * e)300 void editable_move_after(struct editable *e)
301 {
302 struct simple_track *sel;
303
304 if (e->nr_tracks <= 1 || e->shared->sort_keys[0] != SORT_INVALID)
305 return;
306
307 sel = get_selected(e);
308 if (sel)
309 move_sel(e, find_insert_after_point(e, &sel->node));
310 }
311
editable_move_before(struct editable * e)312 void editable_move_before(struct editable *e)
313 {
314 struct simple_track *sel;
315
316 if (e->nr_tracks <= 1 || e->shared->sort_keys[0] != SORT_INVALID)
317 return;
318
319 sel = get_selected(e);
320 if (sel)
321 move_sel(e, find_insert_before_point(e, &sel->node));
322 }
323
editable_clear(struct editable * e)324 void editable_clear(struct editable *e)
325 {
326 struct list_head *item, *tmp;
327
328 list_for_each_safe(item, tmp, &e->head)
329 editable_remove_track(e, to_simple_track(item));
330 }
331
editable_remove_matching_tracks(struct editable * e,int (* cb)(void * data,struct track_info * ti),void * data)332 void editable_remove_matching_tracks(struct editable *e,
333 int (*cb)(void *data, struct track_info *ti), void *data)
334 {
335 struct list_head *item, *tmp;
336
337 list_for_each_safe(item, tmp, &e->head) {
338 struct simple_track *t = to_simple_track(item);
339 if (cb(data, t->info))
340 editable_remove_track(e, t);
341 }
342 }
343
editable_mark(struct editable * e,const char * filter)344 void editable_mark(struct editable *e, const char *filter)
345 {
346 struct expr *expr = NULL;
347 struct simple_track *t;
348
349 if (filter) {
350 expr = parse_filter(filter);
351 if (expr == NULL)
352 return;
353 }
354
355 list_for_each_entry(t, &e->head, node) {
356 e->nr_marked -= t->marked;
357 t->marked = 0;
358 if (expr == NULL || expr_eval(expr, t->info)) {
359 t->marked = 1;
360 e->nr_marked++;
361 }
362 }
363
364 if (editable_owns_shared(e))
365 e->shared->win->changed = 1;
366 }
367
editable_unmark(struct editable * e)368 void editable_unmark(struct editable *e)
369 {
370 struct simple_track *t;
371
372 list_for_each_entry(t, &e->head, node) {
373 e->nr_marked -= t->marked;
374 t->marked = 0;
375 }
376
377 if (editable_owns_shared(e))
378 e->shared->win->changed = 1;
379 }
380
editable_invert_marks(struct editable * e)381 void editable_invert_marks(struct editable *e)
382 {
383 struct simple_track *t;
384
385 list_for_each_entry(t, &e->head, node) {
386 e->nr_marked -= t->marked;
387 t->marked ^= 1;
388 e->nr_marked += t->marked;
389 }
390
391 if (editable_owns_shared(e))
392 e->shared->win->changed = 1;
393 }
394
_editable_for_each_sel(struct editable * e,track_info_cb cb,void * data,int reverse)395 int _editable_for_each_sel(struct editable *e, track_info_cb cb, void *data,
396 int reverse)
397 {
398 int rc = 0;
399
400 if (e->nr_marked) {
401 /* treat marked tracks as selected */
402 rc = simple_list_for_each_marked(&e->head, cb, data, reverse);
403 } else {
404 struct simple_track *t = get_selected(e);
405
406 if (t)
407 rc = cb(data, t->info);
408 }
409 return rc;
410 }
411
editable_for_each_sel(struct editable * e,track_info_cb cb,void * data,int reverse,int advance)412 int editable_for_each_sel(struct editable *e, track_info_cb cb, void *data,
413 int reverse, int advance)
414 {
415 int rc;
416
417 rc = _editable_for_each_sel(e, cb, data, reverse);
418 if (advance && e->nr_marked == 0 && editable_owns_shared(e))
419 window_down(e->shared->win, 1);
420 return rc;
421 }
422
editable_for_each(struct editable * e,track_info_cb cb,void * data,int reverse)423 int editable_for_each(struct editable *e, track_info_cb cb, void *data,
424 int reverse)
425 {
426 return simple_list_for_each(&e->head, cb, data, reverse);
427 }
428
editable_update_track(struct editable * e,struct track_info * old,struct track_info * new)429 void editable_update_track(struct editable *e, struct track_info *old, struct track_info *new)
430 {
431 struct list_head *item, *tmp;
432 int changed = 0;
433
434 list_for_each_safe(item, tmp, &e->head) {
435 struct simple_track *track = to_simple_track(item);
436 if (track->info == old) {
437 if (new) {
438 track_info_unref(old);
439 track_info_ref(new);
440 track->info = new;
441 } else {
442 editable_remove_track(e, track);
443 }
444 changed = 1;
445 }
446 }
447 if (editable_owns_shared(e))
448 e->shared->win->changed |= changed;
449 }
450
editable_rand(struct editable * e)451 void editable_rand(struct editable *e)
452 {
453 if (e->nr_tracks <=1)
454 return;
455 rand_list_rebuild(&e->head, &e->tree_root);
456
457 if (editable_owns_shared(e)) {
458 window_changed(e->shared->win);
459 window_goto_top(e->shared->win);
460 }
461 }
462
editable_empty(struct editable * e)463 int editable_empty(struct editable *e)
464 {
465 return list_empty(&e->head);
466 }
467