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