718ff74b816ec09bcbeaf2e37f777d10025260e6
[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 = 0;
792
793 g_warning("OmPlrClipGetInfo(%s): firstFrame=%d, lastFrame=%d\n",
794 app->playlist.item[i].id, clip.firstFrame, clip.lastFrame);
795
796 /* fix IN */
797 if(app->playlist.item[i].in < clip.firstFrame || app->playlist.item[i].in > clip.lastFrame)
798 {
799 app->playlist.item[i].in = clip.firstFrame;
800 l++;
801 };
802
803 /* fix DUR */
804 if(app->playlist.item[i].in + app->playlist.item[i].dur > clip.lastFrame || !app->playlist.item[i].dur)
805 {
806 app->playlist.item[i].dur = clip.firstFrame - app->playlist.item[i].in;
807 l++;
808 };
809
810 /* notify */
811 if(l)
812 {
813 g_warning("cue: item [%s] will be updated to [%d;%d]\n",
814 app->playlist.item[i].id, app->playlist.item[i].in, app->playlist.item[i].dur);
815 omnplay_playlist_draw_item(app, i);
816 };
817
818 r = OmPlrAttach((OmPlrHandle)player->handle,
819 app->playlist.item[i].id,
820 app->playlist.item[i].in,
821 app->playlist.item[i].in + app->playlist.item[i].dur,
822 0, omPlrShiftModeAfter, &l);
823 };
824
825 if(r)
826 {
827 g_warning("cue: failed with %d, %s\n", r, OmPlrGetErrorString((OmPlrError)r));
828 app->playlist.item[i].omn_idx = -1;
829 app->playlist.item[i].omn_offset = -1;
830 app->playlist.item[i].error |= PLAYLIST_ITEM_ERROR_CUE;
831 }
832 else
833 {
834 app->playlist.item[i].omn_idx = c;
835 app->playlist.item[i].omn_offset = o;
836 app->playlist.item[i].error &= 0xF ^ PLAYLIST_ITEM_ERROR_CUE;
837
838 /* save selected item offset */
839 if(i == idx) p = o;
840
841 c++;
842 o += app->playlist.item[i].dur;
843 };
844 };
845
846 if(c)
847 {
848 OmPlrStatus hs;
849
850 /* Set timeline min/max */
851 OmPlrSetMinPosMin((OmPlrHandle)player->handle);
852 OmPlrSetMaxPosMax((OmPlrHandle)player->handle);
853
854 /* Set timeline position */
855 hs.minPos = 0;
856 hs.size = sizeof(OmPlrStatus);
857 OmPlrGetPlayerStatus((OmPlrHandle)player->handle, &hs);
858 OmPlrSetPos((OmPlrHandle)player->handle, hs.minPos + p);
859
860 /* setup loop */
861 if(app->playlist.item[start].type & OMNPLAY_PLAYLIST_BLOCK_LOOP)
862 OmPlrLoop((OmPlrHandle)player->handle, hs.minPos, hs.maxPos);
863 else
864 OmPlrLoop((OmPlrHandle)player->handle, hs.minPos, hs.minPos);
865
866 player->playlist_start = start;
867 player->playlist_length = stop - start + 1;
868
869 /* Cue */
870 OmPlrCuePlay((OmPlrHandle)player->handle, 0.0);
871 };
872 };
873
874 if(BUTTON_PLAYER_PLAY == button)
875 {
876 /* play */
877 OmPlrPlay((OmPlrHandle)player->handle, 1.0);
878 };
879
880 if(BUTTON_PLAYER_PAUSE == button)
881 /* pause */
882 OmPlrPlay((OmPlrHandle)player->handle, 0.0);
883
884 pthread_mutex_unlock(&app->players.lock);
885
886 pthread_mutex_unlock(&app->playlist.lock);
887 };
888
889 static void omnplay_playlist_item_swap(omnplay_instance_t* app, int dir)
890 {
891 int sel, a, b, e = 1;
892 GtkTreePath* path;
893 playlist_item_t item;
894
895 /* find insert position */
896 sel = get_first_selected_item_playlist(app);
897 if(sel < 0)
898 return;
899
900 if(dir < 0)
901 {
902 a = sel - 1;
903 b = sel;
904 sel = a;
905 }
906 else
907 {
908 a = sel;
909 b = sel + 1;
910 sel = b;
911 };
912
913 /* check for playing block */
914 if(idx_in_players_range(app, a) || idx_in_players_range(app, b))
915 return;
916
917 pthread_mutex_lock(&app->playlist.lock);
918 pthread_mutex_lock(&app->players.lock);
919
920 /* swap */
921 item = app->playlist.item[a];
922 app->playlist.item[a] = app->playlist.item[b];
923 app->playlist.item[b] = item;
924
925 /* rewite type */
926 if(app->playlist.item[a].type != app->playlist.item[b].type)
927 {
928 e = 0;
929 app->playlist.item[a].type = OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE;
930 app->playlist.item[b].type = OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE;
931 };
932
933 /* redraw main items */
934 omnplay_playlist_draw_item(app, a);
935 omnplay_playlist_draw_item(app, b);
936
937 /* fix block types */
938 if(a && !e)
939 {
940 app->playlist.item[a - 1].type = (playlist_item_type_t)(app->playlist.item[a - 1].type |
941 OMNPLAY_PLAYLIST_BLOCK_END);
942 omnplay_playlist_draw_item(app, a - 1);
943 };
944 if(b + 1 < app->playlist.count && !e)
945 {
946 app->playlist.item[b + 1].type = (playlist_item_type_t)(app->playlist.item[b + 1].type |
947 OMNPLAY_PLAYLIST_BLOCK_BEGIN);
948 omnplay_playlist_draw_item(app, b + 1);
949 };
950
951 /* select */
952 path = gtk_tree_path_new_from_indices(sel, -1);
953 gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(app->playlist_grid)), path);
954 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app->playlist_grid), path, NULL, FALSE);
955 gtk_tree_path_free(path);
956
957 pthread_mutex_unlock(&app->players.lock);
958 pthread_mutex_unlock(&app->playlist.lock);
959 };
960
961 static void omnplay_library_add(omnplay_instance_t* app, int after)
962 {
963 int idx, c, i;
964 playlist_item_t* items;
965 playlist_item_type_t t;
966
967 /* find insert position */
968 idx = get_first_selected_item_playlist(app);
969 if(idx < 0)
970 idx = 0;
971 else
972 idx += (after)?1:0;
973
974 if(!omnplay_playlist_insert_check(app, idx, &t))
975 return;
976
977 items = omnplay_library_get_selected(app, &c);
978
979 /* clear item */
980 if(items)
981 {
982 for(i = 0; i < c; i++)
983 {
984 items[i].type = t;
985 items[i].error = 0;
986 };
987 omnplay_playlist_insert_items(app, idx, items, c);
988 };
989 };
990
991
992 static gboolean omnplay_button_click(omnplay_instance_t* app, control_buttons_t button)
993 {
994 switch(button)
995 {
996 case BUTTON_PLAYLIST_ITEM_ADD:
997 omnplay_playlist_item_add(app, 0);
998 break;
999 case BUTTON_PLAYLIST_ITEM_DEL:
1000 omnplay_playlist_item_del(app);
1001 break;
1002 case BUTTON_PLAYLIST_ITEM_EDIT:
1003 omnplay_playlist_item_edit(app);
1004 break;
1005 case BUTTON_PLAYLIST_LOAD:
1006 omnplay_playlist_load(app);
1007 break;
1008 case BUTTON_PLAYLIST_SAVE:
1009 omnplay_playlist_save(app);
1010 break;
1011 case BUTTON_PLAYLIST_BLOCK_SINGLE:
1012 case BUTTON_PLAYLIST_BLOCK_LOOP:
1013 omnplay_playlist_block(app, button);
1014 break;
1015 case BUTTON_PLAYLIST_ITEM_UP:
1016 omnplay_playlist_item_swap(app, -1);
1017 break;
1018 case BUTTON_PLAYLIST_ITEM_DOWN:
1019 omnplay_playlist_item_swap(app, +1);
1020 break;
1021 case BUTTON_PLAYER_CUE:
1022 case BUTTON_PLAYER_PLAY:
1023 case BUTTON_PLAYER_PAUSE:
1024 case BUTTON_PLAYER_STOP:
1025 omnplay_ctl(app, button);
1026 break;
1027 case BUTTON_LIBRARY_ADD:
1028 omnplay_library_add(app, 0);
1029 break;
1030 case BUTTON_LIBRARY_REFRESH:
1031 omnplay_library_refresh(app);
1032 break;
1033 case BUTTON_LIBRARY_FIND:
1034 omnplay_library_search(app, 0);
1035 break;
1036 case BUTTON_LIBRARY_FIND_NEXT:
1037 omnplay_library_search(app, 1);
1038 break;
1039 case BUTTON_PLAYLIST_RELINK:
1040 omnplay_playlist_relink(app);
1041 break;
1042 };
1043
1044 return TRUE;
1045 };
1046
1047 static gboolean on_button_click(GtkWidget *button, gpointer user_data)
1048 {
1049 int i;
1050 omnplay_instance_t* app = (omnplay_instance_t*)user_data;
1051
1052 for(i = 1; i < BUTTON_LAST; i++)
1053 if(app->buttons[i] == button)
1054 return omnplay_button_click(app, (control_buttons_t)i);
1055
1056 return FALSE;
1057 };
1058
1059 static void omnplay_playlist_item_copy(omnplay_instance_t* app)
1060 {
1061 int *list, i;
1062
1063 list = omnplay_selected_idxs_playlist(app);
1064 if(!list) return;
1065
1066 for(i = 0; i < list[0]; i++)
1067 app->clipboard.item[i] = app->playlist.item[list[i + 1]];
1068 app->clipboard.count = list[0];
1069
1070 free(list);
1071 };
1072
1073 static void omnplay_playlist_item_paste(omnplay_instance_t* app, int after)
1074 {
1075 int idx, i;
1076 playlist_item_t* items;
1077 playlist_item_type_t t;
1078
1079 /* find insert position */
1080 idx = get_first_selected_item_playlist(app);
1081 if(idx < 0)
1082 idx = 0;
1083 else
1084 idx += (after)?1:0;
1085
1086 if(!omnplay_playlist_insert_check(app, idx, &t))
1087 return;
1088
1089 /* clear item */
1090 if(app->clipboard.count)
1091 {
1092 for(i = 0; i < app->clipboard.count; i++)
1093 {
1094 app->clipboard.item[i].type = t;
1095 app->clipboard.item[i].error = 0;
1096 };
1097 omnplay_playlist_insert_items(app, idx, app->clipboard.item, app->clipboard.count);
1098 };
1099 };
1100
1101 static gboolean on_playlist_grid_key(GtkWidget *widget, GdkEventKey *event, gpointer data)
1102 {
1103 omnplay_instance_t* app = (omnplay_instance_t*)data;
1104
1105 switch(event->keyval)
1106 {
1107 case GDK_C:
1108 case GDK_c:
1109 if(event->state & GDK_CONTROL_MASK)
1110 {
1111 omnplay_playlist_item_copy(app);
1112 return TRUE;
1113 };
1114 break;
1115 case GDK_V:
1116 case GDK_v:
1117 if(event->state & GDK_CONTROL_MASK)
1118 {
1119 omnplay_playlist_item_paste(app, 0);
1120 return TRUE;
1121 };
1122 break;
1123 case GDK_X:
1124 case GDK_x:
1125 if(event->state & GDK_CONTROL_MASK)
1126 {
1127 omnplay_playlist_item_copy(app);
1128 omnplay_playlist_item_del(app);
1129 return TRUE;
1130 };
1131 break;
1132 case GDK_S:
1133 case GDK_s:
1134 if(event->state & GDK_CONTROL_MASK)
1135 {
1136 omnplay_playlist_save(app);
1137 return TRUE;
1138 };
1139 break;
1140 case GDK_O:
1141 case GDK_o:
1142 if(event->state & GDK_CONTROL_MASK)
1143 {
1144 omnplay_playlist_load(app);
1145 return TRUE;
1146 };
1147 break;
1148 case GDK_KEY_uparrow:
1149 if(event->state & GDK_CONTROL_MASK)
1150 {
1151 omnplay_playlist_item_swap(app, -1);
1152 return TRUE;
1153 };
1154 break;
1155 case GDK_KEY_downarrow:
1156 if(event->state & GDK_CONTROL_MASK)
1157 {
1158 omnplay_playlist_item_swap(app, -1);
1159 return TRUE;
1160 };
1161 break;
1162 case GDK_KEY_space:
1163 omnplay_ctl(app, BUTTON_PLAYER_PLAY);
1164 return TRUE;
1165 case GDK_KEY_Return:
1166 omnplay_ctl(app, BUTTON_PLAYER_CUE);
1167 return TRUE;
1168 case GDK_KEY_Insert:
1169 omnplay_playlist_item_add(app, 0);
1170 return TRUE;
1171 case GDK_KEY_Delete:
1172 omnplay_playlist_item_del(app);
1173 return TRUE;
1174 case GDK_E:
1175 case GDK_e:
1176 omnplay_playlist_item_edit(app);
1177 return TRUE;
1178 };
1179
1180 return FALSE;
1181 };
1182
1183 static gboolean on_library_grid_key(GtkWidget *widget, GdkEventKey *event, gpointer data)
1184 {
1185 omnplay_instance_t* app = (omnplay_instance_t*)data;
1186
1187 switch(event->keyval)
1188 {
1189 case GDK_C:
1190 case GDK_c:
1191 if(event->state & GDK_CONTROL_MASK)
1192 {
1193 int count;
1194 playlist_item_t* items;
1195
1196 items = omnplay_library_get_selected(app, &count);
1197
1198 if(items)
1199 {
1200 int i;
1201
1202 for(i = 0; i < count; i++)
1203 app->clipboard.item[i] = items[i];
1204
1205 app->clipboard.count = count;
1206 };
1207
1208 return TRUE;
1209 };
1210 break;
1211 case GDK_V:
1212 case GDK_v:
1213 if(event->state & GDK_CONTROL_MASK)
1214 {
1215 g_warning("CTRL+v\n");
1216 return TRUE;
1217 };
1218 break;
1219 case GDK_X:
1220 case GDK_x:
1221 if(event->state & GDK_CONTROL_MASK)
1222 {
1223 g_warning("CTRL+x\n");
1224 return TRUE;
1225 };
1226 break;
1227 case GDK_KEY_BackSpace:
1228 omnplay_library_add(app, 0);
1229 return TRUE;
1230 case GDK_KEY_F5:
1231 omnplay_library_refresh(app);
1232 return TRUE;
1233 };
1234
1235 return FALSE;
1236 };
1237
1238 static gboolean on_library_grid_button(GtkWidget *widget, GdkEventButton *event, gpointer data)
1239 {
1240 // g_warning("on_library_grid_button: event->button=%d, event->type=%d", event->button, event->type);
1241
1242 if(event->button==1 && event->type==GDK_2BUTTON_PRESS)
1243 {
1244 omnplay_library_add((omnplay_instance_t* )data, 0);
1245 return TRUE;
1246 };
1247
1248 return FALSE;
1249 };
1250
1251 static gboolean on_playlist_grid_button(GtkWidget *widget, GdkEventButton *event, gpointer data)
1252 {
1253 omnplay_instance_t* app = (omnplay_instance_t*)data;
1254
1255 // g_warning("on_playlist_grid_button");
1256
1257 if(event->button==1 && event->type==GDK_2BUTTON_PRESS)
1258 {
1259 omnplay_playlist_item_edit(app);
1260 return TRUE;
1261 };
1262
1263 return FALSE;
1264 };
1265
1266
1267
1268 static void library_grid_drag_data_get_cb(GtkWidget *widget, GdkDragContext *context,
1269 GtkSelectionData *selection_data, guint info, guint time, gpointer userdata)
1270 {
1271 int c;
1272 playlist_item_t* items;
1273 omnplay_instance_t* app = (omnplay_instance_t*)userdata;
1274
1275 g_warning("library_grid_drag_data_get_cb");
1276
1277 items = omnplay_library_get_selected(app, &c);
1278
1279 /* clear item */
1280 if(items)
1281 {
1282 gtk_selection_data_set(selection_data, selection_data->target, 8,
1283 (const guchar *)items, sizeof(playlist_item_t) * c);
1284 free(items);
1285 };
1286 };
1287
1288 static void playlist_grid_drag_data_get_cb(GtkWidget *widget, GdkDragContext *context,
1289 GtkSelectionData *selection_data, guint info, guint time, gpointer userdata)
1290 {
1291 int *list, i;
1292 playlist_item_t* items;
1293 omnplay_instance_t* app = (omnplay_instance_t*)userdata;
1294
1295 list = omnplay_selected_idxs_playlist(app);
1296 if(!list) return;
1297
1298 /* clear delete flag */
1299 for(i = 0; i < app->playlist.count; i++)
1300 app->playlist.item[i].del = 0;
1301
1302 items = (playlist_item_t*)malloc(sizeof(playlist_item_t) * list[0]);
1303 for(i = 0; i < list[0]; i++)
1304 {
1305 items[i] = app->playlist.item[list[i + 1]];
1306 if(context->action == GDK_ACTION_MOVE)
1307 app->playlist.item[list[i + 1]].del = 1;
1308 }
1309 gtk_selection_data_set(selection_data, selection_data->target, 8,
1310 (const guchar *)items, sizeof(playlist_item_t) * list[0]);
1311
1312 free(items);
1313 free(list);
1314 };
1315
1316 static void library_grid_drag_begin_cb(GtkWidget *widget, GdkDragContext *context, gpointer userdata)
1317 {
1318 g_warning("library_grid_drag_begin_cb");
1319 gtk_drag_source_set_icon_stock(widget, GTK_STOCK_DND);
1320 };
1321
1322 static void playlist_grid_drag_begin_cb(GtkWidget *widget, GdkDragContext *context, gpointer userdata)
1323 {
1324 g_warning("playlist_grid_drag_begin_cb");
1325 gtk_drag_source_set_icon_stock(widget, GTK_STOCK_DND);
1326 };
1327
1328 static void playlist_grid_drag_data_received(GtkWidget *widget, GdkDragContext *context,
1329 gint x, gint y, GtkSelectionData *selection_data, guint info, guint time, gpointer userdata)
1330 {
1331 int c, i, idx;
1332 playlist_item_type_t t;
1333 playlist_item_t* items;
1334 GtkTreePath *path = NULL;
1335 omnplay_instance_t* app = (omnplay_instance_t*)userdata;
1336
1337 g_warning("playlist_grid_drag_data_received: context->action=%d", context->action);
1338
1339 items = (playlist_item_t*)gtk_selection_data_get_data(selection_data);
1340 c = gtk_selection_data_get_length(selection_data);
1341
1342 if(c % sizeof(playlist_item_t))
1343 {
1344 g_warning("playlist_grid_drag_data_received: ODD ITEMS");
1345 }
1346 else
1347 {
1348 c /= sizeof(playlist_item_t);
1349
1350 if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), x, y, &path, NULL, NULL, NULL))
1351 {
1352 idx = gtk_tree_path_get_indices(path)[0];
1353 gtk_tree_path_free(path);
1354
1355 g_warning("playlist_grid_drag_data_received: gtk_tree_path_get_indice[0]=%d", idx);
1356
1357 /* normalize, FIX ME */
1358 idx--; if(idx < 0) idx = 0;
1359 }
1360 else
1361 idx = app->playlist.count;
1362
1363 g_warning("playlist_grid_drag_data_received: idx=%d", idx);
1364
1365 if(omnplay_playlist_insert_check(app, idx, &t))
1366 {
1367 for(i = 0; i < c; i++)
1368 {
1369 items[i].type = t;
1370 items[i].error = 0;
1371 };
1372 omnplay_playlist_insert_items(app, idx, items, c);
1373 };
1374 };
1375
1376 /* Finish the drag */
1377 gtk_drag_finish(context, TRUE, FALSE, time);
1378 };
1379
1380 static void playlist_grid_drag_data_delete(GtkWidget *widget, GdkDragContext *context, gpointer userdata)
1381 {
1382 int c, i, *list;
1383 omnplay_instance_t* app = (omnplay_instance_t*)userdata;
1384
1385 g_warning("playlist_grid_drag_data_delete");
1386
1387 list = (int*)malloc(sizeof(int) * MAX_PLAYLIST_ITEMS);
1388
1389 for(i = 0, c = 0; i < app->playlist.count; i++)
1390 if(app->playlist.item[i].del)
1391 if(!idx_in_players_range(app, i))
1392 {
1393 /* save index */
1394 list[c++] = i;
1395 g_warning("playlist_grid_drag_data_delete: i=%d, c=%d", i, c);
1396 };
1397
1398 if(c)
1399 omnplay_playlist_delete_items(app, list, c, 0);
1400
1401 free(list);
1402 };
1403
1404 /*
1405 * http://www.mail-archive.com/mahogany-users@lists.sourceforge.net/msg00286.html
1406 */
1407 static gboolean playlist_grid_drag_motion(GtkWidget *widget, GdkDragContext *context,
1408 gint x, gint y, guint time, gpointer data)
1409 {
1410 gboolean same;
1411 GtkWidget *source_widget;
1412
1413 g_warning("playlist_grid_drag_motion");
1414
1415 /* Get source widget and check if it is the same as the
1416 * destination widget.
1417 */
1418 source_widget = gtk_drag_get_source_widget(context);
1419 same = ((source_widget == widget) ? TRUE : FALSE);
1420
1421 /* Put additional checks here, perhaps if same is FALSE then
1422 * set the default drag to GDK_ACTION_COPY.
1423 */
1424
1425 /* Say we just want to allow GDK_ACTION_MOVE, first we check
1426 * if that is in the list of allowed actions on the dc. If
1427 * so then we set it to that. Note if the user holds down the
1428 * ctrl key then the only flag in dc->actions will be
1429 * GDK_ACTION_COPY. The constraint for dc->actions is that
1430 * specified from the given actions in gtk_drag_dest_set() and
1431 * gtk_drag_source_set().
1432 */
1433 if(same)
1434 {
1435 if(context->actions == GDK_ACTION_MOVE)
1436 gdk_drag_status(context, GDK_ACTION_COPY, time);
1437 else
1438 gdk_drag_status(context, GDK_ACTION_MOVE, time);
1439 }
1440 else
1441 gdk_drag_status(context, context->actions, time);
1442
1443 return(TRUE);
1444 }
1445
1446 void omnplay_init(omnplay_instance_t* app)
1447 {
1448 int i;
1449 pthread_mutexattr_t attr;
1450
1451 pthread_mutexattr_init(&attr);
1452 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
1453
1454 gtk_signal_connect( GTK_OBJECT( app->window ), "delete-event",
1455 GTK_SIGNAL_FUNC(on_main_window_delete_event), app);
1456
1457 gtk_signal_connect( GTK_OBJECT( app->window ), "destroy",
1458 GTK_SIGNAL_FUNC(on_main_window_destroy), app);
1459
1460 gtk_widget_add_events(app->playlist_grid, GDK_BUTTON_PRESS_MASK);
1461 gtk_widget_add_events(app->playlist_grid, GDK_KEY_PRESS_MASK);
1462 gtk_signal_connect(GTK_OBJECT(app->playlist_grid), "key-press-event",
1463 GTK_SIGNAL_FUNC(on_playlist_grid_key), app);
1464
1465 gtk_widget_add_events(app->library_grid, GDK_BUTTON_PRESS_MASK);
1466 gtk_widget_add_events(app->library_grid, GDK_KEY_PRESS_MASK);
1467 gtk_signal_connect(GTK_OBJECT(app->library_grid), "key-press-event",
1468 GTK_SIGNAL_FUNC(on_library_grid_key), app);
1469
1470 gtk_signal_connect(GTK_OBJECT(app->playlist_grid), "button-press-event",
1471 GTK_SIGNAL_FUNC(on_playlist_grid_button), app);
1472
1473 gtk_signal_connect(GTK_OBJECT(app->library_grid), "button-press-event",
1474 GTK_SIGNAL_FUNC(on_library_grid_button), app);
1475
1476 /* create lock */
1477 pthread_mutex_init(&app->players.lock, &attr);
1478 pthread_mutex_init(&app->playlist.lock, &attr);
1479 pthread_mutex_init(&app->library.lock, &attr);
1480
1481 /* create a omneon status thread */
1482 for(i = 0; i < app->players.count; i++)
1483 app->players.item[i].thread = g_thread_create(
1484 omnplay_thread_proc, &app->players.item[i], TRUE, NULL);
1485
1486 /* attach buttons click */
1487 for(i = 1; i < BUTTON_LAST; i++)
1488 gtk_signal_connect(GTK_OBJECT(app->buttons[i]), "clicked",
1489 GTK_SIGNAL_FUNC( on_button_click), app );
1490
1491 /* load library */
1492 omnplay_library_load(app);
1493
1494 pthread_mutexattr_destroy(&attr);
1495
1496 /* setup drag n drop source/target */
1497 static GtkTargetEntry drag_targets[] = { { (char*) "application/playlist_item_t", 0, 0 } };
1498
1499 gtk_drag_source_set(app->library_grid, GDK_BUTTON1_MASK,
1500 drag_targets, 1, (GdkDragAction)(GDK_ACTION_COPY));
1501
1502 gtk_drag_source_set(app->playlist_grid, GDK_BUTTON1_MASK,
1503 drag_targets, 1, (GdkDragAction)(GDK_ACTION_COPY | GDK_ACTION_MOVE));
1504
1505 gtk_drag_dest_set(app->playlist_grid, (GtkDestDefaults)(GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP),
1506 drag_targets, 1, (GdkDragAction)(GDK_ACTION_COPY | GDK_ACTION_MOVE));
1507
1508 g_signal_connect (app->library_grid, "drag_data_get", G_CALLBACK(library_grid_drag_data_get_cb), app);
1509 g_signal_connect (app->playlist_grid, "drag_data_get", G_CALLBACK(playlist_grid_drag_data_get_cb), app);
1510 g_signal_connect (app->library_grid, "drag_begin", G_CALLBACK(library_grid_drag_begin_cb), app);
1511 g_signal_connect (app->playlist_grid, "drag_begin", G_CALLBACK(playlist_grid_drag_begin_cb), app);
1512 g_signal_connect (app->playlist_grid, "drag_data_received", G_CALLBACK (playlist_grid_drag_data_received), app);
1513 g_signal_connect (app->playlist_grid, "drag_data_delete", G_CALLBACK (playlist_grid_drag_data_delete), app);
1514 g_signal_connect (app->playlist_grid, "drag_motion", G_CALLBACK (playlist_grid_drag_motion), app);
1515 };
1516
1517 void omnplay_release(omnplay_instance_t* app)
1518 {
1519 int i;
1520
1521 app->f_exit = 1;
1522
1523 for(i = 0; i < app->players.count; i++)
1524 /* create a omneon status thread */
1525 g_thread_join(app->players.item[i].thread);
1526
1527 /* destroy lock */
1528 pthread_mutex_destroy(&app->players.lock);
1529
1530 /* destroy lock */
1531 pthread_mutex_destroy(&app->playlist.lock);
1532
1533 /* load library */
1534 omnplay_library_save(app);
1535
1536 /* destroy library lock */
1537 pthread_mutex_destroy(&app->library.lock);
1538 };
1539
1540 void omnplay_playlist_normalize(omnplay_instance_t* app)
1541 {
1542 int i;
1543
1544 /* normalize playlist */
1545 for(i = 0; i < app->playlist.count; i++)
1546 if(omnplay_library_normalize_item(app, &app->playlist.item[i]))
1547 omnplay_playlist_draw_item(app, i);
1548 };
1549
1550 void omnplay_set_status(omnplay_instance_t* app, char* str)
1551 {
1552 gdk_threads_enter();
1553 gtk_label_set_text(GTK_LABEL(app->status_label), str);
1554 gdk_flush();
1555 gdk_threads_leave();
1556 };
1557