implement saving
[melted_gui] / src / playlist.c
1 /*
2 * playlist.c -- GTK+ 2 omnplay
3 * Copyright (C) 2011 Maksym Veremeyenko <verem@m1stereo.tv>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU 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, write to the Free Software Foundation,
17 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <gtk/gtk.h>
28 #include <gdk/gdkkeysyms.h>
29 #include <pthread.h>
30
31 #include <errno.h>
32
33 #include "playlist.h"
34 #include "ui.h"
35 #include "timecode.h"
36 #include "library.h"
37
38 extern GtkTargetEntry drag_targets[];
39
40 static void playlist_get_selected_items_idx_iter
41 (
42 GtkTreeModel *model,
43 GtkTreePath *path,
44 GtkTreeIter *iter,
45 gpointer data
46 )
47 {
48 int idx, l;
49 int **plist = (int**)data;
50 int *list = *plist;
51
52 gtk_tree_model_get(model, iter, 7, &idx, -1);
53
54 if(!list)
55 {
56 list = (int*)malloc(sizeof(int));
57 list[0] = -1;
58 };
59
60 /* find numbers of items in list */
61 for(l = 0; -1 != list[l]; l++);
62 g_warning("playlist_get_selected_items_idx_iter: l=%d", l);
63
64 /* realloc items */
65 list = (int*)realloc(list, (l + 2) * sizeof(int));
66
67 /* clean last item */
68 list[l + 1] = -1;
69
70 /* setup items */
71 list[l] = idx;
72
73 *plist = list;
74 };
75
76 int* playlist_get_selected_items_idx(instance_t* app, int *count)
77 {
78 int* list = NULL, l = 0;
79 GtkTreeSelection *selection;
80
81 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(app->playlist_grid));
82 if(selection)
83 {
84 gtk_tree_selection_selected_foreach(
85 selection,
86 playlist_get_selected_items_idx_iter,
87 &list);
88
89 if(list)
90 for(l = 0; -1 != list[l]; l++);
91 };
92
93 *count = l;
94 return list;
95 };
96
97
98 static void playlist_drag_data_get_cb(GtkWidget *widget, GdkDragContext *context,
99 GtkSelectionData *selection_data, guint info, guint time, gpointer userdata)
100 {
101 int *list, i, c;
102 playlist_item_t* items;
103 instance_t* app = (instance_t*)userdata;
104
105 g_warning("playlist_drag_data_get_cb");
106
107 list = playlist_get_selected_items_idx(app, &c);
108 if(!list) return;
109
110 /* clear delete flag */
111 for(i = 0; i < app->playlist.count; i++)
112 app->playlist.item[i].del = 0;
113
114 items = (playlist_item_t*)malloc(sizeof(playlist_item_t) * c);
115 for(i = 0; i < c; i++)
116 {
117 items[i] = app->playlist.item[list[i]];
118 if(context->action == GDK_ACTION_MOVE)
119 app->playlist.item[list[i]].del = 1;
120 }
121 gtk_selection_data_set(selection_data, selection_data->target, 8,
122 (const guchar *)items, sizeof(playlist_item_t) * c);
123
124 free(items);
125 free(list);
126 };
127
128 static void playlist_drag_begin_cb(GtkWidget *widget, GdkDragContext *context, gpointer userdata)
129 {
130 g_warning("playlist_drag_begin_cb");
131 gtk_drag_source_set_icon_stock(widget, GTK_STOCK_DND);
132 };
133
134 static void playlist_drag_data_received(GtkWidget *widget, GdkDragContext *context,
135 gint x, gint y, GtkSelectionData *selection_data, guint info, guint time, gpointer userdata)
136 {
137 int c, i, idx;
138 playlist_item_type_t t;
139 playlist_item_t* items;
140 GtkTreePath *path = NULL;
141 instance_t* app = (instance_t*)userdata;
142
143 g_warning("playlist_drag_data_received: context->action=%d", context->action);
144
145 items = (playlist_item_t*)gtk_selection_data_get_data(selection_data);
146 c = gtk_selection_data_get_length(selection_data);
147
148 if(c % sizeof(playlist_item_t))
149 {
150 g_warning("playlist_drag_data_received: ODD ITEMS");
151 }
152 else
153 {
154 c /= sizeof(playlist_item_t);
155
156 if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), x, y, &path, NULL, NULL, NULL))
157 {
158 idx = gtk_tree_path_get_indices(path)[0];
159 gtk_tree_path_free(path);
160
161 g_warning("playlist_drag_data_received: gtk_tree_path_get_indice[0]=%d", idx);
162
163 /* normalize, FIX ME */
164 idx--; if(idx < 0) idx = 0;
165 }
166 else
167 idx = app->playlist.count;
168
169 g_warning("playlist_drag_data_received: idx=%d", idx);
170
171 if(playlist_insert_check(app, idx, &t))
172 {
173 for(i = 0; i < c; i++)
174 {
175 items[i].type = t;
176 items[i].error = 0;
177 };
178 playlist_insert_items(app, idx, items, c);
179 };
180 };
181
182 /* Finish the drag */
183 gtk_drag_finish(context, TRUE, FALSE, time);
184 };
185
186 static void playlist_drag_data_delete(GtkWidget *widget, GdkDragContext *context, gpointer userdata)
187 {
188 int c, i, *list;
189 instance_t* app = (instance_t*)userdata;
190
191 g_warning("playlist_drag_data_delete");
192
193 list = (int*)malloc(sizeof(int) * MAX_PLAYLIST_ITEMS);
194
195 for(i = 0, c = 0; i < app->playlist.count; i++)
196 if(app->playlist.item[i].del)
197 if(!playlist_idx_cued(app, i, NULL))
198 {
199 /* save index */
200 list[c++] = i;
201 g_warning("playlist_drag_data_delete: i=%d, c=%d", i, c);
202 };
203
204 if(c)
205 playlist_delete_items(app, list, c, 0);
206
207 free(list);
208 };
209
210 /*
211 * http://www.mail-archive.com/mahogany-users@lists.sourceforge.net/msg00286.html
212 */
213 static gboolean playlist_drag_motion(GtkWidget *widget, GdkDragContext *context,
214 gint x, gint y, guint time, gpointer data)
215 {
216 gboolean same;
217 GtkWidget *source_widget;
218
219 g_warning("playlist_grid_drag_motion");
220
221 /* Get source widget and check if it is the same as the
222 * destination widget.
223 */
224 source_widget = gtk_drag_get_source_widget(context);
225 same = ((source_widget == widget) ? TRUE : FALSE);
226
227 /* Put additional checks here, perhaps if same is FALSE then
228 * set the default drag to GDK_ACTION_COPY.
229 */
230
231 /* Say we just want to allow GDK_ACTION_MOVE, first we check
232 * if that is in the list of allowed actions on the dc. If
233 * so then we set it to that. Note if the user holds down the
234 * ctrl key then the only flag in dc->actions will be
235 * GDK_ACTION_COPY. The constraint for dc->actions is that
236 * specified from the given actions in gtk_drag_dest_set() and
237 * gtk_drag_source_set().
238 */
239 if(same)
240 {
241 if(context->actions == GDK_ACTION_MOVE)
242 gdk_drag_status(context, GDK_ACTION_COPY, time);
243 else
244 gdk_drag_status(context, GDK_ACTION_MOVE, time);
245 }
246 else
247 gdk_drag_status(context, context->actions, time);
248
249 return(TRUE);
250 }
251
252
253 int playlist_item_index(instance_t* app, int start, int int_idx)
254 {
255 if(start < 0 || start >= app->playlist.count)
256 return -1;
257
258 while(1)
259 {
260 if(app->playlist.item[start].int_idx == int_idx)
261 return start;
262
263 if(app->playlist.item[start].type & PLAYLIST_BLOCK_END)
264 break;
265
266 start++;
267 };
268
269 return -1;
270 };
271
272
273 void playlist_init(instance_t* app)
274 {
275 gtk_drag_source_set(app->playlist_grid, GDK_BUTTON1_MASK,
276 drag_targets, 1, (GdkDragAction)(GDK_ACTION_COPY | GDK_ACTION_MOVE));
277
278 gtk_drag_dest_set(app->playlist_grid, (GtkDestDefaults)(GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP),
279 drag_targets, 1, (GdkDragAction)(GDK_ACTION_COPY | GDK_ACTION_MOVE));
280
281 g_signal_connect (app->playlist_grid, "drag_data_get", G_CALLBACK(playlist_drag_data_get_cb), app);
282 g_signal_connect (app->playlist_grid, "drag_begin", G_CALLBACK(playlist_drag_begin_cb), app);
283 g_signal_connect (app->playlist_grid, "drag_data_received", G_CALLBACK (playlist_drag_data_received), app);
284 g_signal_connect (app->playlist_grid, "drag_data_delete", G_CALLBACK (playlist_drag_data_delete), app);
285 g_signal_connect (app->playlist_grid, "drag_motion", G_CALLBACK (playlist_drag_motion), app);
286 };
287
288 void playlist_release(instance_t* app)
289 {
290 };
291
292 int playlist_idx_cued(instance_t* app, int idx, int* player_idx)
293 {
294 int i;
295
296 for(i = 0; i < app->players.count; i++)
297 {
298 int a, b;
299
300 a = app->players.item[i].playlist_start;
301 b = app->players.item[i].playlist_length;
302
303 if(b <= 0)
304 continue;
305
306 b = a + b - 1;
307
308 if(idx >= a && idx <= b)
309 {
310 if(player_idx)
311 *player_idx = i;
312 return 1;
313 };
314 };
315
316 return 0;
317 };
318
319 int playlist_range_cued(instance_t* app, int start, int stop)
320 {
321 int i;
322
323 for(i = start; i <= stop; i++)
324 if(playlist_idx_cued(app, i, NULL))
325 return 1;
326
327 return 0;
328 };
329
330 void playlist_block(instance_t* app, int loop)
331 {
332 int start, stop, i, c;
333 int* list = playlist_get_selected_items_idx(app, &c);
334
335 if(!list)
336 return;
337
338 pthread_mutex_lock(&app->playlist.lock);
339 pthread_mutex_lock(&app->players.lock);
340
341 start = list[0];
342 stop = list[c - 1];
343
344 if(loop)
345 loop = PLAYLIST_BLOCK_LOOP;
346
347 if(!playlist_range_cued(app, start, stop))
348 {
349 /* update selected item */
350 for(i = start; i <= stop; i++)
351 {
352 int t = PLAYLIST_BLOCK_BODY | loop;
353
354 if(i == start) t |= PLAYLIST_BLOCK_BEGIN;
355 if(i == stop) t |= PLAYLIST_BLOCK_END;
356
357 app->playlist.item[i].type = (playlist_item_type_t)t;
358
359 ui_playlist_draw_item(app, i);
360 };
361
362 /* update border items */
363 if(start && !(app->playlist.item[start - 1].type & PLAYLIST_BLOCK_END))
364 {
365 app->playlist.item[start - 1].type = (playlist_item_type_t)(PLAYLIST_BLOCK_END
366 | app->playlist.item[start - 1].type);
367 ui_playlist_draw_item(app, start - 1);
368 };
369 if((stop + 1) < app->playlist.count && !(app->playlist.item[stop + 1].type & PLAYLIST_BLOCK_BEGIN))
370 {
371 app->playlist.item[stop + 1].type = (playlist_item_type_t)(PLAYLIST_BLOCK_BEGIN
372 | app->playlist.item[stop + 1].type);
373 ui_playlist_draw_item(app, stop + 1);
374 };
375 }
376 else
377 g_warning("omnplay_playlist_block: range [%d %d] do OVERLAP player\n",
378 start, stop);
379
380 pthread_mutex_unlock(&app->players.lock);
381 pthread_mutex_unlock(&app->playlist.lock);
382
383 free(list);
384 };
385
386 int playlist_get_first_selected_item_idx(instance_t* app)
387 {
388 int idx, c;
389 int* list = playlist_get_selected_items_idx(app, &c);
390 if(!list)
391 return -1;
392 idx = list[0];
393 free(list);
394 return idx;
395 };
396
397 int playlist_get_block(instance_t* app, int idx, int* pstart, int* pstop)
398 {
399 int start, stop;
400
401 for(start = idx; start >= 0; start--)
402 if(app->playlist.item[start].type & PLAYLIST_BLOCK_BEGIN)
403 break;
404
405 for(stop = idx; stop < app->playlist.count; stop++)
406 if(app->playlist.item[stop].type & PLAYLIST_BLOCK_END)
407 break;
408
409 g_warning("playlist_get_block: range %d -> %d\n", start, stop);
410
411 /* check block range */
412 if(start >= 0 && stop < app->playlist.count)
413 {
414 *pstart = start;
415 *pstop = stop;
416 return (stop - start + 1);
417 };
418
419 return -1;
420 };
421
422 player_t *playlist_get_player_at_pos(instance_t* app, int pos)
423 {
424 /* check player range */
425 if(app->playlist.item[pos].player > -1 && app->playlist.item[pos].player < app->players.count)
426 return &app->players.item[app->playlist.item[pos].player];
427
428 return NULL;
429 };
430
431 void playlist_delete_items(instance_t* app, int* idxs, int count, int sel)
432 {
433 int i, j, idx;
434
435 pthread_mutex_lock(&app->playlist.lock);
436 pthread_mutex_lock(&app->players.lock);
437
438 for(j = 0; j < count; j++)
439 {
440 idx = idxs[j] - j;
441
442 /* fix block types */
443 if( app->playlist.item[idx].type != PLAYLIST_ITEM_BLOCK_BODY &&
444 app->playlist.item[idx].type != PLAYLIST_ITEM_LOOP_BODY)
445 {
446 if(idx)
447 app->playlist.item[idx - 1].type = (playlist_item_type_t)(app->playlist.item[idx - 1].type |
448 PLAYLIST_BLOCK_END);
449 if(idx + 1 < app->playlist.count)
450 app->playlist.item[idx + 1].type = (playlist_item_type_t)(app->playlist.item[idx + 1].type |
451 PLAYLIST_BLOCK_BEGIN);
452 };
453
454 /* shift playlist items */
455 memmove
456 (
457 &app->playlist.item[idx],
458 &app->playlist.item[idx + 1],
459 (app->playlist.count - idx - 1) * sizeof(playlist_item_t)
460 );
461
462 /* decrement items count */
463 app->playlist.count--;
464
465 /* increment servers indexes */
466 for(i = 0; i < app->players.count; i++)
467 if(app->players.item[i].playlist_start >= idx)
468 app->players.item[i].playlist_start--;
469 };
470
471 /* redraw playlist */
472 ui_playlist_draw(app);
473
474 /* select */
475 if(sel)
476 ui_playlist_select_item(app, idxs[0]);
477
478 pthread_mutex_unlock(&app->players.lock);
479 pthread_mutex_unlock(&app->playlist.lock);
480 };
481
482 void playlist_delete_selected_items(instance_t* app)
483 {
484 int i, cnt1, cnt2;
485 int *list1, *list2;
486
487 list1 = playlist_get_selected_items_idx(app, &cnt1);
488 if(!list1) return;
489
490 list2 = (int*)malloc(sizeof(int) * cnt1);
491
492 for(i = 0, cnt2 = 0; i < cnt1; i++)
493 {
494 /* check for playing block */
495 if(playlist_idx_cued(app, list1[i], NULL))
496 continue;
497
498 /* save index */
499 list2[cnt2++] = list1[i];
500 };
501
502 if(cnt2)
503 playlist_delete_items(app, list2, cnt2, 1);
504
505 free(list2);
506 free(list1);
507 };
508
509 int playlist_insert_check(instance_t* app, int idx, playlist_item_type_t* t)
510 {
511 *t = PLAYLIST_ITEM_BLOCK_SINGLE;
512
513 /* before or after playlist */
514 if(!idx || idx == app->playlist.count)
515 return 1;
516
517 /* check for block borders */
518 if( app->playlist.item[idx - 1].type & PLAYLIST_BLOCK_END &&
519 app->playlist.item[idx + 0].type & PLAYLIST_BLOCK_BEGIN)
520 return 1;
521
522 /* check for playing block */
523 if(playlist_idx_cued(app, idx, NULL))
524 return 0;
525
526 if(app->playlist.item[idx].type & PLAYLIST_BLOCK_LOOP)
527 *t = PLAYLIST_ITEM_LOOP_BODY;
528 else
529 *t = PLAYLIST_ITEM_BLOCK_BODY;
530
531 return 1;
532 };
533
534 void playlist_insert_items(instance_t* app, int idx, playlist_item_t* items, int count)
535 {
536 int i;
537
538 pthread_mutex_lock(&app->playlist.lock);
539 pthread_mutex_lock(&app->players.lock);
540
541 /* shift playlist items */
542 memmove
543 (
544 &app->playlist.item[idx + count],
545 &app->playlist.item[idx],
546 (app->playlist.count - idx) * sizeof(playlist_item_t)
547 );
548
549 /* copy new items */
550 memcpy
551 (
552 &app->playlist.item[idx],
553 items,
554 count * sizeof(playlist_item_t)
555 );
556
557 /* increment servers indexes */
558 for(i = 0; i < app->players.count; i++)
559 if(app->players.item[i].playlist_start >= idx)
560 app->players.item[i].playlist_start += idx;
561
562 /* increment items count */
563 app->playlist.count += count;
564
565 /* redraw playlist */
566 ui_playlist_draw(app);
567
568 /* select */
569 ui_playlist_select_item(app, idx);
570
571 pthread_mutex_unlock(&app->players.lock);
572 pthread_mutex_unlock(&app->playlist.lock);
573 };
574
575 void playlist_item_copy(instance_t* app)
576 {
577 int *list, i, c;
578
579 list = playlist_get_selected_items_idx(app, &c);
580 if(!list) return;
581
582 for(i = 0; i < c; i++)
583 app->clipboard.item[i] = app->playlist.item[list[i]];
584 app->clipboard.count = c;
585
586 free(list);
587 };
588
589 void playlist_item_paste(instance_t* app, int after)
590 {
591 int idx, i;
592 playlist_item_type_t t;
593
594 /* find insert position */
595 idx = playlist_get_first_selected_item_idx(app);
596 if(idx < 0)
597 idx = 0;
598 else
599 idx += (after)?1:0;
600
601 if(!playlist_insert_check(app, idx, &t))
602 return;
603
604 /* clear item */
605 if(app->clipboard.count)
606 {
607 for(i = 0; i < app->clipboard.count; i++)
608 {
609 app->clipboard.item[i].type = t;
610 app->clipboard.item[i].error = 0;
611 };
612 playlist_insert_items(app, idx, app->clipboard.item, app->clipboard.count);
613 };
614 };
615
616 void playlist_item_swap(instance_t* app, int dir)
617 {
618 int sel, a, b, e = 1;
619 playlist_item_t item;
620
621 /* find insert position */
622 sel = playlist_get_first_selected_item_idx(app);
623 if(sel < 0)
624 return;
625
626 if(dir < 0)
627 {
628 a = sel - 1;
629 b = sel;
630 sel = a;
631 }
632 else
633 {
634 a = sel;
635 b = sel + 1;
636 sel = b;
637 };
638
639 /* check for playing block */
640 if(playlist_idx_cued(app, a, NULL) || playlist_idx_cued(app, b, NULL))
641 return;
642
643 pthread_mutex_lock(&app->playlist.lock);
644 pthread_mutex_lock(&app->players.lock);
645
646 /* swap */
647 item = app->playlist.item[a];
648 app->playlist.item[a] = app->playlist.item[b];
649 app->playlist.item[b] = item;
650
651 /* rewite type */
652 if(app->playlist.item[a].type != app->playlist.item[b].type)
653 {
654 e = 0;
655 app->playlist.item[a].type = PLAYLIST_ITEM_BLOCK_SINGLE;
656 app->playlist.item[b].type = PLAYLIST_ITEM_BLOCK_SINGLE;
657 };
658
659 /* redraw main items */
660 ui_playlist_draw_item(app, a);
661 ui_playlist_draw_item(app, b);
662
663 /* fix block types */
664 if(a && !e)
665 {
666 app->playlist.item[a - 1].type = (playlist_item_type_t)(app->playlist.item[a - 1].type |
667 PLAYLIST_BLOCK_END);
668 ui_playlist_draw_item(app, a - 1);
669 };
670 if(b + 1 < app->playlist.count && !e)
671 {
672 app->playlist.item[b + 1].type = (playlist_item_type_t)(app->playlist.item[b + 1].type |
673 PLAYLIST_BLOCK_BEGIN);
674 ui_playlist_draw_item(app, b + 1);
675 };
676
677 /* select */
678 ui_playlist_select_item(app, sel);
679
680 pthread_mutex_unlock(&app->players.lock);
681 pthread_mutex_unlock(&app->playlist.lock);
682 };
683
684 #if 0
685 static int playlist_load_file_ply(instance_t* app, char* filename)
686 {
687 FILE* f;
688 char *ID, *CH, *B, *IN, *OUT, *DUR, *REST, *l;
689 int count = 0, i;
690 playlist_item_t* items;
691
692 /* allocate space for strings and items */
693 items = malloc(sizeof(playlist_item_t) * MAX_PLAYLIST_ITEMS);
694 memset(items, 0, sizeof(playlist_item_t) * MAX_PLAYLIST_ITEMS);
695 ID = malloc(PATH_MAX);
696 CH = malloc(PATH_MAX);
697 B = malloc(PATH_MAX);
698 IN = malloc(PATH_MAX);
699 OUT = malloc(PATH_MAX);
700 DUR = malloc(PATH_MAX);
701 REST = malloc(PATH_MAX);
702 l = malloc(PATH_MAX);
703
704 /* open and process file */
705 f = fopen(filename, "rt");
706 if(f)
707 {
708 while( !feof(f) )
709 {
710 char* s;
711
712 /* load string */
713 memset(l, 0, PATH_MAX);
714 fgets(l, PATH_MAX, f);
715
716 /* remove newlines */
717 if( (s = strchr(l, '\n')) ) *s = 0;
718 if( (s = strchr(l, '\r')) ) *s = 0;
719 if( (s = strchr(l, '\t')) ) *s = 0;
720
721 /* check for empty line */
722 if(l[0] && l[0] != '#')
723 {
724 if (6 != sscanf(l, "%128[^,],%128[^,],%128[^,],%128[^,],%128[^,],%128[^,],%s",
725 ID, CH, B, IN, OUT, DUR, REST))
726 {
727 int b = atol(B);
728 /* setup item */
729 tc2frames(IN, 25.0, &items[count].in);
730 tc2frames(DUR, 25.0, &items[count].dur);
731 strncpy(items[count].id, ID, PATH_MAX);
732 items[count].player = atol(CH) - 1;
733 switch(b)
734 {
735 case 1: items[count].type = PLAYLIST_ITEM_BLOCK_SINGLE; break;
736 case 2: items[count].type = PLAYLIST_ITEM_LOOP_BEGIN; break;
737 case 3: items[count].type = PLAYLIST_ITEM_LOOP_BODY; break;
738 case 4: items[count].type = PLAYLIST_ITEM_LOOP_END; break;
739 case 6: items[count].type = PLAYLIST_ITEM_BLOCK_END; break;
740 case 0:
741 if(!count)
742 items[count].type = PLAYLIST_ITEM_BLOCK_BEGIN;
743 else if(items[count - 1].type == PLAYLIST_ITEM_BLOCK_BEGIN ||
744 items[count - 1].type == PLAYLIST_ITEM_BLOCK_BODY)
745 items[count].type = PLAYLIST_ITEM_BLOCK_BODY;
746 else
747 items[count].type = PLAYLIST_ITEM_BLOCK_BEGIN;
748 break;
749 default:
750 if(b >= 1024)
751 items[count].type = b - 1024;
752 };
753 #if 0
754 {
755 char* n;
756 switch(items[count].type)
757 {
758 case PLAYLIST_ITEM_BLOCK_BEGIN: n = "BLOCK_BEGIN"; break;
759 case PLAYLIST_ITEM_BLOCK_BODY: n = "BLOCK_BODY"; break;
760 case PLAYLIST_ITEM_BLOCK_END: n = "BLOCK_END"; break;
761 case PLAYLIST_ITEM_BLOCK_SINGLE: n = "BLOCK_SINGLE"; break;
762 case PLAYLIST_ITEM_LOOP_BEGIN: n = "LOOP_BEGIN"; break;
763 case PLAYLIST_ITEM_LOOP_BODY: n = "LOOP_BODY"; break;
764 case PLAYLIST_ITEM_LOOP_END: n = "LOOP_END"; break;
765 case PLAYLIST_ITEM_LOOP_SINGLE: n = "LOOP_SINGLE"; break;
766 };
767 fprintf(stderr, "src=[%s]\ndst=[idx=%d,block=%s,block_id=%d,in=%d,out=%d]\n",
768 l, count, n, items[count].type, items[count].in, items[count].dur);
769 };
770 #endif
771
772 count++;
773 }
774 };
775 }
776
777 fclose(f);
778 }
779
780 /* add loaded items to playlist */
781 if(count)
782 {
783 pthread_mutex_lock(&app->playlist.lock);
784 for(i = 0; i < count && app->playlist.count + 1 < MAX_PLAYLIST_ITEMS; i++)
785 {
786 // omnplay_library_normalize_item(app, &items[i]);
787 app->playlist.item[app->playlist.count++] = items[i];
788 };
789 app->playlist.ver_curr++;
790 pthread_mutex_unlock(&app->playlist.lock);
791 }
792
793 /* free data */
794 free(items);
795 free(ID);
796 free(CH);
797 free(IN);
798 free(OUT);
799 free(DUR);
800 free(REST);
801 free(l);
802
803 return count;
804 };
805
806 static int playlist_save_file_ply(instance_t* app, char* filename)
807 {
808 };
809 #endif
810
811 static int playlist_load_plt(instance_t* app, char* filename, char* err_buf, int err_len)
812 {
813 return 0;
814 };
815
816 static int playlist_save_plt(instance_t* app, char* filename, char* err_buf, int err_len)
817 {
818 int i;
819 FILE* f;
820 char in[12], dur[12];
821
822 f = fopen(filename, "wt");
823
824 if(!f)
825 {
826 i = errno;
827 strncpy(err_buf, strerror(i), err_len);
828 return -i;
829 };
830
831 for(i = 0; i < app->playlist.count; i++)
832 fprintf
833 (
834 f,
835 "%s\n" // id
836 "\t%s\n" // title
837 "\t%s\n" // in
838 "\t%s\n" // dur
839 "\t%d\n" // player
840 "\t%d\n", // type
841
842 app->playlist.item[i].id,
843 app->playlist.item[i].title,
844 frames2tc(app->playlist.item[i].in, 25.0, in),
845 frames2tc(app->playlist.item[i].dur, 25.0, dur),
846 app->playlist.item[i].player + 1,
847 app->playlist.item[i].type + 1024
848 );
849
850 fclose(f);
851
852 return 0;
853 };
854
855 static int playlist_load_plx(instance_t* app, char* filename, char* err_buf, int err_len)
856 {
857 strncpy(err_buf, "Method not implemented", err_len);
858 return -1;
859 };
860
861 static int playlist_save_plx(instance_t* app, char* filename, char* err_buf, int err_len)
862 {
863 strncpy(err_buf, "Method not implemented", err_len);
864 return -1;
865 };
866
867 static struct ui_playlist_io_funcs playlist_io[] =
868 {
869 {
870 "Text formatted playlist (*.plt)",
871 "*.plt",
872 playlist_load_plt,
873 playlist_save_plt
874 },
875 {
876 "Xml formatted playlist (*.plx)",
877 "*.plx",
878 playlist_load_plx,
879 playlist_save_plx,
880 },
881 {
882 NULL,
883 NULL,
884 NULL,
885 NULL
886 }
887 };
888
889 void playlist_load(instance_t* app)
890 {
891 ui_playlist_load(app, (app->playlist.path)?app->playlist.path:getenv("HOME"), playlist_io);
892 };
893
894 void playlist_save(instance_t* app)
895 {
896 ui_playlist_save(app, (app->playlist.path)?app->playlist.path:getenv("HOME"), playlist_io);
897 };
898
899 void playlist_relink(instance_t* app)
900 {
901 int i, cnt;
902 int *list;
903
904 pthread_mutex_lock(&app->playlist.lock);
905 list = playlist_get_selected_items_idx(app, &cnt);
906 if(list)
907 {
908 for(i = 0; i < cnt; i++)
909 {
910 /* check for playing block */
911 if(playlist_idx_cued(app, list[i], NULL))
912 continue;
913
914 /* relink item */
915 library_relink_item(app, &app->playlist.item[list[i]]);
916 };
917
918 free(list);
919 };
920 pthread_mutex_unlock(&app->playlist.lock);
921
922 /* redraw playlist */
923 ui_playlist_draw(app);
924 };
925
926 void playlist_item_add(instance_t* app, int after)
927 {
928 int idx;
929 playlist_item_t item;
930 playlist_item_type_t t;
931
932 /* find insert position */
933 idx = playlist_get_first_selected_item_idx(app);
934 if(idx < 0)
935 idx = 0;
936 else
937 idx += (after)?1:0;
938
939 if(!playlist_insert_check(app, idx, &t))
940 return;
941
942 g_warning("allowed insert into idx=%d\n", idx);
943
944 /* clear item */
945 memset(&item, 0, sizeof(playlist_item_t));
946 if(ui_playlist_item_dialog(app, &item))
947 {
948 library_normalize_item(app, &item);
949 item.type = t;
950 playlist_insert_items(app, idx, &item, 1);
951 };
952 };
953
954 void playlist_item_edit(instance_t* app)
955 {
956 int idx;
957 playlist_item_t item;
958
959 /* find insert position */
960 idx = playlist_get_first_selected_item_idx(app);
961
962 if(idx < 0)
963 return;
964
965 /* check for playing block */
966 if(playlist_idx_cued(app, idx, NULL))
967 return;
968
969 item = app->playlist.item[idx];
970
971 if(ui_playlist_item_dialog(app, &item))
972 {
973 library_normalize_item(app, &item);
974 app->playlist.item[idx] = item;
975 ui_playlist_draw_item(app, idx);
976 };
977 };
978
979 void playlist_item_add_from_library(instance_t* app, int after)
980 {
981 };
982
983 void playlist_normalize(instance_t* app)
984 {
985 int i;
986
987 /* normalize playlist */
988 for(i = 0; i < app->playlist.count; i++)
989 if(library_normalize_item(app, &app->playlist.item[i]))
990 ui_playlist_draw_item(app, i);
991 };