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