8eeffec40db1de6158e024bc60acc913ae41e3aa
[omnplay] / 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 int omnplay_get_content(omnplay_instance_t* app, playlist_item_t *items, int limit,
43 omnplay_get_content_cb_proc proc, void* data)
44 {
45 int r, c = 0;
46 OmPlrClipInfo clip_info;
47 char clip_name[omPlrMaxClipDirLen];
48
49 app->library.id_display_idx = 0;
50
51 pthread_mutex_lock(&app->players.lock);
52
53 r = OmPlrClipGetFirst((OmPlrHandle)app->players.item[0].handle, clip_name, sizeof(clip_name));
54 for(; c < limit && !r;)
55 {
56 /* get clip info */
57 clip_info.maxMsTracks = 0;
58 clip_info.size = sizeof(clip_info);
59
60 r = OmPlrClipGetInfo((OmPlrHandle)app->players.item[0].handle, clip_name, &clip_info);
61
62 if(!r)
63 {
64 /* copy item props */
65 strncpy(items[c].id, clip_name, PATH_MAX);
66 items[c].in = clip_info.firstFrame;
67 items[c].dur = clip_info.lastFrame - clip_info.firstFrame;
68
69 /* callback */
70 pthread_mutex_unlock(&app->players.lock);
71 if(proc)
72 proc(app, &items[c], data);
73 pthread_mutex_lock(&app->players.lock);
74
75 c++;
76 };
77
78 r = OmPlrClipGetNext((OmPlrHandle)app->players.item[0].handle, clip_name, sizeof(clip_name));
79 };
80
81 pthread_mutex_unlock(&app->players.lock);
82
83 return c;
84 };
85
86
87 static gboolean on_main_window_delete_event( GtkWidget *widget, GdkEvent *event, gpointer user_data )
88 {
89 g_print ("delete event occurred [start]\n");
90 gdk_threads_leave();
91 omnplay_release((omnplay_instance_t*)user_data);
92 gdk_threads_enter();
93 g_print ("delete event occurred [finish]\n");
94
95 return FALSE;
96 }
97
98 static void on_main_window_destroy( GtkWidget *widget, gpointer user_data )
99 {
100 g_print ("destroy occurred\n");
101 gtk_main_quit();
102 }
103
104 omnplay_instance_t* omnplay_create(int argc, char** argv)
105 {
106 int i, c;
107 omnplay_instance_t* app;
108
109 /* prepare application instance */
110 app = (omnplay_instance_t*)malloc(sizeof(omnplay_instance_t));
111 memset(app, 0, sizeof(omnplay_instance_t));
112
113 /* load parameters from command line */
114 if(!omnplay_opt(argc, argv, app) && app->players.count)
115 app->window = ui_omnplay(app);
116 else
117 omnplay_usage();
118
119 return app;
120 };
121
122 void omnplay_destroy(omnplay_instance_t* app)
123 {
124 free(app);
125 };
126
127 static int find_index_of_playlist_item(omnplay_instance_t* app, int start, int idx)
128 {
129 if(start < 0 || start >= app->playlist.count)
130 return -1;
131
132 while(1)
133 {
134 if(app->playlist.item[start].omn_idx == idx)
135 return start;
136
137 if(app->playlist.item[start].type & OMNPLAY_PLAYLIST_BLOCK_END)
138 break;
139
140 start++;
141 };
142
143 return -1;
144 };
145
146 static void omnplay_update_status(omnplay_player_t* player, OmPlrStatus *prev , OmPlrStatus *curr)
147 {
148 int idx;
149 char tc_cur[32], tc_rem[32], state[32], status[32];
150 const char *clip;
151
152 if(curr)
153 {
154 frames2tc(curr->pos - curr->minPos, 25.0, tc_cur);
155 frames2tc(curr->maxPos - curr->pos, 25.0, tc_rem);
156 strcpy(status, "ONLINE");
157 clip = curr->currClipName;
158
159 switch(curr->state)
160 {
161 case omPlrStateStopped: strcpy(state, "STOPPED"); break;
162 case omPlrStateCuePlay: strcpy(state, "CUE_PLAY"); break;
163 case omPlrStatePlay: strcpy(state, "PLAY"); break;
164 case omPlrStateCueRecord: strcpy(state, "CUE_RECORD"); break;
165 case omPlrStateRecord: strcpy(state, "RECORD"); break;
166 };
167 }
168 else
169 {
170 tc_cur[0] = 0;
171 tc_rem[0] = 0;
172 clip = "";
173 state[0] = 0;
174 strcpy(status, "OFFLINE");
175 };
176
177 /* update status in status page */
178 gdk_threads_enter();
179 gtk_label_set_text(GTK_LABEL (player->label_tc_cur), tc_cur);
180 gtk_label_set_text(GTK_LABEL (player->label_tc_rem), tc_rem);
181 gtk_label_set_text(GTK_LABEL (player->label_state), state);
182 gtk_label_set_text(GTK_LABEL (player->label_status), status);
183 gtk_label_set_text(GTK_LABEL (player->label_clip), clip);
184 gdk_flush();
185 gdk_threads_leave();
186
187 /* update remaining time */
188 gdk_threads_enter();
189 pthread_mutex_lock(&player->app->playlist.lock);
190 pthread_mutex_lock(&player->app->players.lock);
191 if(curr->state == omPlrStatePlay || curr->state == omPlrStateCuePlay)
192 {
193 idx = find_index_of_playlist_item(player->app, player->playlist_start, curr->currClipNum);
194 if(idx >= 0)
195 {
196 frames2tc(curr->currClipStartPos + curr->currClipLen - curr->pos, 25.0, tc_rem);
197 omnplay_playlist_draw_item_rem(player->app, idx, tc_rem);
198 }
199 if(curr->currClipNum != prev->currClipNum && 1 != prev->numClips)
200 {
201 tc_rem[0] = 0;
202 idx = find_index_of_playlist_item(player->app, player->playlist_start, prev->currClipNum);
203 if(idx >= 0)
204 omnplay_playlist_draw_item_rem(player->app, idx, tc_rem);
205 };
206 }
207 else
208 {
209 tc_rem[0] = 0;
210 idx = find_index_of_playlist_item(player->app, player->playlist_start, curr->currClipNum);
211 if(idx >= 0)
212 omnplay_playlist_draw_item_rem(player->app, idx, tc_rem);
213 idx = find_index_of_playlist_item(player->app, player->playlist_start, prev->currClipNum);
214 if(idx >= 0)
215 omnplay_playlist_draw_item_rem(player->app, idx, tc_rem);
216 };
217 pthread_mutex_unlock(&player->app->players.lock);
218 pthread_mutex_unlock(&player->app->playlist.lock);
219 gdk_flush();
220 gdk_threads_leave();
221
222
223 memcpy(prev, curr, sizeof(OmPlrStatus));
224 };
225
226 static void* omnplay_thread_proc(void* data)
227 {
228 int r;
229 OmPlrStatus st_curr, st_prev;
230 omnplay_player_t* player = (omnplay_player_t*)data;
231
232 g_warning("omnplay_thread_proc\n");
233
234 memset(&st_curr, 0, sizeof(OmPlrStatus));
235 memset(&st_prev, 0, sizeof(OmPlrStatus));
236
237 /* connect */
238 pthread_mutex_lock(&player->app->players.lock);
239 r = OmPlrOpen(player->host, player->name, (OmPlrHandle*)&player->handle);
240 pthread_mutex_unlock(&player->app->players.lock);
241 if(r)
242 {
243 g_warning("ERROR: OmPlrOpen(%s, %s) failed with 0x%.8X\n",
244 player->host, player->name, r);
245
246 return (void*)r;
247 };
248
249 /* setup to do not reconnect */
250 pthread_mutex_lock(&player->app->players.lock);
251 OmPlrSetRetryOpen((OmPlrHandle)player->handle, 0);
252 pthread_mutex_unlock(&player->app->players.lock);
253
254 /* setup directory */
255 if(player->app->players.path[0])
256 {
257 pthread_mutex_lock(&player->app->players.lock);
258 r = OmPlrClipSetDirectory((OmPlrHandle)player->handle, player->app->players.path);
259 pthread_mutex_unlock(&player->app->players.lock);
260
261 if(r)
262 {
263 g_warning("ERROR: OmPlrClipSetDirectory(%s) failed with 0x%.8X\n",
264 player->app->players.path, r);
265
266 pthread_mutex_lock(&player->app->players.lock);
267 OmPlrClose((OmPlrHandle)player->handle);
268 pthread_mutex_unlock(&player->app->players.lock);
269
270 return (void*)r;
271 };
272 };
273
274 /* endless loop */
275 for(r = 0 ; !player->app->f_exit && !r;)
276 {
277 /* sleep */
278 #ifdef _WIN32
279 Sleep(100);
280 #else
281 usleep(100000);
282 #endif
283
284 /* get status */
285 pthread_mutex_lock(&player->app->players.lock);
286 st_curr.size = sizeof(OmPlrStatus);
287 r = OmPlrGetPlayerStatus((OmPlrHandle)player->handle, &st_curr);
288 pthread_mutex_unlock(&player->app->players.lock);
289
290 if(r)
291 g_warning("ERROR: OmPlrGetPlayerStatus failed with 0x%.8X\n", r);
292 else
293 if(memcmp(&st_curr, &st_prev, sizeof(OmPlrStatus)))
294 omnplay_update_status(player, &st_prev , &st_curr);
295 };
296
297 pthread_mutex_lock(&player->app->players.lock);
298 OmPlrClose((OmPlrHandle)player->handle);
299 pthread_mutex_unlock(&player->app->players.lock);
300
301 return NULL;
302 };
303
304 void get_selected_items_playlist_proc(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
305 {
306 int idx, *list = (int*)data;
307 gtk_tree_model_get(model, iter, 7, &idx, -1);
308 list[list[0] + 1] = idx;
309 list[0] = list[0] + 1;
310 };
311
312 int* omnplay_selected_idxs_playlist(omnplay_instance_t* app)
313 {
314 int* list = NULL;
315 GtkTreeSelection *selection;
316
317 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(app->playlist_grid));
318 if(selection)
319 {
320 list = (int*)malloc(sizeof(int) * (MAX_PLAYLIST_ITEMS + 1));
321 memset(list, 0, sizeof(int) * (MAX_PLAYLIST_ITEMS + 1));
322
323 gtk_tree_selection_selected_foreach(
324 selection,
325 get_selected_items_playlist_proc,
326 list);
327
328 if(!list[0])
329 {
330 free(list);
331 list = NULL;
332 };
333 };
334
335 return list;
336 };
337
338 static int idx_in_players_range(omnplay_instance_t* app, int idx)
339 {
340 int i, r = 0;
341
342 for(i = 0; i < app->players.count && !r; i++)
343 {
344 int a, b;
345
346 a = app->players.item[i].playlist_start;
347 b = app->players.item[i].playlist_length;
348
349 if(b <= 0)
350 continue;
351
352 b = a + b - 1;
353
354 if(idx >= a && idx <= b) r = 1;
355 };
356
357 return r;
358 };
359
360 static int idxs_in_players_range(omnplay_instance_t* app, int start, int stop)
361 {
362 int i, r = 0;
363
364 for(i = 0; i < app->players.count && !r; i++)
365 {
366 int a, b;
367
368 a = app->players.item[i].playlist_start;
369 b = app->players.item[i].playlist_length;
370
371 if(b <= 0)
372 continue;
373
374 b = a + b - 1;
375
376 #define IN_RANGE(A,B,C) (A <= C && C <= B)
377 if( IN_RANGE(a,b,start) ||
378 IN_RANGE(a,b,stop) ||
379 IN_RANGE(start,stop,a) ||
380 IN_RANGE(start,stop,b))
381 r = 1;
382 };
383
384 return r;
385 };
386
387 static void omnplay_playlist_block(omnplay_instance_t* app, control_buttons_t button)
388 {
389 int start, stop, r, i;
390 int* list = omnplay_selected_idxs_playlist(app);
391
392 if(!list)
393 return;
394
395 pthread_mutex_lock(&app->playlist.lock);
396 pthread_mutex_lock(&app->players.lock);
397
398 start = list[1];
399 stop = list[list[0]];
400
401 if(!idxs_in_players_range(app, start, stop))
402 {
403 int loop = (button == BUTTON_PLAYLIST_BLOCK_LOOP)?OMNPLAY_PLAYLIST_BLOCK_LOOP:0;
404
405 /* update selected item */
406 for(i = start; i <= stop; i++)
407 {
408 int t = OMNPLAY_PLAYLIST_BLOCK_BODY | loop;
409
410 if(i == start) t |= OMNPLAY_PLAYLIST_BLOCK_BEGIN;
411 if(i == stop) t |= OMNPLAY_PLAYLIST_BLOCK_END;
412
413 app->playlist.item[i].type = (playlist_item_type_t)t;
414
415 omnplay_playlist_draw_item(app, i);
416 };
417
418 /* update border items */
419 if(start && !(app->playlist.item[start - 1].type & OMNPLAY_PLAYLIST_BLOCK_END))
420 {
421 app->playlist.item[start - 1].type = (playlist_item_type_t)(OMNPLAY_PLAYLIST_BLOCK_END
422 | app->playlist.item[start - 1].type);
423 omnplay_playlist_draw_item(app, start - 1);
424 };
425 if((stop + 1) < app->playlist.count && !(app->playlist.item[stop + 1].type & OMNPLAY_PLAYLIST_BLOCK_BEGIN))
426 {
427 app->playlist.item[stop + 1].type = (playlist_item_type_t)(OMNPLAY_PLAYLIST_BLOCK_BEGIN
428 | app->playlist.item[stop + 1].type);
429 omnplay_playlist_draw_item(app, stop + 1);
430 };
431 }
432 else
433 g_warning("omnplay_playlist_block: range [%d %d] do OVERLAP player\n",
434 start, stop);
435
436 pthread_mutex_unlock(&app->players.lock);
437 pthread_mutex_unlock(&app->playlist.lock);
438
439 free(list);
440 };
441
442 static int get_first_selected_item_playlist(omnplay_instance_t* app)
443 {
444 int idx;
445 int* list = omnplay_selected_idxs_playlist(app);
446 if(!list) return -1;
447 idx = list[1];
448 free(list);
449 return idx;
450 };
451
452 static int get_playlist_block(omnplay_instance_t* app, int idx, int* start_ptr, int* stop_ptr)
453 {
454 int start, stop;
455
456 for(start = idx; start >= 0; start--)
457 if(app->playlist.item[start].type & OMNPLAY_PLAYLIST_BLOCK_BEGIN)
458 break;
459
460 for(stop = idx; stop < app->playlist.count; stop++)
461 if(app->playlist.item[stop].type & OMNPLAY_PLAYLIST_BLOCK_END)
462 break;
463
464 g_warning("get_playlist_block: range %d -> %d\n", start, stop);
465
466 /* check block range */
467 if(start >= 0 && stop < app->playlist.count)
468 {
469 *start_ptr = start;
470 *stop_ptr = stop;
471 return (stop - start + 1);
472 };
473
474 return -1;
475 };
476
477 static omnplay_player_t *get_player_at_pos(omnplay_instance_t* app, int pos)
478 {
479 /* check player range */
480 if(app->playlist.item[pos].player > -1 && app->playlist.item[pos].player < app->players.count)
481 return &app->players.item[app->playlist.item[pos].player];
482
483 return NULL;
484 };
485
486 static void omnplay_playlist_delete_items(omnplay_instance_t* app, int* idxs, int count, int sel)
487 {
488 int i, j, idx;
489
490 pthread_mutex_lock(&app->playlist.lock);
491 pthread_mutex_lock(&app->players.lock);
492
493 for(j = 0; j < count; j++)
494 {
495 idx = idxs[j] - j;
496
497 /* fix block types */
498 if( app->playlist.item[idx].type != OMNPLAY_PLAYLIST_ITEM_BLOCK_BODY &&
499 app->playlist.item[idx].type != OMNPLAY_PLAYLIST_ITEM_LOOP_BODY)
500 {
501 if(idx)
502 app->playlist.item[idx - 1].type = (playlist_item_type_t)(app->playlist.item[idx - 1].type |
503 OMNPLAY_PLAYLIST_BLOCK_END);
504 if(idx + 1 < app->playlist.count)
505 app->playlist.item[idx + 1].type = (playlist_item_type_t)(app->playlist.item[idx + 1].type |
506 OMNPLAY_PLAYLIST_BLOCK_BEGIN);
507 };
508
509 /* shift playlist items */
510 memmove
511 (
512 &app->playlist.item[idx],
513 &app->playlist.item[idx + 1],
514 (app->playlist.count - idx - 1) * sizeof(playlist_item_t)
515 );
516
517 /* decrement items count */
518 app->playlist.count--;
519
520 /* increment servers indexes */
521 for(i = 0; i < app->players.count; i++)
522 if(app->players.item[i].playlist_start >= idx)
523 app->players.item[i].playlist_start--;
524
525
526 };
527
528 /* redraw playlist */
529 omnplay_playlist_draw(app);
530
531 /* select */
532 if(sel)
533 {
534 GtkTreePath* path;
535 path = gtk_tree_path_new_from_indices(idxs[0], -1);
536 gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(app->playlist_grid)), path);
537 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app->playlist_grid), path, NULL, FALSE);
538 gtk_tree_path_free(path);
539 };
540
541 pthread_mutex_unlock(&app->players.lock);
542 pthread_mutex_unlock(&app->playlist.lock);
543 };
544
545 static void omnplay_playlist_item_del(omnplay_instance_t* app)
546 {
547 int i, idx, c;
548 int *list, *list2;
549
550 list = omnplay_selected_idxs_playlist(app);
551 if(!list) return;
552
553 list2 = (int*)malloc(sizeof(int) * list[0]);
554
555 for(i = 0, c = 0; i < list[0]; i++)
556 {
557 /* check for playing block */
558 if(idx_in_players_range(app, list[i + 1]))
559 continue;
560
561 /* save index */
562 list2[c++] = list[i + 1];
563 };
564
565 if(c)
566 omnplay_playlist_delete_items(app, list2, c, 1);
567
568 free(list2);
569 free(list);
570 };
571
572 static int omnplay_playlist_insert_check(omnplay_instance_t* app, int idx, playlist_item_type_t* t)
573 {
574 *t = OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE;
575
576 /* before or after playlist */
577 if(!idx || idx == app->playlist.count)
578 return 1;
579
580 /* check for block borders */
581 if( app->playlist.item[idx - 1].type & OMNPLAY_PLAYLIST_BLOCK_END &&
582 app->playlist.item[idx + 0].type & OMNPLAY_PLAYLIST_BLOCK_BEGIN)
583 return 1;
584
585 /* check for playing block */
586 if(idx_in_players_range(app, idx))
587 return 0;
588
589 if(app->playlist.item[idx].type & OMNPLAY_PLAYLIST_BLOCK_LOOP)
590 *t = OMNPLAY_PLAYLIST_ITEM_LOOP_BODY;
591 else
592 *t = OMNPLAY_PLAYLIST_ITEM_BLOCK_BODY;
593
594 return 1;
595 };
596
597 static void omnplay_playlist_insert_items(omnplay_instance_t* app, int idx,
598 playlist_item_t* items, int count)
599 {
600 int i;
601 GtkTreePath* path;
602
603 pthread_mutex_lock(&app->playlist.lock);
604 pthread_mutex_lock(&app->players.lock);
605
606 /* shift playlist items */
607 memmove
608 (
609 &app->playlist.item[idx + count],
610 &app->playlist.item[idx],
611 (app->playlist.count - idx) * sizeof(playlist_item_t)
612 );
613
614 /* copy new items */
615 memcpy
616 (
617 &app->playlist.item[idx],
618 items,
619 count * sizeof(playlist_item_t)
620 );
621
622 /* increment servers indexes */
623 for(i = 0; i < app->players.count; i++)
624 if(app->players.item[i].playlist_start >= idx)
625 app->players.item[i].playlist_start += idx;
626
627 /* increment items count */
628 app->playlist.count += count;
629
630 /* redraw playlist */
631 omnplay_playlist_draw(app);
632
633 /* select */
634 path = gtk_tree_path_new_from_indices(idx, -1);
635 gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(app->playlist_grid)), path);
636 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app->playlist_grid), path, NULL, FALSE);
637 gtk_tree_path_free(path);
638
639 pthread_mutex_unlock(&app->players.lock);
640 pthread_mutex_unlock(&app->playlist.lock);
641 };
642
643 static void omnplay_playlist_item_add(omnplay_instance_t* app, int after)
644 {
645 int idx;
646 playlist_item_t item;
647 playlist_item_type_t t;
648
649 /* find insert position */
650 idx = get_first_selected_item_playlist(app);
651 if(idx < 0)
652 idx = 0;
653 else
654 idx += (after)?1:0;
655
656 if(!omnplay_playlist_insert_check(app, idx, &t))
657 return;
658
659 g_warning("allowed insert into idx=%d\n", idx);
660
661 /* clear item */
662 memset(&item, 0, sizeof(playlist_item_t));
663 if(ui_playlist_item_dialog(app, &item))
664 {
665 omnplay_library_normalize_item(app, &item);
666 item.type = t;
667 omnplay_playlist_insert_items(app, idx, &item, 1);
668 };
669 };
670
671 static void omnplay_playlist_item_edit(omnplay_instance_t* app)
672 {
673 int idx;
674 playlist_item_t item;
675
676 /* find insert position */
677 idx = get_first_selected_item_playlist(app);
678
679 if(idx < 0)
680 return;
681
682 /* check for playing block */
683 if(idx_in_players_range(app, idx))
684 return;
685
686 item = app->playlist.item[idx];
687
688 if(ui_playlist_item_dialog(app, &item))
689 {
690 omnplay_library_normalize_item(app, &item);
691 app->playlist.item[idx] = item;
692 omnplay_playlist_draw_item(app, idx);
693 };
694 };
695
696 static void omnplay_ctl(omnplay_instance_t* app, control_buttons_t button)
697 {
698 int i, r;
699 int idx, start, stop;
700 omnplay_player_t *player;
701
702 pthread_mutex_lock(&app->playlist.lock);
703
704 idx = get_first_selected_item_playlist(app);
705
706 if(idx < 0)
707 {
708 pthread_mutex_unlock(&app->playlist.lock);
709 return;
710 };
711
712 g_warning("cue: selected item is %d\n", idx);
713
714 if(get_playlist_block(app, idx, &start, &stop) < 0)
715 {
716 pthread_mutex_unlock(&app->playlist.lock);
717 return;
718 };
719
720 g_warning("cue: range %d -> %d\n", start, stop);
721
722 player = get_player_at_pos(app, start);
723
724 if(!player)
725 {
726 pthread_mutex_unlock(&app->playlist.lock);
727 return;
728 };
729
730 pthread_mutex_lock(&app->players.lock);
731
732 if(BUTTON_PLAYER_STOP == button || BUTTON_PLAYER_CUE == button)
733 {
734 /* stop */
735 OmPlrStop((OmPlrHandle)player->handle);
736
737 /* detach previous clips */
738 player->playlist_length = -1;
739 OmPlrDetachAllClips((OmPlrHandle)player->handle);
740 };
741
742 if(BUTTON_PLAYER_CUE == button)
743 {
744 int o, c, p = 0;
745
746 /* Attach clips to timeline */
747 for(i = start, c = 0, o = 0; i <= stop; i++)
748 {
749 OmPlrClipInfo clip;
750
751 /* get clip info */
752 clip.maxMsTracks = 0;
753 clip.size = sizeof(clip);
754 r = OmPlrClipGetInfo((OmPlrHandle)player->handle, app->playlist.item[i].id, &clip);
755
756 if(!r)
757 {
758 unsigned int l;
759
760 g_warning("OmPlrClipGetInfo(%s): firstFrame=%d, lastFrame=%d\n",
761 app->playlist.item[i].id, clip.firstFrame, clip.lastFrame);
762
763 /* should we fix playlist clip timings */
764 if(!(
765 app->playlist.item[i].in >= clip.firstFrame &&
766 app->playlist.item[i].in + app->playlist.item[i].dur <= clip.lastFrame) ||
767 !app->playlist.item[i].dur)
768 {
769 g_warning("cue: item [%s] will be updated [%d;%d]->[%d;%d]\n",
770 app->playlist.item[i].id,
771 app->playlist.item[i].in, app->playlist.item[i].dur,
772 clip.firstFrame, clip.lastFrame - clip.firstFrame);
773
774 app->playlist.item[i].in = clip.firstFrame;
775 app->playlist.item[i].dur = clip.lastFrame - clip.firstFrame;
776 omnplay_playlist_draw_item(app, i);
777 };
778
779 r = OmPlrAttach((OmPlrHandle)player->handle,
780 app->playlist.item[i].id,
781 app->playlist.item[i].in,
782 app->playlist.item[i].in + app->playlist.item[i].dur,
783 0, omPlrShiftModeAfter, &l);
784 };
785
786 if(r)
787 {
788 g_warning("cue: failed with %d, %s\n", r, OmPlrGetErrorString((OmPlrError)r));
789 app->playlist.item[i].omn_idx = -1;
790 app->playlist.item[i].omn_offset = -1;
791 app->playlist.item[i].error |= PLAYLIST_ITEM_ERROR_CUE;
792 }
793 else
794 {
795 app->playlist.item[i].omn_idx = c;
796 app->playlist.item[i].omn_offset = o;
797 app->playlist.item[i].error &= 0xF ^ PLAYLIST_ITEM_ERROR_CUE;
798
799 /* save selected item offset */
800 if(i == idx) p = o;
801
802 c++;
803 o += app->playlist.item[i].dur;
804 };
805 };
806
807 if(c)
808 {
809 OmPlrStatus hs;
810
811 /* Set timeline min/max */
812 OmPlrSetMinPosMin((OmPlrHandle)player->handle);
813 OmPlrSetMaxPosMax((OmPlrHandle)player->handle);
814
815 /* Set timeline position */
816 hs.minPos = 0;
817 hs.size = sizeof(OmPlrStatus);
818 OmPlrGetPlayerStatus((OmPlrHandle)player->handle, &hs);
819 OmPlrSetPos((OmPlrHandle)player->handle, hs.minPos + p);
820
821 /* setup loop */
822 if(app->playlist.item[start].type & OMNPLAY_PLAYLIST_BLOCK_LOOP)
823 OmPlrLoop((OmPlrHandle)player->handle, hs.minPos, hs.maxPos);
824 else
825 OmPlrLoop((OmPlrHandle)player->handle, hs.minPos, hs.minPos);
826
827 player->playlist_start = start;
828 player->playlist_length = stop - start + 1;
829
830 /* Cue */
831 OmPlrCuePlay((OmPlrHandle)player->handle, 0.0);
832 };
833 };
834
835 if(BUTTON_PLAYER_PLAY == button)
836 {
837 /* play */
838 OmPlrPlay((OmPlrHandle)player->handle, 1.0);
839 };
840
841 if(BUTTON_PLAYER_PAUSE == button)
842 /* pause */
843 OmPlrPlay((OmPlrHandle)player->handle, 0.0);
844
845 pthread_mutex_unlock(&app->players.lock);
846
847 pthread_mutex_unlock(&app->playlist.lock);
848 };
849
850 static void omnplay_playlist_item_swap(omnplay_instance_t* app, int dir)
851 {
852 int sel, a, b, e = 1;
853 GtkTreePath* path;
854 playlist_item_t item;
855
856 /* find insert position */
857 sel = get_first_selected_item_playlist(app);
858 if(sel < 0)
859 return;
860
861 if(dir < 0)
862 {
863 a = sel - 1;
864 b = sel;
865 sel = a;
866 }
867 else
868 {
869 a = sel;
870 b = sel + 1;
871 sel = b;
872 };
873
874 /* check for playing block */
875 if(idx_in_players_range(app, a) || idx_in_players_range(app, b))
876 return;
877
878 pthread_mutex_lock(&app->playlist.lock);
879 pthread_mutex_lock(&app->players.lock);
880
881 /* swap */
882 item = app->playlist.item[a];
883 app->playlist.item[a] = app->playlist.item[b];
884 app->playlist.item[b] = item;
885
886 /* rewite type */
887 if(app->playlist.item[a].type != app->playlist.item[b].type)
888 {
889 e = 0;
890 app->playlist.item[a].type = OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE;
891 app->playlist.item[b].type = OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE;
892 };
893
894 /* redraw main items */
895 omnplay_playlist_draw_item(app, a);
896 omnplay_playlist_draw_item(app, b);
897
898 /* fix block types */
899 if(a && !e)
900 {
901 app->playlist.item[a - 1].type = (playlist_item_type_t)(app->playlist.item[a - 1].type |
902 OMNPLAY_PLAYLIST_BLOCK_END);
903 omnplay_playlist_draw_item(app, a - 1);
904 };
905 if(b + 1 < app->playlist.count && !e)
906 {
907 app->playlist.item[b + 1].type = (playlist_item_type_t)(app->playlist.item[b + 1].type |
908 OMNPLAY_PLAYLIST_BLOCK_BEGIN);
909 omnplay_playlist_draw_item(app, b + 1);
910 };
911
912 /* select */
913 path = gtk_tree_path_new_from_indices(sel, -1);
914 gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(app->playlist_grid)), path);
915 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app->playlist_grid), path, NULL, FALSE);
916 gtk_tree_path_free(path);
917
918 pthread_mutex_unlock(&app->players.lock);
919 pthread_mutex_unlock(&app->playlist.lock);
920 };
921
922 static void omnplay_library_add(omnplay_instance_t* app, int after)
923 {
924 int idx, c, i;
925 playlist_item_t* items;
926 playlist_item_type_t t;
927
928 /* find insert position */
929 idx = get_first_selected_item_playlist(app);
930 if(idx < 0)
931 idx = 0;
932 else
933 idx += (after)?1:0;
934
935 if(!omnplay_playlist_insert_check(app, idx, &t))
936 return;
937
938 items = omnplay_library_get_selected(app, &c);
939
940 /* clear item */
941 if(items)
942 {
943 for(i = 0; i < c; i++)
944 {
945 items[i].type = t;
946 items[i].error = 0;
947 };
948 omnplay_playlist_insert_items(app, idx, items, c);
949 };
950 };
951
952
953 static gboolean omnplay_button_click(omnplay_instance_t* app, control_buttons_t button)
954 {
955 switch(button)
956 {
957 case BUTTON_PLAYLIST_ITEM_ADD:
958 omnplay_playlist_item_add(app, 0);
959 break;
960 case BUTTON_PLAYLIST_ITEM_DEL:
961 omnplay_playlist_item_del(app);
962 break;
963 case BUTTON_PLAYLIST_ITEM_EDIT:
964 omnplay_playlist_item_edit(app);
965 break;
966 case BUTTON_PLAYLIST_LOAD:
967 omnplay_playlist_load(app);
968 break;
969 case BUTTON_PLAYLIST_SAVE:
970 omnplay_playlist_save(app);
971 break;
972 case BUTTON_PLAYLIST_BLOCK_SINGLE:
973 case BUTTON_PLAYLIST_BLOCK_LOOP:
974 omnplay_playlist_block(app, button);
975 break;
976 case BUTTON_PLAYLIST_ITEM_UP:
977 omnplay_playlist_item_swap(app, -1);
978 break;
979 case BUTTON_PLAYLIST_ITEM_DOWN:
980 omnplay_playlist_item_swap(app, +1);
981 break;
982 case BUTTON_PLAYER_CUE:
983 case BUTTON_PLAYER_PLAY:
984 case BUTTON_PLAYER_PAUSE:
985 case BUTTON_PLAYER_STOP:
986 omnplay_ctl(app, button);
987 break;
988 case BUTTON_LIBRARY_ADD:
989 omnplay_library_add(app, 0);
990 break;
991 case BUTTON_LIBRARY_REFRESH:
992 omnplay_library_refresh(app);
993 break;
994 case BUTTON_LIBRARY_FIND:
995 omnplay_library_search(app, 0);
996 break;
997 case BUTTON_LIBRARY_FIND_NEXT:
998 omnplay_library_search(app, 1);
999 break;
1000 };
1001
1002 return TRUE;
1003 };
1004
1005 static gboolean on_button_click(GtkWidget *button, gpointer user_data)
1006 {
1007 int i;
1008 omnplay_instance_t* app = (omnplay_instance_t*)user_data;
1009
1010 for(i = 1; i < BUTTON_LAST; i++)
1011 if(app->buttons[i] == button)
1012 return omnplay_button_click(app, (control_buttons_t)i);
1013
1014 return FALSE;
1015 };
1016
1017 static void omnplay_playlist_item_copy(omnplay_instance_t* app)
1018 {
1019 int *list, i;
1020
1021 list = omnplay_selected_idxs_playlist(app);
1022 if(!list) return;
1023
1024 for(i = 0; i < list[0]; i++)
1025 app->clipboard.item[i] = app->playlist.item[list[i + 1]];
1026 app->clipboard.count = list[0];
1027
1028 free(list);
1029 };
1030
1031 static void omnplay_playlist_item_paste(omnplay_instance_t* app, int after)
1032 {
1033 int idx, i;
1034 playlist_item_t* items;
1035 playlist_item_type_t t;
1036
1037 /* find insert position */
1038 idx = get_first_selected_item_playlist(app);
1039 if(idx < 0)
1040 idx = 0;
1041 else
1042 idx += (after)?1:0;
1043
1044 if(!omnplay_playlist_insert_check(app, idx, &t))
1045 return;
1046
1047 /* clear item */
1048 if(app->clipboard.count)
1049 {
1050 for(i = 0; i < app->clipboard.count; i++)
1051 {
1052 app->clipboard.item[i].type = t;
1053 app->clipboard.item[i].error = 0;
1054 };
1055 omnplay_playlist_insert_items(app, idx, app->clipboard.item, app->clipboard.count);
1056 };
1057 };
1058
1059 static gboolean on_playlist_grid_key(GtkWidget *widget, GdkEventKey *event, gpointer data)
1060 {
1061 omnplay_instance_t* app = (omnplay_instance_t*)data;
1062
1063 switch(event->keyval)
1064 {
1065 case GDK_C:
1066 case GDK_c:
1067 if(event->state & GDK_CONTROL_MASK)
1068 {
1069 omnplay_playlist_item_copy(app);
1070 return TRUE;
1071 };
1072 break;
1073 case GDK_V:
1074 case GDK_v:
1075 if(event->state & GDK_CONTROL_MASK)
1076 {
1077 omnplay_playlist_item_paste(app, 0);
1078 return TRUE;
1079 };
1080 break;
1081 case GDK_X:
1082 case GDK_x:
1083 if(event->state & GDK_CONTROL_MASK)
1084 {
1085 omnplay_playlist_item_copy(app);
1086 omnplay_playlist_item_del(app);
1087 return TRUE;
1088 };
1089 break;
1090 case GDK_S:
1091 case GDK_s:
1092 if(event->state & GDK_CONTROL_MASK)
1093 {
1094 omnplay_playlist_save(app);
1095 return TRUE;
1096 };
1097 break;
1098 case GDK_O:
1099 case GDK_o:
1100 if(event->state & GDK_CONTROL_MASK)
1101 {
1102 omnplay_playlist_load(app);
1103 return TRUE;
1104 };
1105 break;
1106 case GDK_KEY_uparrow:
1107 if(event->state & GDK_CONTROL_MASK)
1108 {
1109 omnplay_playlist_item_swap(app, -1);
1110 return TRUE;
1111 };
1112 break;
1113 case GDK_KEY_downarrow:
1114 if(event->state & GDK_CONTROL_MASK)
1115 {
1116 omnplay_playlist_item_swap(app, -1);
1117 return TRUE;
1118 };
1119 break;
1120 case GDK_KEY_space:
1121 omnplay_ctl(app, BUTTON_PLAYER_PLAY);
1122 return TRUE;
1123 case GDK_KEY_Return:
1124 omnplay_ctl(app, BUTTON_PLAYER_CUE);
1125 return TRUE;
1126 case GDK_KEY_Insert:
1127 omnplay_playlist_item_add(app, 0);
1128 return TRUE;
1129 case GDK_KEY_Delete:
1130 omnplay_playlist_item_del(app);
1131 return TRUE;
1132 case GDK_E:
1133 case GDK_e:
1134 omnplay_playlist_item_edit(app);
1135 return TRUE;
1136 };
1137
1138 return FALSE;
1139 };
1140
1141 static gboolean on_library_grid_key(GtkWidget *widget, GdkEventKey *event, gpointer data)
1142 {
1143 omnplay_instance_t* app = (omnplay_instance_t*)data;
1144
1145 switch(event->keyval)
1146 {
1147 case GDK_C:
1148 case GDK_c:
1149 if(event->state & GDK_CONTROL_MASK)
1150 {
1151 int count;
1152 playlist_item_t* items;
1153
1154 items = omnplay_library_get_selected(app, &count);
1155
1156 if(items)
1157 {
1158 int i;
1159
1160 for(i = 0; i < count; i++)
1161 app->clipboard.item[i] = items[i];
1162
1163 app->clipboard.count = count;
1164 };
1165
1166 return TRUE;
1167 };
1168 break;
1169 case GDK_V:
1170 case GDK_v:
1171 if(event->state & GDK_CONTROL_MASK)
1172 {
1173 g_warning("CTRL+v\n");
1174 return TRUE;
1175 };
1176 break;
1177 case GDK_X:
1178 case GDK_x:
1179 if(event->state & GDK_CONTROL_MASK)
1180 {
1181 g_warning("CTRL+x\n");
1182 return TRUE;
1183 };
1184 break;
1185 case GDK_KEY_BackSpace:
1186 omnplay_library_add(app, 0);
1187 return TRUE;
1188 case GDK_KEY_F5:
1189 omnplay_library_refresh(app);
1190 return TRUE;
1191 };
1192
1193 return FALSE;
1194 };
1195
1196 static gboolean on_library_grid_button(GtkWidget *widget, GdkEventButton *event, gpointer data)
1197 {
1198 // g_warning("on_library_grid_button: event->button=%d, event->type=%d", event->button, event->type);
1199
1200 if(event->button==1 && event->type==GDK_2BUTTON_PRESS)
1201 {
1202 omnplay_library_add((omnplay_instance_t* )data, 0);
1203 return TRUE;
1204 };
1205
1206 return FALSE;
1207 };
1208
1209 static gboolean on_playlist_grid_button(GtkWidget *widget, GdkEventButton *event, gpointer data)
1210 {
1211 // g_warning("on_playlist_grid_button");
1212
1213 if(event->button==1 && event->type==GDK_2BUTTON_PRESS)
1214 {
1215 omnplay_ctl((omnplay_instance_t* )data, BUTTON_PLAYER_CUE);
1216 return TRUE;
1217 };
1218
1219 return FALSE;
1220 };
1221
1222
1223
1224 static void library_grid_drag_data_get_cb(GtkWidget *widget, GdkDragContext *context,
1225 GtkSelectionData *selection_data, guint info, guint time, gpointer userdata)
1226 {
1227 int c;
1228 playlist_item_t* items;
1229 omnplay_instance_t* app = (omnplay_instance_t*)userdata;
1230
1231 g_warning("library_grid_drag_data_get_cb");
1232
1233 items = omnplay_library_get_selected(app, &c);
1234
1235 /* clear item */
1236 if(items)
1237 {
1238 gtk_selection_data_set(selection_data, selection_data->target, 8,
1239 (const guchar *)items, sizeof(playlist_item_t) * c);
1240 free(items);
1241 };
1242 };
1243
1244 static void playlist_grid_drag_data_get_cb(GtkWidget *widget, GdkDragContext *context,
1245 GtkSelectionData *selection_data, guint info, guint time, gpointer userdata)
1246 {
1247 int *list, i;
1248 playlist_item_t* items;
1249 omnplay_instance_t* app = (omnplay_instance_t*)userdata;
1250
1251 list = omnplay_selected_idxs_playlist(app);
1252 if(!list) return;
1253
1254 /* clear delete flag */
1255 for(i = 0; i < app->playlist.count; i++)
1256 app->playlist.item[i].del = 0;
1257
1258 items = (playlist_item_t*)malloc(sizeof(playlist_item_t) * list[0]);
1259 for(i = 0; i < list[0]; i++)
1260 {
1261 items[i] = app->playlist.item[list[i + 1]];
1262 if(context->action == GDK_ACTION_MOVE)
1263 app->playlist.item[list[i + 1]].del = 1;
1264 }
1265 gtk_selection_data_set(selection_data, selection_data->target, 8,
1266 (const guchar *)items, sizeof(playlist_item_t) * list[0]);
1267
1268 free(items);
1269 free(list);
1270 };
1271
1272 static void library_grid_drag_begin_cb(GtkWidget *widget, GdkDragContext *context, gpointer userdata)
1273 {
1274 g_warning("library_grid_drag_begin_cb");
1275 gtk_drag_source_set_icon_stock(widget, GTK_STOCK_DND);
1276 };
1277
1278 static void playlist_grid_drag_begin_cb(GtkWidget *widget, GdkDragContext *context, gpointer userdata)
1279 {
1280 g_warning("playlist_grid_drag_begin_cb");
1281 gtk_drag_source_set_icon_stock(widget, GTK_STOCK_DND);
1282 };
1283
1284 static void playlist_grid_drag_data_received(GtkWidget *widget, GdkDragContext *context,
1285 gint x, gint y, GtkSelectionData *selection_data, guint info, guint time, gpointer userdata)
1286 {
1287 int c, i, idx;
1288 playlist_item_type_t t;
1289 playlist_item_t* items;
1290 GtkTreePath *path = NULL;
1291 omnplay_instance_t* app = (omnplay_instance_t*)userdata;
1292
1293 g_warning("playlist_grid_drag_data_received: context->action=%d", context->action);
1294
1295 items = (playlist_item_t*)gtk_selection_data_get_data(selection_data);
1296 c = gtk_selection_data_get_length(selection_data);
1297
1298 if(c % sizeof(playlist_item_t))
1299 {
1300 g_warning("playlist_grid_drag_data_received: ODD ITEMS");
1301 }
1302 else
1303 {
1304 c /= sizeof(playlist_item_t);
1305
1306 if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), x, y, &path, NULL, NULL, NULL))
1307 {
1308 idx = gtk_tree_path_get_indices(path)[0];
1309 gtk_tree_path_free(path);
1310
1311 g_warning("playlist_grid_drag_data_received: gtk_tree_path_get_indice[0]=%d", idx);
1312
1313 /* normalize, FIX ME */
1314 idx--; if(idx < 0) idx = 0;
1315 }
1316 else
1317 idx = app->playlist.count;
1318
1319 g_warning("playlist_grid_drag_data_received: idx=%d", idx);
1320
1321 if(omnplay_playlist_insert_check(app, idx, &t))
1322 {
1323 for(i = 0; i < c; i++)
1324 {
1325 items[i].type = t;
1326 items[i].error = 0;
1327 };
1328 omnplay_playlist_insert_items(app, idx, items, c);
1329 };
1330 };
1331
1332 /* Finish the drag */
1333 gtk_drag_finish(context, TRUE, FALSE, time);
1334 };
1335
1336 static void playlist_grid_drag_data_delete(GtkWidget *widget, GdkDragContext *context, gpointer userdata)
1337 {
1338 int c, i, *list;
1339 omnplay_instance_t* app = (omnplay_instance_t*)userdata;
1340
1341 g_warning("playlist_grid_drag_data_delete");
1342
1343 list = (int*)malloc(sizeof(int) * MAX_PLAYLIST_ITEMS);
1344
1345 for(i = 0, c = 0; i < app->playlist.count; i++)
1346 if(app->playlist.item[i].del)
1347 if(!idx_in_players_range(app, i))
1348 {
1349 /* save index */
1350 list[c++] = i;
1351 g_warning("playlist_grid_drag_data_delete: i=%d, c=%d", i, c);
1352 };
1353
1354 if(c)
1355 omnplay_playlist_delete_items(app, list, c, 0);
1356
1357 free(list);
1358 };
1359
1360 /*
1361 * http://www.mail-archive.com/mahogany-users@lists.sourceforge.net/msg00286.html
1362 */
1363 static gboolean playlist_grid_drag_motion(GtkWidget *widget, GdkDragContext *context,
1364 gint x, gint y, guint time, gpointer data)
1365 {
1366 gboolean same;
1367 GtkWidget *source_widget;
1368
1369 g_warning("playlist_grid_drag_motion");
1370
1371 /* Get source widget and check if it is the same as the
1372 * destination widget.
1373 */
1374 source_widget = gtk_drag_get_source_widget(context);
1375 same = ((source_widget == widget) ? TRUE : FALSE);
1376
1377 /* Put additional checks here, perhaps if same is FALSE then
1378 * set the default drag to GDK_ACTION_COPY.
1379 */
1380
1381 /* Say we just want to allow GDK_ACTION_MOVE, first we check
1382 * if that is in the list of allowed actions on the dc. If
1383 * so then we set it to that. Note if the user holds down the
1384 * ctrl key then the only flag in dc->actions will be
1385 * GDK_ACTION_COPY. The constraint for dc->actions is that
1386 * specified from the given actions in gtk_drag_dest_set() and
1387 * gtk_drag_source_set().
1388 */
1389 if(same)
1390 {
1391 if(context->actions == GDK_ACTION_MOVE)
1392 gdk_drag_status(context, GDK_ACTION_COPY, time);
1393 else
1394 gdk_drag_status(context, GDK_ACTION_MOVE, time);
1395 }
1396 else
1397 gdk_drag_status(context, context->actions, time);
1398
1399 return(TRUE);
1400 }
1401
1402 void omnplay_init(omnplay_instance_t* app)
1403 {
1404 int i;
1405 pthread_mutexattr_t attr;
1406
1407 pthread_mutexattr_init(&attr);
1408 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
1409
1410 gtk_signal_connect( GTK_OBJECT( app->window ), "delete-event",
1411 GTK_SIGNAL_FUNC(on_main_window_delete_event), app);
1412
1413 gtk_signal_connect( GTK_OBJECT( app->window ), "destroy",
1414 GTK_SIGNAL_FUNC(on_main_window_destroy), app);
1415
1416 gtk_widget_add_events(app->playlist_grid, GDK_BUTTON_PRESS_MASK);
1417 gtk_widget_add_events(app->playlist_grid, GDK_KEY_PRESS_MASK);
1418 gtk_signal_connect(GTK_OBJECT(app->playlist_grid), "key-press-event",
1419 GTK_SIGNAL_FUNC(on_playlist_grid_key), app);
1420
1421 gtk_widget_add_events(app->library_grid, GDK_BUTTON_PRESS_MASK);
1422 gtk_widget_add_events(app->library_grid, GDK_KEY_PRESS_MASK);
1423 gtk_signal_connect(GTK_OBJECT(app->library_grid), "key-press-event",
1424 GTK_SIGNAL_FUNC(on_library_grid_key), app);
1425
1426 gtk_signal_connect(GTK_OBJECT(app->playlist_grid), "button-press-event",
1427 GTK_SIGNAL_FUNC(on_playlist_grid_button), app);
1428
1429 gtk_signal_connect(GTK_OBJECT(app->library_grid), "button-press-event",
1430 GTK_SIGNAL_FUNC(on_library_grid_button), app);
1431
1432 /* create lock */
1433 pthread_mutex_init(&app->players.lock, &attr);
1434 pthread_mutex_init(&app->playlist.lock, &attr);
1435 pthread_mutex_init(&app->library.lock, &attr);
1436
1437 /* create a omneon status thread */
1438 for(i = 0; i < app->players.count; i++)
1439 app->players.item[i].thread = g_thread_create(
1440 omnplay_thread_proc, &app->players.item[i], TRUE, NULL);
1441
1442 /* attach buttons click */
1443 for(i = 1; i < BUTTON_LAST; i++)
1444 gtk_signal_connect(GTK_OBJECT(app->buttons[i]), "clicked",
1445 GTK_SIGNAL_FUNC( on_button_click), app );
1446
1447 /* load library */
1448 omnplay_library_load(app);
1449
1450 pthread_mutexattr_destroy(&attr);
1451
1452 /* setup drag n drop source/target */
1453 static GtkTargetEntry drag_targets[] = { { (char*) "application/playlist_item_t", 0, 0 } };
1454
1455 gtk_drag_source_set(app->library_grid, GDK_BUTTON1_MASK,
1456 drag_targets, 1, (GdkDragAction)(GDK_ACTION_COPY));
1457
1458 gtk_drag_source_set(app->playlist_grid, GDK_BUTTON1_MASK,
1459 drag_targets, 1, (GdkDragAction)(GDK_ACTION_COPY | GDK_ACTION_MOVE));
1460
1461 gtk_drag_dest_set(app->playlist_grid, (GtkDestDefaults)(GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP),
1462 drag_targets, 1, (GdkDragAction)(GDK_ACTION_COPY | GDK_ACTION_MOVE));
1463
1464 g_signal_connect (app->library_grid, "drag_data_get", G_CALLBACK(library_grid_drag_data_get_cb), app);
1465 g_signal_connect (app->playlist_grid, "drag_data_get", G_CALLBACK(playlist_grid_drag_data_get_cb), app);
1466 g_signal_connect (app->library_grid, "drag_begin", G_CALLBACK(library_grid_drag_begin_cb), app);
1467 g_signal_connect (app->playlist_grid, "drag_begin", G_CALLBACK(playlist_grid_drag_begin_cb), app);
1468 g_signal_connect (app->playlist_grid, "drag_data_received", G_CALLBACK (playlist_grid_drag_data_received), app);
1469 g_signal_connect (app->playlist_grid, "drag_data_delete", G_CALLBACK (playlist_grid_drag_data_delete), app);
1470 g_signal_connect (app->playlist_grid, "drag_motion", G_CALLBACK (playlist_grid_drag_motion), app);
1471 };
1472
1473 void omnplay_release(omnplay_instance_t* app)
1474 {
1475 int i;
1476
1477 app->f_exit = 1;
1478
1479 for(i = 0; i < app->players.count; i++)
1480 /* create a omneon status thread */
1481 g_thread_join(app->players.item[i].thread);
1482
1483 /* destroy lock */
1484 pthread_mutex_destroy(&app->players.lock);
1485
1486 /* destroy lock */
1487 pthread_mutex_destroy(&app->playlist.lock);
1488
1489 /* load library */
1490 omnplay_library_save(app);
1491
1492 /* destroy library lock */
1493 pthread_mutex_destroy(&app->library.lock);
1494 };
1495
1496 void omnplay_playlist_normalize(omnplay_instance_t* app)
1497 {
1498 int i;
1499
1500 /* normalize playlist */
1501 for(i = 0; i < app->playlist.count; i++)
1502 if(omnplay_library_normalize_item(app, &app->playlist.item[i]))
1503 omnplay_playlist_draw_item(app, i);
1504 };
1505
1506 void omnplay_set_status(omnplay_instance_t* app, char* str)
1507 {
1508 gdk_threads_enter();
1509 gtk_label_set_text(GTK_LABEL(app->status_label), str);
1510 gdk_flush();
1511 gdk_threads_leave();
1512 };
1513