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