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