fix prev item block type update
[melted_gui] / src / omnplay.cpp
1 /*
2 * omnplay.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 #ifndef _GNU_SOURCE
21 #define _GNU_SOURCE
22 #endif
23
24 #ifdef HAVE_CONFIG_H
25 # include <config.h>
26 #endif
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <gtk/gtk.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <pthread.h>
34
35 #include "omnplay.h"
36 #include "ui.h"
37 #include "opts.h"
38 #include "timecode.h"
39
40 #include "omplrclnt.h"
41
42 static gboolean on_main_window_delete_event( GtkWidget *widget, GdkEvent *event, gpointer user_data )
43 {
44 gtk_exit(0);
45 return TRUE;
46 }
47
48 omnplay_instance_t* omnplay_create(int argc, char** argv)
49 {
50 int i, c;
51 omnplay_instance_t* app;
52
53 /* prepare application instance */
54 app = (omnplay_instance_t*)malloc(sizeof(omnplay_instance_t));
55 memset(app, 0, sizeof(omnplay_instance_t));
56
57 /* load parameters from command line */
58 if(!omnplay_opt(argc, argv, app) && app->players.count)
59 app->window = ui_omnplay(app);
60 else
61 omnplay_usage();
62
63 return app;
64 };
65
66 void omnplay_destroy(omnplay_instance_t* app)
67 {
68 free(app);
69 };
70
71 static int find_index_of_playlist_item(omnplay_instance_t* app, int start, int idx)
72 {
73 while(1)
74 {
75 if(app->playlist.item[start].omn_idx == idx)
76 return start;
77
78 if(app->playlist.item[start].type & OMNPLAY_PLAYLIST_BLOCK_END)
79 break;
80
81 start++;
82 };
83
84 return -1;
85 };
86
87 static void omnplay_update_status(omnplay_player_t* player, OmPlrStatus *prev , OmPlrStatus *curr)
88 {
89 int idx;
90 char tc_cur[32], tc_rem[32], state[32], status[32];
91 const char *clip;
92
93 if(curr)
94 {
95 frames2tc(curr->pos - curr->minPos, 25.0, tc_cur);
96 frames2tc(curr->maxPos - curr->pos, 25.0, tc_rem);
97 strcpy(status, "ONLINE");
98 clip = curr->currClipName;
99
100 switch(curr->state)
101 {
102 case omPlrStateStopped: strcpy(state, "STOPPED"); break;
103 case omPlrStateCuePlay: strcpy(state, "CUE_PLAY"); break;
104 case omPlrStatePlay: strcpy(state, "PLAY"); break;
105 case omPlrStateCueRecord: strcpy(state, "CUE_RECORD"); break;
106 case omPlrStateRecord: strcpy(state, "RECORD"); break;
107 };
108 }
109 else
110 {
111 tc_cur[0] = 0;
112 tc_rem[0] = 0;
113 clip = "";
114 state[0] = 0;
115 strcpy(status, "OFFLINE");
116 };
117
118 /* update status in status page */
119 gdk_threads_enter();
120 gtk_label_set_text(GTK_LABEL (player->label_tc_cur), tc_cur);
121 gtk_label_set_text(GTK_LABEL (player->label_tc_rem), tc_rem);
122 gtk_label_set_text(GTK_LABEL (player->label_state), state);
123 gtk_label_set_text(GTK_LABEL (player->label_status), status);
124 gtk_label_set_text(GTK_LABEL (player->label_clip), clip);
125 gdk_flush();
126 gdk_threads_leave();
127
128 /* update remaining time */
129 gdk_threads_enter();
130 pthread_mutex_lock(&player->app->playlist.lock);
131 pthread_mutex_lock(&player->app->players.lock);
132 if(curr->state == omPlrStatePlay || curr->state == omPlrStateCuePlay)
133 {
134 idx = find_index_of_playlist_item(player->app, player->playlist_start, curr->currClipNum);
135 if(idx >= 0)
136 {
137 frames2tc(curr->currClipStartPos + curr->currClipLen - curr->pos, 25.0, tc_rem);
138 omnplay_playlist_draw_item_rem(player->app, idx, tc_rem);
139 }
140 if(curr->currClipNum != prev->currClipNum && 1 != prev->numClips)
141 {
142 tc_rem[0] = 0;
143 idx = find_index_of_playlist_item(player->app, player->playlist_start, prev->currClipNum);
144 if(idx >= 0)
145 omnplay_playlist_draw_item_rem(player->app, idx, tc_rem);
146 };
147 }
148 else
149 {
150 tc_rem[0] = 0;
151 idx = find_index_of_playlist_item(player->app, player->playlist_start, curr->currClipNum);
152 if(idx >= 0)
153 omnplay_playlist_draw_item_rem(player->app, idx, tc_rem);
154 idx = find_index_of_playlist_item(player->app, player->playlist_start, prev->currClipNum);
155 if(idx >= 0)
156 omnplay_playlist_draw_item_rem(player->app, idx, tc_rem);
157 };
158 pthread_mutex_unlock(&player->app->players.lock);
159 pthread_mutex_unlock(&player->app->playlist.lock);
160 gdk_flush();
161 gdk_threads_leave();
162
163
164 memcpy(prev, curr, sizeof(OmPlrStatus));
165 };
166
167 static void* omnplay_thread_proc(void* data)
168 {
169 int r;
170 OmPlrStatus st_curr, st_prev;
171 omnplay_player_t* player = (omnplay_player_t*)data;
172
173 /* connect */
174 pthread_mutex_lock(&player->app->players.lock);
175 r = OmPlrOpen(player->host, player->name, (OmPlrHandle*)&player->handle);
176 pthread_mutex_unlock(&player->app->players.lock);
177 if(r)
178 {
179 fprintf(stderr, "ERROR: OmPlrOpen(%s, %s) failed with 0x%.8X\n",
180 player->host, player->name, r);
181
182 return (void*)r;
183 };
184
185 /* setup to do not reconnect */
186 pthread_mutex_lock(&player->app->players.lock);
187 OmPlrSetRetryOpen((OmPlrHandle)player->handle, 0);
188 pthread_mutex_unlock(&player->app->players.lock);
189
190 /* setup directory */
191 if(player->app->players.path[0])
192 {
193 pthread_mutex_lock(&player->app->players.lock);
194 r = OmPlrClipSetDirectory((OmPlrHandle)player->handle, player->app->players.path);
195 pthread_mutex_unlock(&player->app->players.lock);
196
197 if(r)
198 {
199 fprintf(stderr, "ERROR: OmPlrClipSetDirectory(%s) failed with 0x%.8X\n",
200 player->app->players.path, r);
201
202 pthread_mutex_lock(&player->app->players.lock);
203 OmPlrClose((OmPlrHandle)player->handle);
204 pthread_mutex_unlock(&player->app->players.lock);
205
206 return (void*)r;
207 };
208 };
209
210 /* endless loop */
211 for(r = 0 ; !player->app->f_exit && !r;)
212 {
213 /* sleep */
214 usleep(100000);
215
216 /* get status */
217 pthread_mutex_lock(&player->app->players.lock);
218 st_curr.size = sizeof(OmPlrStatus);
219 r = OmPlrGetPlayerStatus((OmPlrHandle)player->handle, &st_curr);
220 pthread_mutex_unlock(&player->app->players.lock);
221
222 if(r)
223 fprintf(stderr, "ERROR: OmPlrGetPlayerStatus failed with 0x%.8X\n", r);
224 else
225 if(memcmp(&st_curr, &st_prev, sizeof(OmPlrStatus)))
226 omnplay_update_status(player, &st_prev , &st_curr);
227 };
228
229 pthread_mutex_lock(&player->app->players.lock);
230 OmPlrClose((OmPlrHandle)player->handle);
231 pthread_mutex_unlock(&player->app->players.lock);
232
233 return NULL;
234 };
235
236 void get_selected_items_playlist_proc(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
237 {
238 int idx, *list = (int*)data;
239 gtk_tree_model_get(model, iter, 7, &idx, -1);
240 list[list[0] + 1] = idx;
241 list[0] = list[0] + 1;
242 };
243
244 static int* get_selected_items_playlist(omnplay_instance_t* app)
245 {
246 int* list = NULL;
247 GtkTreeSelection *selection;
248
249 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(app->playlist_grid));
250 if(selection)
251 {
252 list = (int*)malloc(sizeof(int) * (MAX_PLAYLIST_ITEMS + 1));
253 memset(list, 0, sizeof(int) * (MAX_PLAYLIST_ITEMS + 1));
254
255 gtk_tree_selection_selected_foreach(
256 selection,
257 get_selected_items_playlist_proc,
258 list);
259
260 if(!list[0])
261 {
262 free(list);
263 list = NULL;
264 };
265 };
266
267 return list;
268 };
269
270 static int idx_in_players_range(omnplay_instance_t* app, int idx)
271 {
272 int i, r = 0;
273
274 for(i = 0; i < app->players.count && !r; i++)
275 {
276 int a, b;
277
278 a = app->players.item[i].playlist_start;
279 b = app->players.item[i].playlist_length;
280
281 if(b <= 0)
282 continue;
283
284 b = a + b - 1;
285
286 if(idx >= a && idx <= b) r = 1;
287 };
288
289 return r;
290 };
291
292 static int idxs_in_players_range(omnplay_instance_t* app, int start, int stop)
293 {
294 int i, r = 0;
295
296 for(i = 0; i < app->players.count && !r; 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 #define IN_RANGE(A,B,C) (A <= C && C <= B)
309 if( IN_RANGE(a,b,start) ||
310 IN_RANGE(a,b,stop) ||
311 IN_RANGE(start,stop,a) ||
312 IN_RANGE(start,stop,b))
313 r = 1;
314 };
315
316 return r;
317 };
318
319 static void omnplay_playlist_block(omnplay_instance_t* app, control_buttons_t button)
320 {
321 int start, stop, r, i;
322 int* list = get_selected_items_playlist(app);
323
324 if(!list)
325 return;
326
327 pthread_mutex_lock(&app->playlist.lock);
328 pthread_mutex_lock(&app->players.lock);
329
330 start = list[1];
331 stop = list[list[0]];
332
333 if(!idxs_in_players_range(app, start, stop))
334 {
335 int loop = (button == BUTTON_PLAYLIST_BLOCK_LOOP)?OMNPLAY_PLAYLIST_BLOCK_LOOP:0;
336
337 /* update selected item */
338 for(i = start; i <= stop; i++)
339 {
340 int t = OMNPLAY_PLAYLIST_BLOCK_BODY | loop;
341
342 if(i == start) t |= OMNPLAY_PLAYLIST_BLOCK_BEGIN;
343 if(i == stop) t |= OMNPLAY_PLAYLIST_BLOCK_END;
344
345 app->playlist.item[i].type = (playlist_item_type_t)t;
346
347 omnplay_playlist_draw_item(app, i);
348 };
349
350 /* update border items */
351 if(!start && !(app->playlist.item[start - 1].type & OMNPLAY_PLAYLIST_BLOCK_END))
352 {
353 app->playlist.item[start - 1].type = (playlist_item_type_t)(OMNPLAY_PLAYLIST_BLOCK_END
354 | app->playlist.item[start - 1].type);
355 omnplay_playlist_draw_item(app, start - 1);
356 };
357 if((stop + 1) < app->playlist.count && !(app->playlist.item[stop + 1].type & OMNPLAY_PLAYLIST_BLOCK_BEGIN))
358 {
359 app->playlist.item[stop + 1].type = (playlist_item_type_t)(OMNPLAY_PLAYLIST_BLOCK_BEGIN
360 | app->playlist.item[stop + 1].type);
361 omnplay_playlist_draw_item(app, stop + 1);
362 };
363 }
364 else
365 fprintf(stderr, "omnplay_playlist_block: range [%d %d] do OVERLAP player\n",
366 start, stop);
367
368 pthread_mutex_unlock(&app->players.lock);
369 pthread_mutex_unlock(&app->playlist.lock);
370
371 free(list);
372 };
373
374 static int get_first_selected_item_playlist(omnplay_instance_t* app)
375 {
376 int idx;
377 int* list = get_selected_items_playlist(app);
378 if(!list) return -1;
379 idx = list[1];
380 free(list);
381 return idx;
382 };
383
384 static int get_playlist_block(omnplay_instance_t* app, int idx, int* start_ptr, int* stop_ptr)
385 {
386 int start, stop;
387
388 for(start = idx; start >= 0; start--)
389 if(app->playlist.item[start].type & OMNPLAY_PLAYLIST_BLOCK_BEGIN)
390 break;
391
392 for(stop = idx; stop < app->playlist.count; stop++)
393 if(app->playlist.item[stop].type & OMNPLAY_PLAYLIST_BLOCK_END)
394 break;
395
396 fprintf(stderr, "get_playlist_block: range %d -> %d\n", start, stop);
397
398 /* check block range */
399 if(start >= 0 && stop < app->playlist.count)
400 {
401 *start_ptr = start;
402 *stop_ptr = stop;
403 return (stop - start + 1);
404 };
405
406 return -1;
407 };
408
409 static omnplay_player_t *get_player_at_pos(omnplay_instance_t* app, int pos)
410 {
411 /* check player range */
412 if(app->playlist.item[pos].player > -1 && app->playlist.item[pos].player < app->players.count)
413 return &app->players.item[app->playlist.item[pos].player];
414
415 return NULL;
416 };
417
418 static void omnplay_playlist_delete_items(omnplay_instance_t* app, int* idxs, int count)
419 {
420 int i, j, idx;
421 GtkTreePath* path;
422
423 pthread_mutex_lock(&app->playlist.lock);
424 pthread_mutex_lock(&app->players.lock);
425
426 for(j = 0; j < count; j++)
427 {
428 idx = idxs[j] - j;
429
430 /* fix block types */
431 if(idx)
432 app->playlist.item[idx - 1].type = (playlist_item_type_t)(app->playlist.item[idx - 1].type |
433 OMNPLAY_PLAYLIST_BLOCK_END);
434 if(idx + 1 < app->playlist.count)
435 app->playlist.item[idx + 1].type = (playlist_item_type_t)(app->playlist.item[idx + 1].type |
436 OMNPLAY_PLAYLIST_BLOCK_BEGIN);
437
438 /* shift playlist items */
439 memmove
440 (
441 &app->playlist.item[idx],
442 &app->playlist.item[idx + 1],
443 (app->playlist.count - idx - 1) * sizeof(playlist_item_t)
444 );
445
446 /* decrement items count */
447 app->playlist.count--;
448
449 /* increment servers indexes */
450 for(i = 0; i < app->players.count; i++)
451 if(app->players.item[i].playlist_start >= idx)
452 app->players.item[i].playlist_start--;
453
454
455 };
456
457 /* redraw playlist */
458 omnplay_playlist_draw(app);
459
460 /* select */
461 path = gtk_tree_path_new_from_indices(idxs[0], -1);
462 gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(app->playlist_grid)), path);
463 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app->playlist_grid), path, NULL, FALSE);
464 gtk_tree_path_free(path);
465
466
467 pthread_mutex_unlock(&app->players.lock);
468 pthread_mutex_unlock(&app->playlist.lock);
469 };
470
471 static void omnplay_playlist_item_del(omnplay_instance_t* app)
472 {
473 int i, idx, c;
474 int *list, *list2;
475
476 list = get_selected_items_playlist(app);
477 if(!list) return;
478
479 list2 = (int*)malloc(sizeof(int) * list[0]);
480
481 for(i = 0, c = 0; i < list[0]; i++)
482 {
483 /* check for playing block */
484 if(idx_in_players_range(app, list[i + 1]))
485 continue;
486
487 /* save index */
488 list2[c++] = list[i + 1];
489 };
490
491 if(c)
492 omnplay_playlist_delete_items(app, list2, c);
493
494 free(list2);
495 free(list);
496 };
497
498 static int omnplay_playlist_insert_check(omnplay_instance_t* app, int idx, playlist_item_type_t* t)
499 {
500 *t = OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE;
501
502 /* before or after playlist */
503 if(!idx || idx == app->playlist.count)
504 return 1;
505
506 /* check for block borders */
507 if( app->playlist.item[idx - 1].type & OMNPLAY_PLAYLIST_BLOCK_END &&
508 app->playlist.item[idx + 0].type & OMNPLAY_PLAYLIST_BLOCK_BEGIN)
509 return 1;
510
511 /* check for playing block */
512 if(idx_in_players_range(app, idx))
513 return 0;
514
515 if(app->playlist.item[idx].type & OMNPLAY_PLAYLIST_BLOCK_LOOP)
516 *t = OMNPLAY_PLAYLIST_ITEM_LOOP_BODY;
517 else
518 *t = OMNPLAY_PLAYLIST_ITEM_BLOCK_BODY;
519
520 return 1;
521 };
522
523 static void omnplay_playlist_insert_items(omnplay_instance_t* app, int idx,
524 playlist_item_t* items, int count)
525 {
526 int i;
527 GtkTreePath* path;
528
529 pthread_mutex_lock(&app->playlist.lock);
530 pthread_mutex_lock(&app->players.lock);
531
532 /* shift playlist items */
533 memmove
534 (
535 &app->playlist.item[idx + count],
536 &app->playlist.item[idx],
537 (app->playlist.count - idx) * sizeof(playlist_item_t)
538 );
539
540 /* copy new items */
541 memcpy
542 (
543 &app->playlist.item[idx],
544 items,
545 count * sizeof(playlist_item_t)
546 );
547
548 /* increment servers indexes */
549 for(i = 0; i < app->players.count; i++)
550 if(app->players.item[i].playlist_start >= idx)
551 app->players.item[i].playlist_start += idx;
552
553 /* increment items count */
554 app->playlist.count += count;
555
556 /* redraw playlist */
557 omnplay_playlist_draw(app);
558
559 /* select */
560 path = gtk_tree_path_new_from_indices(idx + count, -1);
561 gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(app->playlist_grid)), path);
562 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app->playlist_grid), path, NULL, FALSE);
563 gtk_tree_path_free(path);
564
565 pthread_mutex_unlock(&app->players.lock);
566 pthread_mutex_unlock(&app->playlist.lock);
567 };
568
569 static void omnplay_playlist_item_add(omnplay_instance_t* app, int after)
570 {
571 int idx;
572 playlist_item_t item;
573 playlist_item_type_t t;
574
575 /* find insert position */
576 idx = get_first_selected_item_playlist(app);
577 if(idx < 0)
578 idx = 0;
579 else
580 idx += (after)?1:0;
581
582 if(!omnplay_playlist_insert_check(app, idx, &t))
583 return;
584
585 fprintf(stderr, "allowed insert into idx=%d\n", idx);
586
587 /* clear item */
588 memset(&item, 0, sizeof(playlist_item_t));
589 if(ui_playlist_item_dialog(app, &item))
590 {
591 item.type = t;
592 omnplay_playlist_insert_items(app, idx, &item, 1);
593 };
594 };
595
596 static void omnplay_playlist_item_edit(omnplay_instance_t* app)
597 {
598 int idx;
599 playlist_item_t item;
600
601 /* find insert position */
602 idx = get_first_selected_item_playlist(app);
603
604 if(idx < 0)
605 return;
606
607 /* check for playing block */
608 if(idx_in_players_range(app, idx))
609 return;
610
611 item = app->playlist.item[idx];
612
613 if(ui_playlist_item_dialog(app, &item))
614 {
615 app->playlist.item[idx] = item;
616 omnplay_playlist_draw_item(app, idx);
617 };
618 };
619
620 static void omnplay_ctl(omnplay_instance_t* app, control_buttons_t button)
621 {
622 int i, r;
623 int idx, start, stop;
624 omnplay_player_t *player;
625
626 pthread_mutex_lock(&app->playlist.lock);
627
628 idx = get_first_selected_item_playlist(app);
629
630 if(idx < 0)
631 {
632 pthread_mutex_unlock(&app->playlist.lock);
633 return;
634 };
635
636 fprintf(stderr, "cue: selected item is %d\n", idx);
637
638 if(get_playlist_block(app, idx, &start, &stop) < 0)
639 {
640 pthread_mutex_unlock(&app->playlist.lock);
641 return;
642 };
643
644 fprintf(stderr, "cue: range %d -> %d\n", start, stop);
645
646 player = get_player_at_pos(app, start);
647
648 if(!player)
649 {
650 pthread_mutex_unlock(&app->playlist.lock);
651 return;
652 };
653
654 pthread_mutex_lock(&app->players.lock);
655
656 if(BUTTON_PLAYER_STOP == button || BUTTON_PLAYER_CUE == button)
657 {
658 /* stop */
659 OmPlrStop((OmPlrHandle)player->handle);
660
661 /* detach previous clips */
662 player->playlist_length = -1;
663 OmPlrDetachAllClips((OmPlrHandle)player->handle);
664 };
665
666 if(BUTTON_PLAYER_CUE == button)
667 {
668 int o, c, p = 0;
669
670 /* Attach clips to timeline */
671 for(i = start, c = 0, o = 0; i <= stop; i++)
672 {
673 OmPlrClipInfo clip;
674
675 /* get clip info */
676 clip.maxMsTracks = 0;
677 clip.size = sizeof(clip);
678 r = OmPlrClipGetInfo((OmPlrHandle)player->handle, app->playlist.item[i].id, &clip);
679
680 if(!r)
681 {
682 unsigned int l;
683
684 fprintf(stderr, "OmPlrClipGetInfo(%s): firstFrame=%d, lastFrame=%d\n",
685 app->playlist.item[i].id, clip.firstFrame, clip.lastFrame);
686
687 /* should we fix playlist clip timings */
688 if(!(
689 app->playlist.item[i].in >= clip.firstFrame &&
690 app->playlist.item[i].in + app->playlist.item[i].dur <= clip.lastFrame) ||
691 !app->playlist.item[i].dur)
692 {
693 fprintf(stderr, "cue: item [%s] will be updated [%d;%d]->[%d;%d]\n",
694 app->playlist.item[i].id,
695 app->playlist.item[i].in, app->playlist.item[i].dur,
696 clip.firstFrame, clip.lastFrame - clip.firstFrame);
697
698 app->playlist.item[i].in = clip.firstFrame;
699 app->playlist.item[i].dur = clip.lastFrame - clip.firstFrame;
700 omnplay_playlist_draw_item(app, i);
701 };
702
703 r = OmPlrAttach((OmPlrHandle)player->handle,
704 app->playlist.item[i].id,
705 app->playlist.item[i].in,
706 app->playlist.item[i].in + app->playlist.item[i].dur,
707 0, omPlrShiftModeAfter, &l);
708 };
709
710 if(r)
711 {
712 fprintf(stderr, "cue: failed with %d, %s\n", r, OmPlrGetErrorString((OmPlrError)r));
713 app->playlist.item[i].omn_idx = -1;
714 app->playlist.item[i].omn_offset = -1;
715 }
716 else
717 {
718 app->playlist.item[i].omn_idx = c;
719 app->playlist.item[i].omn_offset = o;
720
721 /* save selected item offset */
722 if(i == idx) p = o;
723
724 c++;
725 o += app->playlist.item[i].dur;
726 };
727 };
728
729 if(c)
730 {
731 OmPlrStatus hs;
732
733 /* Set timeline min/max */
734 OmPlrSetMinPosMin((OmPlrHandle)player->handle);
735 OmPlrSetMaxPosMax((OmPlrHandle)player->handle);
736
737 /* Set timeline position */
738 hs.minPos = 0;
739 hs.size = sizeof(OmPlrStatus);
740 OmPlrGetPlayerStatus((OmPlrHandle)player->handle, &hs);
741 OmPlrSetPos((OmPlrHandle)player->handle, hs.minPos + p);
742
743 /* setup loop */
744 if(app->playlist.item[start].type & OMNPLAY_PLAYLIST_BLOCK_LOOP)
745 OmPlrLoop((OmPlrHandle)player->handle, hs.minPos, hs.maxPos);
746 else
747 OmPlrLoop((OmPlrHandle)player->handle, hs.minPos, hs.minPos);
748
749 player->playlist_start = start;
750 player->playlist_length = stop - start + 1;
751
752 /* Cue */
753 OmPlrCuePlay((OmPlrHandle)player->handle, 0.0);
754 };
755 };
756
757 if(BUTTON_PLAYER_PLAY == button)
758 {
759 /* play */
760 OmPlrPlay((OmPlrHandle)player->handle, 1.0);
761 };
762
763 if(BUTTON_PLAYER_PAUSE == button)
764 /* pause */
765 OmPlrPlay((OmPlrHandle)player->handle, 0.0);
766
767 pthread_mutex_unlock(&app->players.lock);
768
769 pthread_mutex_unlock(&app->playlist.lock);
770 };
771
772 static void omnplay_playlist_item_swap(omnplay_instance_t* app, int dir)
773 {
774 int sel, a, b;
775 GtkTreePath* path;
776 playlist_item_t item;
777
778 /* find insert position */
779 sel = get_first_selected_item_playlist(app);
780 if(sel < 0)
781 return;
782
783 if(dir < 0)
784 {
785 a = sel - 1;
786 b = sel;
787 sel = a;
788 }
789 else
790 {
791 a = sel;
792 b = sel + 1;
793 sel = b;
794 };
795
796 /* check for playing block */
797 if(idx_in_players_range(app, a) || idx_in_players_range(app, b))
798 return;
799
800 pthread_mutex_lock(&app->playlist.lock);
801 pthread_mutex_lock(&app->players.lock);
802
803 /* swap */
804 item = app->playlist.item[a];
805 app->playlist.item[a] = app->playlist.item[b];
806 app->playlist.item[b] = item;
807
808 /* rewite type */
809 app->playlist.item[a].type = OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE;
810 app->playlist.item[b].type = OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE;
811
812 /* redraw main items */
813 omnplay_playlist_draw_item(app, a);
814 omnplay_playlist_draw_item(app, b);
815
816 /* fix block types */
817 if(a)
818 {
819 app->playlist.item[a - 1].type = (playlist_item_type_t)(app->playlist.item[a - 1].type |
820 OMNPLAY_PLAYLIST_BLOCK_END);
821 omnplay_playlist_draw_item(app, a - 1);
822 };
823 if(b + 1 < app->playlist.count)
824 {
825 app->playlist.item[b + 1].type = (playlist_item_type_t)(app->playlist.item[b + 1].type |
826 OMNPLAY_PLAYLIST_BLOCK_BEGIN);
827 omnplay_playlist_draw_item(app, b + 1);
828 };
829
830 /* select */
831 path = gtk_tree_path_new_from_indices(sel, -1);
832 gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(app->playlist_grid)), path);
833 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app->playlist_grid), path, NULL, FALSE);
834 gtk_tree_path_free(path);
835
836 pthread_mutex_unlock(&app->players.lock);
837 pthread_mutex_unlock(&app->playlist.lock);
838 };
839
840 static gboolean omnplay_button_click(omnplay_instance_t* app, control_buttons_t button)
841 {
842 switch(button)
843 {
844 case BUTTON_PLAYLIST_ITEM_ADD:
845 omnplay_playlist_item_add(app, 0);
846 break;
847 case BUTTON_PLAYLIST_ITEM_DEL:
848 omnplay_playlist_item_del(app);
849 break;
850 case BUTTON_PLAYLIST_ITEM_EDIT:
851 omnplay_playlist_item_edit(app);
852 break;
853 case BUTTON_PLAYLIST_LOAD:
854 omnplay_playlist_load(app);
855 break;
856 case BUTTON_PLAYLIST_SAVE:
857 omnplay_playlist_save(app);
858 break;
859 case BUTTON_PLAYLIST_BLOCK_SINGLE:
860 case BUTTON_PLAYLIST_BLOCK_LOOP:
861 omnplay_playlist_block(app, button);
862 break;
863 case BUTTON_PLAYLIST_ITEM_UP:
864 omnplay_playlist_item_swap(app, -1);
865 break;
866 case BUTTON_PLAYLIST_ITEM_DOWN:
867 omnplay_playlist_item_swap(app, +1);
868 break;
869 case BUTTON_PLAYER_CUE:
870 case BUTTON_PLAYER_PLAY:
871 case BUTTON_PLAYER_PAUSE:
872 case BUTTON_PLAYER_STOP:
873 omnplay_ctl(app, button);
874 break;
875 case BUTTON_LIBRARY_ADD:
876 case BUTTON_LIBRARY_REFRESH:
877 break;
878 };
879
880 return TRUE;
881 };
882
883 static gboolean on_button_click(GtkWidget *button, gpointer user_data)
884 {
885 int i;
886 omnplay_instance_t* app = (omnplay_instance_t*)user_data;
887
888 for(i = 1; i < BUTTON_LAST; i++)
889 if(app->buttons[i] == button)
890 return omnplay_button_click(app, (control_buttons_t)i);
891
892 return FALSE;
893 };
894
895 static gboolean on_playlist_grid_key(GtkWidget *widget, GdkEventKey *event, gpointer data)
896 {
897 omnplay_instance_t* app = (omnplay_instance_t*)data;
898
899 switch(event->keyval)
900 {
901 case GDK_C:
902 case GDK_c:
903 if(event->state & GDK_CONTROL_MASK)
904 {
905 fprintf(stderr, "CTRL+c\n");
906 return TRUE;
907 };
908 break;
909 case GDK_V:
910 case GDK_v:
911 if(event->state & GDK_CONTROL_MASK)
912 {
913 fprintf(stderr, "CTRL+v\n");
914 return TRUE;
915 };
916 break;
917 case GDK_X:
918 case GDK_x:
919 if(event->state & GDK_CONTROL_MASK)
920 {
921 fprintf(stderr, "CTRL+x\n");
922 return TRUE;
923 };
924 break;
925 case GDK_KEY_space:
926 omnplay_ctl(app, BUTTON_PLAYER_PLAY);
927 return TRUE;
928 case GDK_KEY_Return:
929 omnplay_ctl(app, BUTTON_PLAYER_CUE);
930 return TRUE;
931 case GDK_KEY_Insert:
932 omnplay_playlist_item_add(app, 0);
933 return TRUE;
934 case GDK_KEY_Delete:
935 omnplay_playlist_item_del(app);
936 return TRUE;
937 case GDK_KEY_BackSpace:
938 omnplay_playlist_item_edit(app);
939 return TRUE;
940 };
941
942 return FALSE;
943 };
944
945 void omnplay_init(omnplay_instance_t* app)
946 {
947 int i;
948 pthread_mutexattr_t attr;
949
950 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
951
952 gtk_signal_connect( GTK_OBJECT( app->window ), "destroy",
953 GTK_SIGNAL_FUNC(on_main_window_delete_event), app);
954
955 gtk_widget_add_events(app->playlist_grid, GDK_BUTTON_PRESS_MASK);
956 gtk_signal_connect(GTK_OBJECT(app->playlist_grid), "key-press-event",
957 GTK_SIGNAL_FUNC(on_playlist_grid_key), app);
958
959 /* create lock */
960 pthread_mutex_init(&app->players.lock, &attr);
961
962 /* create a omneon status thread */
963 for(i = 0; i < app->players.count; i++)
964 pthread_create(&app->players.item[i].thread, NULL,
965 omnplay_thread_proc, &app->players.item[i]);
966
967 /* create lock */
968 pthread_mutex_init(&app->playlist.lock, &attr);
969
970 /* attach buttons click */
971 for(i = 1; i < BUTTON_LAST; i++)
972 gtk_signal_connect(GTK_OBJECT(app->buttons[i]), "clicked",
973 GTK_SIGNAL_FUNC( on_button_click), app );
974
975 };
976
977 void omnplay_release(omnplay_instance_t* app)
978 {
979 int i;
980 void* r;
981
982 app->f_exit = 1;
983
984 for(i = 0; i < app->players.count; i++)
985 /* create a omneon status thread */
986 pthread_join(app->players.item[i].thread, &r);
987
988 /* destroy lock */
989 pthread_mutex_destroy(&app->players.lock);
990
991 /* destroy lock */
992 pthread_mutex_destroy(&app->playlist.lock);
993 };