2 * omnplay.c -- GTK+ 2 omnplay
3 * Copyright (C) 2011 Maksym Veremeyenko <verem@m1stereo.tv>
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.
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.
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.
32 #include <gdk/gdkkeysyms.h>
40 #include "omplrclnt.h"
42 int omnplay_get_content(omnplay_instance_t
* app
, playlist_item_t
*items
, int limit
,
43 omnplay_get_content_cb_proc proc
, void* data
)
46 OmPlrClipInfo clip_info
;
47 char clip_name
[omPlrMaxClipDirLen
];
49 pthread_mutex_lock(&app
->players
.lock
);
51 r
= OmPlrClipGetFirst((OmPlrHandle
)app
->players
.item
[0].handle
, clip_name
, sizeof(clip_name
));
52 for(; c
< limit
&& !r
;)
55 clip_info
.maxMsTracks
= 0;
56 clip_info
.size
= sizeof(clip_info
);
58 r
= OmPlrClipGetInfo((OmPlrHandle
)app
->players
.item
[0].handle
, clip_name
, &clip_info
);
63 strncpy(items
[c
].id
, clip_name
, PATH_MAX
);
64 items
[c
].in
= clip_info
.firstFrame
;
65 items
[c
].dur
= clip_info
.lastFrame
- clip_info
.firstFrame
;
68 pthread_mutex_unlock(&app
->players
.lock
);
70 proc(app
, &items
[c
], data
);
71 pthread_mutex_lock(&app
->players
.lock
);
76 r
= OmPlrClipGetNext((OmPlrHandle
)app
->players
.item
[0].handle
, clip_name
, sizeof(clip_name
));
79 pthread_mutex_unlock(&app
->players
.lock
);
85 static gboolean
on_main_window_delete_event( GtkWidget
*widget
, GdkEvent
*event
, gpointer user_data
)
87 g_print ("delete event occurred [start]\n");
89 omnplay_release((omnplay_instance_t
*)user_data
);
91 g_print ("delete event occurred [finish]\n");
96 static void on_main_window_destroy( GtkWidget
*widget
, gpointer user_data
)
98 g_print ("destroy occurred\n");
102 omnplay_instance_t
* omnplay_create(int argc
, char** argv
)
105 omnplay_instance_t
* app
;
107 /* prepare application instance */
108 app
= (omnplay_instance_t
*)malloc(sizeof(omnplay_instance_t
));
109 memset(app
, 0, sizeof(omnplay_instance_t
));
111 /* load parameters from command line */
112 if(!omnplay_opt(argc
, argv
, app
) && app
->players
.count
)
113 app
->window
= ui_omnplay(app
);
120 void omnplay_destroy(omnplay_instance_t
* app
)
125 static int find_index_of_playlist_item(omnplay_instance_t
* app
, int start
, int idx
)
127 if(start
< 0 || start
>= app
->playlist
.count
)
132 if(app
->playlist
.item
[start
].omn_idx
== idx
)
135 if(app
->playlist
.item
[start
].type
& OMNPLAY_PLAYLIST_BLOCK_END
)
144 static void omnplay_update_status(omnplay_player_t
* player
, OmPlrStatus
*prev
, OmPlrStatus
*curr
)
147 char tc_cur
[32], tc_rem
[32], state
[32], status
[32];
152 frames2tc(curr
->pos
- curr
->minPos
, 25.0, tc_cur
);
153 frames2tc(curr
->maxPos
- curr
->pos
, 25.0, tc_rem
);
154 strcpy(status
, "ONLINE");
155 clip
= curr
->currClipName
;
159 case omPlrStateStopped
: strcpy(state
, "STOPPED"); break;
160 case omPlrStateCuePlay
: strcpy(state
, "CUE_PLAY"); break;
161 case omPlrStatePlay
: strcpy(state
, "PLAY"); break;
162 case omPlrStateCueRecord
: strcpy(state
, "CUE_RECORD"); break;
163 case omPlrStateRecord
: strcpy(state
, "RECORD"); break;
172 strcpy(status
, "OFFLINE");
175 /* update status in status page */
177 gtk_label_set_text(GTK_LABEL (player
->label_tc_cur
), tc_cur
);
178 gtk_label_set_text(GTK_LABEL (player
->label_tc_rem
), tc_rem
);
179 gtk_label_set_text(GTK_LABEL (player
->label_state
), state
);
180 gtk_label_set_text(GTK_LABEL (player
->label_status
), status
);
181 gtk_label_set_text(GTK_LABEL (player
->label_clip
), clip
);
185 /* update remaining time */
187 pthread_mutex_lock(&player
->app
->playlist
.lock
);
188 pthread_mutex_lock(&player
->app
->players
.lock
);
189 if(curr
->state
== omPlrStatePlay
|| curr
->state
== omPlrStateCuePlay
)
191 idx
= find_index_of_playlist_item(player
->app
, player
->playlist_start
, curr
->currClipNum
);
194 frames2tc(curr
->currClipStartPos
+ curr
->currClipLen
- curr
->pos
, 25.0, tc_rem
);
195 omnplay_playlist_draw_item_rem(player
->app
, idx
, tc_rem
);
197 if(curr
->currClipNum
!= prev
->currClipNum
&& 1 != prev
->numClips
)
200 idx
= find_index_of_playlist_item(player
->app
, player
->playlist_start
, prev
->currClipNum
);
202 omnplay_playlist_draw_item_rem(player
->app
, idx
, tc_rem
);
208 idx
= find_index_of_playlist_item(player
->app
, player
->playlist_start
, curr
->currClipNum
);
210 omnplay_playlist_draw_item_rem(player
->app
, idx
, tc_rem
);
211 idx
= find_index_of_playlist_item(player
->app
, player
->playlist_start
, prev
->currClipNum
);
213 omnplay_playlist_draw_item_rem(player
->app
, idx
, tc_rem
);
215 pthread_mutex_unlock(&player
->app
->players
.lock
);
216 pthread_mutex_unlock(&player
->app
->playlist
.lock
);
221 memcpy(prev
, curr
, sizeof(OmPlrStatus
));
224 static void* omnplay_thread_proc(void* data
)
227 OmPlrStatus st_curr
, st_prev
;
228 omnplay_player_t
* player
= (omnplay_player_t
*)data
;
230 g_warning("omnplay_thread_proc\n");
232 memset(&st_curr
, 0, sizeof(OmPlrStatus
));
233 memset(&st_prev
, 0, sizeof(OmPlrStatus
));
236 pthread_mutex_lock(&player
->app
->players
.lock
);
237 r
= OmPlrOpen(player
->host
, player
->name
, (OmPlrHandle
*)&player
->handle
);
238 pthread_mutex_unlock(&player
->app
->players
.lock
);
241 g_warning("ERROR: OmPlrOpen(%s, %s) failed with 0x%.8X\n",
242 player
->host
, player
->name
, r
);
247 /* setup to do not reconnect */
248 pthread_mutex_lock(&player
->app
->players
.lock
);
249 OmPlrSetRetryOpen((OmPlrHandle
)player
->handle
, 0);
250 pthread_mutex_unlock(&player
->app
->players
.lock
);
252 /* setup directory */
253 if(player
->app
->players
.path
[0])
255 pthread_mutex_lock(&player
->app
->players
.lock
);
256 r
= OmPlrClipSetDirectory((OmPlrHandle
)player
->handle
, player
->app
->players
.path
);
257 pthread_mutex_unlock(&player
->app
->players
.lock
);
261 g_warning("ERROR: OmPlrClipSetDirectory(%s) failed with 0x%.8X\n",
262 player
->app
->players
.path
, r
);
264 pthread_mutex_lock(&player
->app
->players
.lock
);
265 OmPlrClose((OmPlrHandle
)player
->handle
);
266 pthread_mutex_unlock(&player
->app
->players
.lock
);
273 for(r
= 0 ; !player
->app
->f_exit
&& !r
;)
283 pthread_mutex_lock(&player
->app
->players
.lock
);
284 st_curr
.size
= sizeof(OmPlrStatus
);
285 r
= OmPlrGetPlayerStatus((OmPlrHandle
)player
->handle
, &st_curr
);
286 pthread_mutex_unlock(&player
->app
->players
.lock
);
289 g_warning("ERROR: OmPlrGetPlayerStatus failed with 0x%.8X\n", r
);
291 if(memcmp(&st_curr
, &st_prev
, sizeof(OmPlrStatus
)))
292 omnplay_update_status(player
, &st_prev
, &st_curr
);
295 pthread_mutex_lock(&player
->app
->players
.lock
);
296 OmPlrClose((OmPlrHandle
)player
->handle
);
297 pthread_mutex_unlock(&player
->app
->players
.lock
);
302 void get_selected_items_playlist_proc(GtkTreeModel
*model
, GtkTreePath
*path
, GtkTreeIter
*iter
, gpointer data
)
304 int idx
, *list
= (int*)data
;
305 gtk_tree_model_get(model
, iter
, 7, &idx
, -1);
306 list
[list
[0] + 1] = idx
;
307 list
[0] = list
[0] + 1;
310 static int* get_selected_items_playlist(omnplay_instance_t
* app
)
313 GtkTreeSelection
*selection
;
315 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(app
->playlist_grid
));
318 list
= (int*)malloc(sizeof(int) * (MAX_PLAYLIST_ITEMS
+ 1));
319 memset(list
, 0, sizeof(int) * (MAX_PLAYLIST_ITEMS
+ 1));
321 gtk_tree_selection_selected_foreach(
323 get_selected_items_playlist_proc
,
336 static int idx_in_players_range(omnplay_instance_t
* app
, int idx
)
340 for(i
= 0; i
< app
->players
.count
&& !r
; i
++)
344 a
= app
->players
.item
[i
].playlist_start
;
345 b
= app
->players
.item
[i
].playlist_length
;
352 if(idx
>= a
&& idx
<= b
) r
= 1;
358 static int idxs_in_players_range(omnplay_instance_t
* app
, int start
, int stop
)
362 for(i
= 0; i
< app
->players
.count
&& !r
; i
++)
366 a
= app
->players
.item
[i
].playlist_start
;
367 b
= app
->players
.item
[i
].playlist_length
;
374 #define IN_RANGE(A,B,C) (A <= C && C <= B)
375 if( IN_RANGE(a
,b
,start
) ||
376 IN_RANGE(a
,b
,stop
) ||
377 IN_RANGE(start
,stop
,a
) ||
378 IN_RANGE(start
,stop
,b
))
385 static void omnplay_playlist_block(omnplay_instance_t
* app
, control_buttons_t button
)
387 int start
, stop
, r
, i
;
388 int* list
= get_selected_items_playlist(app
);
393 pthread_mutex_lock(&app
->playlist
.lock
);
394 pthread_mutex_lock(&app
->players
.lock
);
397 stop
= list
[list
[0]];
399 if(!idxs_in_players_range(app
, start
, stop
))
401 int loop
= (button
== BUTTON_PLAYLIST_BLOCK_LOOP
)?OMNPLAY_PLAYLIST_BLOCK_LOOP
:0;
403 /* update selected item */
404 for(i
= start
; i
<= stop
; i
++)
406 int t
= OMNPLAY_PLAYLIST_BLOCK_BODY
| loop
;
408 if(i
== start
) t
|= OMNPLAY_PLAYLIST_BLOCK_BEGIN
;
409 if(i
== stop
) t
|= OMNPLAY_PLAYLIST_BLOCK_END
;
411 app
->playlist
.item
[i
].type
= (playlist_item_type_t
)t
;
413 omnplay_playlist_draw_item(app
, i
);
416 /* update border items */
417 if(start
&& !(app
->playlist
.item
[start
- 1].type
& OMNPLAY_PLAYLIST_BLOCK_END
))
419 app
->playlist
.item
[start
- 1].type
= (playlist_item_type_t
)(OMNPLAY_PLAYLIST_BLOCK_END
420 | app
->playlist
.item
[start
- 1].type
);
421 omnplay_playlist_draw_item(app
, start
- 1);
423 if((stop
+ 1) < app
->playlist
.count
&& !(app
->playlist
.item
[stop
+ 1].type
& OMNPLAY_PLAYLIST_BLOCK_BEGIN
))
425 app
->playlist
.item
[stop
+ 1].type
= (playlist_item_type_t
)(OMNPLAY_PLAYLIST_BLOCK_BEGIN
426 | app
->playlist
.item
[stop
+ 1].type
);
427 omnplay_playlist_draw_item(app
, stop
+ 1);
431 g_warning("omnplay_playlist_block: range [%d %d] do OVERLAP player\n",
434 pthread_mutex_unlock(&app
->players
.lock
);
435 pthread_mutex_unlock(&app
->playlist
.lock
);
440 static int get_first_selected_item_playlist(omnplay_instance_t
* app
)
443 int* list
= get_selected_items_playlist(app
);
450 static int get_playlist_block(omnplay_instance_t
* app
, int idx
, int* start_ptr
, int* stop_ptr
)
454 for(start
= idx
; start
>= 0; start
--)
455 if(app
->playlist
.item
[start
].type
& OMNPLAY_PLAYLIST_BLOCK_BEGIN
)
458 for(stop
= idx
; stop
< app
->playlist
.count
; stop
++)
459 if(app
->playlist
.item
[stop
].type
& OMNPLAY_PLAYLIST_BLOCK_END
)
462 g_warning("get_playlist_block: range %d -> %d\n", start
, stop
);
464 /* check block range */
465 if(start
>= 0 && stop
< app
->playlist
.count
)
469 return (stop
- start
+ 1);
475 static omnplay_player_t
*get_player_at_pos(omnplay_instance_t
* app
, int pos
)
477 /* check player range */
478 if(app
->playlist
.item
[pos
].player
> -1 && app
->playlist
.item
[pos
].player
< app
->players
.count
)
479 return &app
->players
.item
[app
->playlist
.item
[pos
].player
];
484 static void omnplay_playlist_delete_items(omnplay_instance_t
* app
, int* idxs
, int count
, int sel
)
488 pthread_mutex_lock(&app
->playlist
.lock
);
489 pthread_mutex_lock(&app
->players
.lock
);
491 for(j
= 0; j
< count
; j
++)
495 /* fix block types */
496 if( app
->playlist
.item
[idx
].type
!= OMNPLAY_PLAYLIST_ITEM_BLOCK_BODY
&&
497 app
->playlist
.item
[idx
].type
!= OMNPLAY_PLAYLIST_ITEM_LOOP_BODY
)
500 app
->playlist
.item
[idx
- 1].type
= (playlist_item_type_t
)(app
->playlist
.item
[idx
- 1].type
|
501 OMNPLAY_PLAYLIST_BLOCK_END
);
502 if(idx
+ 1 < app
->playlist
.count
)
503 app
->playlist
.item
[idx
+ 1].type
= (playlist_item_type_t
)(app
->playlist
.item
[idx
+ 1].type
|
504 OMNPLAY_PLAYLIST_BLOCK_BEGIN
);
507 /* shift playlist items */
510 &app
->playlist
.item
[idx
],
511 &app
->playlist
.item
[idx
+ 1],
512 (app
->playlist
.count
- idx
- 1) * sizeof(playlist_item_t
)
515 /* decrement items count */
516 app
->playlist
.count
--;
518 /* increment servers indexes */
519 for(i
= 0; i
< app
->players
.count
; i
++)
520 if(app
->players
.item
[i
].playlist_start
>= idx
)
521 app
->players
.item
[i
].playlist_start
--;
526 /* redraw playlist */
527 omnplay_playlist_draw(app
);
533 path
= gtk_tree_path_new_from_indices(idxs
[0], -1);
534 gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(app
->playlist_grid
)), path
);
535 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app
->playlist_grid
), path
, NULL
, FALSE
);
536 gtk_tree_path_free(path
);
539 pthread_mutex_unlock(&app
->players
.lock
);
540 pthread_mutex_unlock(&app
->playlist
.lock
);
543 static void omnplay_playlist_item_del(omnplay_instance_t
* app
)
548 list
= get_selected_items_playlist(app
);
551 list2
= (int*)malloc(sizeof(int) * list
[0]);
553 for(i
= 0, c
= 0; i
< list
[0]; i
++)
555 /* check for playing block */
556 if(idx_in_players_range(app
, list
[i
+ 1]))
560 list2
[c
++] = list
[i
+ 1];
564 omnplay_playlist_delete_items(app
, list2
, c
, 1);
570 static int omnplay_playlist_insert_check(omnplay_instance_t
* app
, int idx
, playlist_item_type_t
* t
)
572 *t
= OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE
;
574 /* before or after playlist */
575 if(!idx
|| idx
== app
->playlist
.count
)
578 /* check for block borders */
579 if( app
->playlist
.item
[idx
- 1].type
& OMNPLAY_PLAYLIST_BLOCK_END
&&
580 app
->playlist
.item
[idx
+ 0].type
& OMNPLAY_PLAYLIST_BLOCK_BEGIN
)
583 /* check for playing block */
584 if(idx_in_players_range(app
, idx
))
587 if(app
->playlist
.item
[idx
].type
& OMNPLAY_PLAYLIST_BLOCK_LOOP
)
588 *t
= OMNPLAY_PLAYLIST_ITEM_LOOP_BODY
;
590 *t
= OMNPLAY_PLAYLIST_ITEM_BLOCK_BODY
;
595 static void omnplay_playlist_insert_items(omnplay_instance_t
* app
, int idx
,
596 playlist_item_t
* items
, int count
)
601 pthread_mutex_lock(&app
->playlist
.lock
);
602 pthread_mutex_lock(&app
->players
.lock
);
604 /* shift playlist items */
607 &app
->playlist
.item
[idx
+ count
],
608 &app
->playlist
.item
[idx
],
609 (app
->playlist
.count
- idx
) * sizeof(playlist_item_t
)
615 &app
->playlist
.item
[idx
],
617 count
* sizeof(playlist_item_t
)
620 /* increment servers indexes */
621 for(i
= 0; i
< app
->players
.count
; i
++)
622 if(app
->players
.item
[i
].playlist_start
>= idx
)
623 app
->players
.item
[i
].playlist_start
+= idx
;
625 /* increment items count */
626 app
->playlist
.count
+= count
;
628 /* redraw playlist */
629 omnplay_playlist_draw(app
);
632 path
= gtk_tree_path_new_from_indices(idx
+ count
, -1);
633 gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(app
->playlist_grid
)), path
);
634 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app
->playlist_grid
), path
, NULL
, FALSE
);
635 gtk_tree_path_free(path
);
637 pthread_mutex_unlock(&app
->players
.lock
);
638 pthread_mutex_unlock(&app
->playlist
.lock
);
641 static void omnplay_playlist_item_add(omnplay_instance_t
* app
, int after
)
644 playlist_item_t item
;
645 playlist_item_type_t t
;
647 /* find insert position */
648 idx
= get_first_selected_item_playlist(app
);
654 if(!omnplay_playlist_insert_check(app
, idx
, &t
))
657 g_warning("allowed insert into idx=%d\n", idx
);
660 memset(&item
, 0, sizeof(playlist_item_t
));
661 if(ui_playlist_item_dialog(app
, &item
))
663 omnplay_library_normalize_item(app
, &item
);
665 omnplay_playlist_insert_items(app
, idx
, &item
, 1);
669 static void omnplay_playlist_item_edit(omnplay_instance_t
* app
)
672 playlist_item_t item
;
674 /* find insert position */
675 idx
= get_first_selected_item_playlist(app
);
680 /* check for playing block */
681 if(idx_in_players_range(app
, idx
))
684 item
= app
->playlist
.item
[idx
];
686 if(ui_playlist_item_dialog(app
, &item
))
688 omnplay_library_normalize_item(app
, &item
);
689 app
->playlist
.item
[idx
] = item
;
690 omnplay_playlist_draw_item(app
, idx
);
694 static void omnplay_ctl(omnplay_instance_t
* app
, control_buttons_t button
)
697 int idx
, start
, stop
;
698 omnplay_player_t
*player
;
700 pthread_mutex_lock(&app
->playlist
.lock
);
702 idx
= get_first_selected_item_playlist(app
);
706 pthread_mutex_unlock(&app
->playlist
.lock
);
710 g_warning("cue: selected item is %d\n", idx
);
712 if(get_playlist_block(app
, idx
, &start
, &stop
) < 0)
714 pthread_mutex_unlock(&app
->playlist
.lock
);
718 g_warning("cue: range %d -> %d\n", start
, stop
);
720 player
= get_player_at_pos(app
, start
);
724 pthread_mutex_unlock(&app
->playlist
.lock
);
728 pthread_mutex_lock(&app
->players
.lock
);
730 if(BUTTON_PLAYER_STOP
== button
|| BUTTON_PLAYER_CUE
== button
)
733 OmPlrStop((OmPlrHandle
)player
->handle
);
735 /* detach previous clips */
736 player
->playlist_length
= -1;
737 OmPlrDetachAllClips((OmPlrHandle
)player
->handle
);
740 if(BUTTON_PLAYER_CUE
== button
)
744 /* Attach clips to timeline */
745 for(i
= start
, c
= 0, o
= 0; i
<= stop
; i
++)
750 clip
.maxMsTracks
= 0;
751 clip
.size
= sizeof(clip
);
752 r
= OmPlrClipGetInfo((OmPlrHandle
)player
->handle
, app
->playlist
.item
[i
].id
, &clip
);
758 g_warning("OmPlrClipGetInfo(%s): firstFrame=%d, lastFrame=%d\n",
759 app
->playlist
.item
[i
].id
, clip
.firstFrame
, clip
.lastFrame
);
761 /* should we fix playlist clip timings */
763 app
->playlist
.item
[i
].in
>= clip
.firstFrame
&&
764 app
->playlist
.item
[i
].in
+ app
->playlist
.item
[i
].dur
<= clip
.lastFrame
) ||
765 !app
->playlist
.item
[i
].dur
)
767 g_warning("cue: item [%s] will be updated [%d;%d]->[%d;%d]\n",
768 app
->playlist
.item
[i
].id
,
769 app
->playlist
.item
[i
].in
, app
->playlist
.item
[i
].dur
,
770 clip
.firstFrame
, clip
.lastFrame
- clip
.firstFrame
);
772 app
->playlist
.item
[i
].in
= clip
.firstFrame
;
773 app
->playlist
.item
[i
].dur
= clip
.lastFrame
- clip
.firstFrame
;
774 omnplay_playlist_draw_item(app
, i
);
777 r
= OmPlrAttach((OmPlrHandle
)player
->handle
,
778 app
->playlist
.item
[i
].id
,
779 app
->playlist
.item
[i
].in
,
780 app
->playlist
.item
[i
].in
+ app
->playlist
.item
[i
].dur
,
781 0, omPlrShiftModeAfter
, &l
);
786 g_warning("cue: failed with %d, %s\n", r
, OmPlrGetErrorString((OmPlrError
)r
));
787 app
->playlist
.item
[i
].omn_idx
= -1;
788 app
->playlist
.item
[i
].omn_offset
= -1;
789 app
->playlist
.item
[i
].error
|= PLAYLIST_ITEM_ERROR_CUE
;
793 app
->playlist
.item
[i
].omn_idx
= c
;
794 app
->playlist
.item
[i
].omn_offset
= o
;
795 app
->playlist
.item
[i
].error
&= 0xF ^ PLAYLIST_ITEM_ERROR_CUE
;
797 /* save selected item offset */
801 o
+= app
->playlist
.item
[i
].dur
;
809 /* Set timeline min/max */
810 OmPlrSetMinPosMin((OmPlrHandle
)player
->handle
);
811 OmPlrSetMaxPosMax((OmPlrHandle
)player
->handle
);
813 /* Set timeline position */
815 hs
.size
= sizeof(OmPlrStatus
);
816 OmPlrGetPlayerStatus((OmPlrHandle
)player
->handle
, &hs
);
817 OmPlrSetPos((OmPlrHandle
)player
->handle
, hs
.minPos
+ p
);
820 if(app
->playlist
.item
[start
].type
& OMNPLAY_PLAYLIST_BLOCK_LOOP
)
821 OmPlrLoop((OmPlrHandle
)player
->handle
, hs
.minPos
, hs
.maxPos
);
823 OmPlrLoop((OmPlrHandle
)player
->handle
, hs
.minPos
, hs
.minPos
);
825 player
->playlist_start
= start
;
826 player
->playlist_length
= stop
- start
+ 1;
829 OmPlrCuePlay((OmPlrHandle
)player
->handle
, 0.0);
833 if(BUTTON_PLAYER_PLAY
== button
)
836 OmPlrPlay((OmPlrHandle
)player
->handle
, 1.0);
839 if(BUTTON_PLAYER_PAUSE
== button
)
841 OmPlrPlay((OmPlrHandle
)player
->handle
, 0.0);
843 pthread_mutex_unlock(&app
->players
.lock
);
845 pthread_mutex_unlock(&app
->playlist
.lock
);
848 static void omnplay_playlist_item_swap(omnplay_instance_t
* app
, int dir
)
850 int sel
, a
, b
, e
= 1;
852 playlist_item_t item
;
854 /* find insert position */
855 sel
= get_first_selected_item_playlist(app
);
872 /* check for playing block */
873 if(idx_in_players_range(app
, a
) || idx_in_players_range(app
, b
))
876 pthread_mutex_lock(&app
->playlist
.lock
);
877 pthread_mutex_lock(&app
->players
.lock
);
880 item
= app
->playlist
.item
[a
];
881 app
->playlist
.item
[a
] = app
->playlist
.item
[b
];
882 app
->playlist
.item
[b
] = item
;
885 if(app
->playlist
.item
[a
].type
!= app
->playlist
.item
[b
].type
)
888 app
->playlist
.item
[a
].type
= OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE
;
889 app
->playlist
.item
[b
].type
= OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE
;
892 /* redraw main items */
893 omnplay_playlist_draw_item(app
, a
);
894 omnplay_playlist_draw_item(app
, b
);
896 /* fix block types */
899 app
->playlist
.item
[a
- 1].type
= (playlist_item_type_t
)(app
->playlist
.item
[a
- 1].type
|
900 OMNPLAY_PLAYLIST_BLOCK_END
);
901 omnplay_playlist_draw_item(app
, a
- 1);
903 if(b
+ 1 < app
->playlist
.count
&& !e
)
905 app
->playlist
.item
[b
+ 1].type
= (playlist_item_type_t
)(app
->playlist
.item
[b
+ 1].type
|
906 OMNPLAY_PLAYLIST_BLOCK_BEGIN
);
907 omnplay_playlist_draw_item(app
, b
+ 1);
911 path
= gtk_tree_path_new_from_indices(sel
, -1);
912 gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(app
->playlist_grid
)), path
);
913 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app
->playlist_grid
), path
, NULL
, FALSE
);
914 gtk_tree_path_free(path
);
916 pthread_mutex_unlock(&app
->players
.lock
);
917 pthread_mutex_unlock(&app
->playlist
.lock
);
920 static void omnplay_library_add(omnplay_instance_t
* app
, int after
)
923 playlist_item_t
* items
;
924 playlist_item_type_t t
;
926 /* find insert position */
927 idx
= get_first_selected_item_playlist(app
);
933 if(!omnplay_playlist_insert_check(app
, idx
, &t
))
936 items
= omnplay_library_get_selected(app
, &c
);
941 for(i
= 0; i
< c
; i
++)
946 omnplay_playlist_insert_items(app
, idx
, items
, c
);
951 static gboolean
omnplay_button_click(omnplay_instance_t
* app
, control_buttons_t button
)
955 case BUTTON_PLAYLIST_ITEM_ADD
:
956 omnplay_playlist_item_add(app
, 0);
958 case BUTTON_PLAYLIST_ITEM_DEL
:
959 omnplay_playlist_item_del(app
);
961 case BUTTON_PLAYLIST_ITEM_EDIT
:
962 omnplay_playlist_item_edit(app
);
964 case BUTTON_PLAYLIST_LOAD
:
965 omnplay_playlist_load(app
);
967 case BUTTON_PLAYLIST_SAVE
:
968 omnplay_playlist_save(app
);
970 case BUTTON_PLAYLIST_BLOCK_SINGLE
:
971 case BUTTON_PLAYLIST_BLOCK_LOOP
:
972 omnplay_playlist_block(app
, button
);
974 case BUTTON_PLAYLIST_ITEM_UP
:
975 omnplay_playlist_item_swap(app
, -1);
977 case BUTTON_PLAYLIST_ITEM_DOWN
:
978 omnplay_playlist_item_swap(app
, +1);
980 case BUTTON_PLAYER_CUE
:
981 case BUTTON_PLAYER_PLAY
:
982 case BUTTON_PLAYER_PAUSE
:
983 case BUTTON_PLAYER_STOP
:
984 omnplay_ctl(app
, button
);
986 case BUTTON_LIBRARY_ADD
:
987 omnplay_library_add(app
, 0);
989 case BUTTON_LIBRARY_REFRESH
:
990 omnplay_library_refresh(app
);
992 case BUTTON_LIBRARY_FIND
:
993 omnplay_library_search(app
, 0);
995 case BUTTON_LIBRARY_FIND_NEXT
:
996 omnplay_library_search(app
, 1);
1003 static gboolean
on_button_click(GtkWidget
*button
, gpointer user_data
)
1006 omnplay_instance_t
* app
= (omnplay_instance_t
*)user_data
;
1008 for(i
= 1; i
< BUTTON_LAST
; i
++)
1009 if(app
->buttons
[i
] == button
)
1010 return omnplay_button_click(app
, (control_buttons_t
)i
);
1015 static void omnplay_playlist_item_copy(omnplay_instance_t
* app
)
1019 list
= get_selected_items_playlist(app
);
1022 for(i
= 0; i
< list
[0]; i
++)
1023 app
->clipboard
.item
[i
] = app
->playlist
.item
[list
[i
+ 1]];
1024 app
->clipboard
.count
= list
[0];
1029 static void omnplay_playlist_item_paste(omnplay_instance_t
* app
, int after
)
1032 playlist_item_t
* items
;
1033 playlist_item_type_t t
;
1035 /* find insert position */
1036 idx
= get_first_selected_item_playlist(app
);
1042 if(!omnplay_playlist_insert_check(app
, idx
, &t
))
1046 if(app
->clipboard
.count
)
1048 for(i
= 0; i
< app
->clipboard
.count
; i
++)
1050 app
->clipboard
.item
[i
].type
= t
;
1051 app
->clipboard
.item
[i
].error
= 0;
1053 omnplay_playlist_insert_items(app
, idx
, app
->clipboard
.item
, app
->clipboard
.count
);
1057 static gboolean
on_playlist_grid_key(GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
)
1059 omnplay_instance_t
* app
= (omnplay_instance_t
*)data
;
1061 switch(event
->keyval
)
1065 if(event
->state
& GDK_CONTROL_MASK
)
1067 omnplay_playlist_item_copy(app
);
1073 if(event
->state
& GDK_CONTROL_MASK
)
1075 omnplay_playlist_item_paste(app
, 0);
1081 if(event
->state
& GDK_CONTROL_MASK
)
1083 omnplay_playlist_item_copy(app
);
1084 omnplay_playlist_item_del(app
);
1090 if(event
->state
& GDK_CONTROL_MASK
)
1092 omnplay_playlist_save(app
);
1098 if(event
->state
& GDK_CONTROL_MASK
)
1100 omnplay_playlist_load(app
);
1104 case GDK_KEY_uparrow
:
1105 if(event
->state
& GDK_CONTROL_MASK
)
1107 omnplay_playlist_item_swap(app
, -1);
1111 case GDK_KEY_downarrow
:
1112 if(event
->state
& GDK_CONTROL_MASK
)
1114 omnplay_playlist_item_swap(app
, -1);
1119 omnplay_ctl(app
, BUTTON_PLAYER_PLAY
);
1121 case GDK_KEY_Return
:
1122 omnplay_ctl(app
, BUTTON_PLAYER_CUE
);
1124 case GDK_KEY_Insert
:
1125 omnplay_playlist_item_add(app
, 0);
1127 case GDK_KEY_Delete
:
1128 omnplay_playlist_item_del(app
);
1132 omnplay_playlist_item_edit(app
);
1139 static gboolean
on_library_grid_key(GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
)
1141 omnplay_instance_t
* app
= (omnplay_instance_t
*)data
;
1143 switch(event
->keyval
)
1147 if(event
->state
& GDK_CONTROL_MASK
)
1150 playlist_item_t
* items
;
1152 items
= omnplay_library_get_selected(app
, &count
);
1158 for(i
= 0; i
< count
; i
++)
1159 app
->clipboard
.item
[i
] = items
[i
];
1161 app
->clipboard
.count
= count
;
1169 if(event
->state
& GDK_CONTROL_MASK
)
1171 g_warning("CTRL+v\n");
1177 if(event
->state
& GDK_CONTROL_MASK
)
1179 g_warning("CTRL+x\n");
1183 case GDK_KEY_BackSpace
:
1184 omnplay_library_add(app
, 0);
1187 omnplay_library_refresh(app
);
1194 static gboolean
on_library_grid_button(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
1196 // g_warning("on_library_grid_button: event->button=%d, event->type=%d", event->button, event->type);
1198 if(event
->button
==1 && event
->type
==GDK_2BUTTON_PRESS
)
1200 omnplay_library_add((omnplay_instance_t
* )data
, 0);
1207 static gboolean
on_playlist_grid_button(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
1209 // g_warning("on_playlist_grid_button");
1211 if(event
->button
==1 && event
->type
==GDK_2BUTTON_PRESS
)
1213 omnplay_ctl((omnplay_instance_t
* )data
, BUTTON_PLAYER_CUE
);
1222 static void library_grid_drag_data_get_cb(GtkWidget
*widget
, GdkDragContext
*context
,
1223 GtkSelectionData
*selection_data
, guint info
, guint time
, gpointer userdata
)
1226 playlist_item_t
* items
;
1227 omnplay_instance_t
* app
= (omnplay_instance_t
*)userdata
;
1229 g_warning("library_grid_drag_data_get_cb");
1231 items
= omnplay_library_get_selected(app
, &c
);
1236 gtk_selection_data_set(selection_data
, selection_data
->target
, 8,
1237 (const guchar
*)items
, sizeof(playlist_item_t
) * c
);
1242 static void playlist_grid_drag_data_get_cb(GtkWidget
*widget
, GdkDragContext
*context
,
1243 GtkSelectionData
*selection_data
, guint info
, guint time
, gpointer userdata
)
1246 playlist_item_t
* items
;
1247 omnplay_instance_t
* app
= (omnplay_instance_t
*)userdata
;
1249 list
= get_selected_items_playlist(app
);
1252 /* clear delete flag */
1253 for(i
= 0; i
< app
->playlist
.count
; i
++)
1254 app
->playlist
.item
[i
].del
= 0;
1256 items
= (playlist_item_t
*)malloc(sizeof(playlist_item_t
) * list
[0]);
1257 for(i
= 0; i
< list
[0]; i
++)
1259 items
[i
] = app
->playlist
.item
[list
[i
+ 1]];
1260 if(context
->action
== GDK_ACTION_MOVE
)
1261 app
->playlist
.item
[list
[i
+ 1]].del
= 1;
1263 gtk_selection_data_set(selection_data
, selection_data
->target
, 8,
1264 (const guchar
*)items
, sizeof(playlist_item_t
) * list
[0]);
1270 static void library_grid_drag_begin_cb(GtkWidget
*widget
, GdkDragContext
*context
, gpointer userdata
)
1272 g_warning("library_grid_drag_begin_cb");
1273 gtk_drag_source_set_icon_stock(widget
, GTK_STOCK_DND
);
1276 static void playlist_grid_drag_begin_cb(GtkWidget
*widget
, GdkDragContext
*context
, gpointer userdata
)
1278 g_warning("playlist_grid_drag_begin_cb");
1279 gtk_drag_source_set_icon_stock(widget
, GTK_STOCK_DND
);
1282 static void playlist_grid_drag_data_received(GtkWidget
*widget
, GdkDragContext
*context
,
1283 gint x
, gint y
, GtkSelectionData
*selection_data
, guint info
, guint time
, gpointer userdata
)
1286 playlist_item_type_t t
;
1287 playlist_item_t
* items
;
1288 GtkTreePath
*path
= NULL
;
1289 omnplay_instance_t
* app
= (omnplay_instance_t
*)userdata
;
1291 g_warning("playlist_grid_drag_data_received: context->action=%d", context
->action
);
1293 items
= (playlist_item_t
*)gtk_selection_data_get_data(selection_data
);
1294 c
= gtk_selection_data_get_length(selection_data
);
1296 if(c
% sizeof(playlist_item_t
))
1298 g_warning("playlist_grid_drag_data_received: ODD ITEMS");
1302 c
/= sizeof(playlist_item_t
);
1304 if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget
), x
, y
, &path
, NULL
, NULL
, NULL
))
1306 idx
= gtk_tree_path_get_indices(path
)[0];
1307 gtk_tree_path_free(path
);
1309 g_warning("playlist_grid_drag_data_received: gtk_tree_path_get_indice[0]=%d", idx
);
1311 /* normalize, FIX ME */
1312 idx
--; if(idx
< 0) idx
= 0;
1315 idx
= app
->playlist
.count
;
1317 g_warning("playlist_grid_drag_data_received: idx=%d", idx
);
1319 if(omnplay_playlist_insert_check(app
, idx
, &t
))
1321 for(i
= 0; i
< c
; i
++)
1326 omnplay_playlist_insert_items(app
, idx
, items
, c
);
1330 /* Finish the drag */
1331 gtk_drag_finish(context
, TRUE
, FALSE
, time
);
1334 static void playlist_grid_drag_data_delete(GtkWidget
*widget
, GdkDragContext
*context
, gpointer userdata
)
1337 omnplay_instance_t
* app
= (omnplay_instance_t
*)userdata
;
1339 g_warning("playlist_grid_drag_data_delete");
1341 list
= (int*)malloc(sizeof(int) * MAX_PLAYLIST_ITEMS
);
1343 for(i
= 0, c
= 0; i
< app
->playlist
.count
; i
++)
1344 if(app
->playlist
.item
[i
].del
)
1345 if(!idx_in_players_range(app
, i
))
1349 g_warning("playlist_grid_drag_data_delete: i=%d, c=%d", i
, c
);
1353 omnplay_playlist_delete_items(app
, list
, c
, 0);
1358 void omnplay_init(omnplay_instance_t
* app
)
1361 pthread_mutexattr_t attr
;
1363 pthread_mutexattr_init(&attr
);
1364 pthread_mutexattr_settype(&attr
, PTHREAD_MUTEX_RECURSIVE
);
1366 gtk_signal_connect( GTK_OBJECT( app
->window
), "delete-event",
1367 GTK_SIGNAL_FUNC(on_main_window_delete_event
), app
);
1369 gtk_signal_connect( GTK_OBJECT( app
->window
), "destroy",
1370 GTK_SIGNAL_FUNC(on_main_window_destroy
), app
);
1372 gtk_widget_add_events(app
->playlist_grid
, GDK_BUTTON_PRESS_MASK
);
1373 gtk_widget_add_events(app
->playlist_grid
, GDK_KEY_PRESS_MASK
);
1374 gtk_signal_connect(GTK_OBJECT(app
->playlist_grid
), "key-press-event",
1375 GTK_SIGNAL_FUNC(on_playlist_grid_key
), app
);
1377 gtk_widget_add_events(app
->library_grid
, GDK_BUTTON_PRESS_MASK
);
1378 gtk_widget_add_events(app
->library_grid
, GDK_KEY_PRESS_MASK
);
1379 gtk_signal_connect(GTK_OBJECT(app
->library_grid
), "key-press-event",
1380 GTK_SIGNAL_FUNC(on_library_grid_key
), app
);
1382 gtk_signal_connect(GTK_OBJECT(app
->playlist_grid
), "button-press-event",
1383 GTK_SIGNAL_FUNC(on_playlist_grid_button
), app
);
1385 gtk_signal_connect(GTK_OBJECT(app
->library_grid
), "button-press-event",
1386 GTK_SIGNAL_FUNC(on_library_grid_button
), app
);
1389 pthread_mutex_init(&app
->players
.lock
, &attr
);
1390 pthread_mutex_init(&app
->playlist
.lock
, &attr
);
1391 pthread_mutex_init(&app
->library
.lock
, &attr
);
1393 /* create a omneon status thread */
1394 for(i
= 0; i
< app
->players
.count
; i
++)
1395 app
->players
.item
[i
].thread
= g_thread_create(
1396 omnplay_thread_proc
, &app
->players
.item
[i
], TRUE
, NULL
);
1398 /* attach buttons click */
1399 for(i
= 1; i
< BUTTON_LAST
; i
++)
1400 gtk_signal_connect(GTK_OBJECT(app
->buttons
[i
]), "clicked",
1401 GTK_SIGNAL_FUNC( on_button_click
), app
);
1404 omnplay_library_load(app
);
1406 pthread_mutexattr_destroy(&attr
);
1408 /* setup drag n drop source/target */
1409 static GtkTargetEntry drag_targets
[] = { { (char*) "application/playlist_item_t", 0, 0 } };
1411 gtk_drag_source_set(app
->library_grid
, GDK_BUTTON1_MASK
,
1412 drag_targets
, 1, (GdkDragAction
)(GDK_ACTION_COPY
));
1414 gtk_drag_source_set(app
->playlist_grid
, GDK_BUTTON1_MASK
,
1415 drag_targets
, 1, (GdkDragAction
)(GDK_ACTION_COPY
| GDK_ACTION_MOVE
));
1417 gtk_drag_dest_set(app
->playlist_grid
, GTK_DEST_DEFAULT_ALL
,
1418 drag_targets
, 1, (GdkDragAction
)(GDK_ACTION_COPY
| GDK_ACTION_MOVE
));
1420 g_signal_connect (app
->library_grid
, "drag_data_get", G_CALLBACK(library_grid_drag_data_get_cb
), app
);
1421 g_signal_connect (app
->playlist_grid
, "drag_data_get", G_CALLBACK(playlist_grid_drag_data_get_cb
), app
);
1422 g_signal_connect (app
->library_grid
, "drag_begin", G_CALLBACK(library_grid_drag_begin_cb
), app
);
1423 g_signal_connect (app
->playlist_grid
, "drag_begin", G_CALLBACK(playlist_grid_drag_begin_cb
), app
);
1424 g_signal_connect (app
->playlist_grid
, "drag_data_received", G_CALLBACK (playlist_grid_drag_data_received
), app
);
1425 g_signal_connect (app
->playlist_grid
, "drag_data_delete", G_CALLBACK (playlist_grid_drag_data_delete
), app
);
1428 void omnplay_release(omnplay_instance_t
* app
)
1434 for(i
= 0; i
< app
->players
.count
; i
++)
1435 /* create a omneon status thread */
1436 g_thread_join(app
->players
.item
[i
].thread
);
1439 pthread_mutex_destroy(&app
->players
.lock
);
1442 pthread_mutex_destroy(&app
->playlist
.lock
);
1445 omnplay_library_save(app
);
1447 /* destroy library lock */
1448 pthread_mutex_destroy(&app
->library
.lock
);
1451 void omnplay_playlist_normalize(omnplay_instance_t
* app
)
1455 /* normalize playlist */
1456 for(i
= 0; i
< app
->playlist
.count
; i
++)
1457 if(omnplay_library_normalize_item(app
, &app
->playlist
.item
[i
]))
1458 omnplay_playlist_draw_item(app
, i
);
1461 void omnplay_set_status(omnplay_instance_t
* app
, char* str
)
1463 gdk_threads_enter();
1464 gtk_label_set_text(GTK_LABEL(app
->status_label
), str
);
1466 gdk_threads_leave();