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 app
->library
.id_display_idx
= 0;
51 pthread_mutex_lock(&app
->players
.lock
);
53 r
= OmPlrClipGetFirst((OmPlrHandle
)app
->players
.item
[0].handle
, clip_name
, sizeof(clip_name
));
54 for(; c
< limit
&& !r
;)
57 clip_info
.maxMsTracks
= 0;
58 clip_info
.size
= sizeof(clip_info
);
60 r
= OmPlrClipGetInfo((OmPlrHandle
)app
->players
.item
[0].handle
, clip_name
, &clip_info
);
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
;
70 pthread_mutex_unlock(&app
->players
.lock
);
72 proc(app
, &items
[c
], data
);
73 pthread_mutex_lock(&app
->players
.lock
);
78 r
= OmPlrClipGetNext((OmPlrHandle
)app
->players
.item
[0].handle
, clip_name
, sizeof(clip_name
));
81 pthread_mutex_unlock(&app
->players
.lock
);
87 static gboolean
on_main_window_delete_event( GtkWidget
*widget
, GdkEvent
*event
, gpointer user_data
)
89 g_print ("delete event occurred [start]\n");
91 omnplay_release((omnplay_instance_t
*)user_data
);
93 g_print ("delete event occurred [finish]\n");
98 static void on_main_window_destroy( GtkWidget
*widget
, gpointer user_data
)
100 g_print ("destroy occurred\n");
104 omnplay_instance_t
* omnplay_create(int argc
, char** argv
)
107 omnplay_instance_t
* app
;
109 /* prepare application instance */
110 app
= (omnplay_instance_t
*)malloc(sizeof(omnplay_instance_t
));
111 memset(app
, 0, sizeof(omnplay_instance_t
));
113 /* load parameters from command line */
114 if(!omnplay_opt(argc
, argv
, app
) && app
->players
.count
)
115 app
->window
= ui_omnplay(app
);
122 void omnplay_destroy(omnplay_instance_t
* app
)
127 static int find_index_of_playlist_item(omnplay_instance_t
* app
, int start
, int idx
)
129 if(start
< 0 || start
>= app
->playlist
.count
)
134 if(app
->playlist
.item
[start
].omn_idx
== idx
)
137 if(app
->playlist
.item
[start
].type
& OMNPLAY_PLAYLIST_BLOCK_END
)
146 static void omnplay_update_status(omnplay_player_t
* player
, OmPlrStatus
*prev
, OmPlrStatus
*curr
, int *playlist_start_prev
)
149 char tc_cur
[32], tc_rem
[32], state
[32], status
[32];
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
;
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;
174 strcpy(status
, "OFFLINE");
177 /* update status in status page */
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
);
187 /* update remaining time */
189 pthread_mutex_lock(&player
->app
->playlist
.lock
);
190 pthread_mutex_lock(&player
->app
->players
.lock
);
192 /* check if playlist exist */
193 if(player
->playlist_length
)
195 /* clear remain on "previous" item */
196 if((curr
->currClipNum
!= prev
->currClipNum
&& 1 != prev
->numClips
) ||
197 (*playlist_start_prev
!= player
->playlist_start
))
200 idx
= find_index_of_playlist_item(player
->app
, *playlist_start_prev
, prev
->currClipNum
);
202 omnplay_playlist_draw_item_rem(player
->app
, idx
, tc_rem
);
205 /* update current item */
206 idx
= find_index_of_playlist_item(player
->app
, player
->playlist_start
, curr
->currClipNum
);
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
);
216 /* setup that value */
217 omnplay_playlist_draw_item_rem(player
->app
, idx
, tc_rem
);
220 pthread_mutex_unlock(&player
->app
->players
.lock
);
221 pthread_mutex_unlock(&player
->app
->playlist
.lock
);
226 memcpy(prev
, curr
, sizeof(OmPlrStatus
));
229 static void* omnplay_thread_proc(void* data
)
232 int playlist_start_prev
= 0;
233 OmPlrStatus st_curr
, st_prev
;
234 omnplay_player_t
* player
= (omnplay_player_t
*)data
;
236 g_warning("omnplay_thread_proc\n");
238 memset(&st_curr
, 0, sizeof(OmPlrStatus
));
239 memset(&st_prev
, 0, sizeof(OmPlrStatus
));
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
);
247 g_warning("ERROR: OmPlrOpen(%s, %s) failed with 0x%.8X\n",
248 player
->host
, player
->name
, r
);
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
);
258 /* setup directory */
259 if(player
->app
->players
.path
[0])
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
);
267 g_warning("ERROR: OmPlrClipSetDirectory(%s) failed with 0x%.8X\n",
268 player
->app
->players
.path
, r
);
270 pthread_mutex_lock(&player
->app
->players
.lock
);
271 OmPlrClose((OmPlrHandle
)player
->handle
);
272 pthread_mutex_unlock(&player
->app
->players
.lock
);
279 for(r
= 0 ; !player
->app
->f_exit
&& !r
;)
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
);
295 g_warning("ERROR: OmPlrGetPlayerStatus failed with 0x%.8X\n", r
);
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
));
304 pthread_mutex_lock(&player
->app
->players
.lock
);
305 OmPlrClose((OmPlrHandle
)player
->handle
);
306 pthread_mutex_unlock(&player
->app
->players
.lock
);
311 void get_selected_items_playlist_proc(GtkTreeModel
*model
, GtkTreePath
*path
, GtkTreeIter
*iter
, gpointer data
)
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;
319 int* omnplay_selected_idxs_playlist(omnplay_instance_t
* app
)
322 GtkTreeSelection
*selection
;
324 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(app
->playlist_grid
));
327 list
= (int*)malloc(sizeof(int) * (MAX_PLAYLIST_ITEMS
+ 1));
328 memset(list
, 0, sizeof(int) * (MAX_PLAYLIST_ITEMS
+ 1));
330 gtk_tree_selection_selected_foreach(
332 get_selected_items_playlist_proc
,
345 static int idx_in_players_range(omnplay_instance_t
* app
, int idx
)
349 for(i
= 0; i
< app
->players
.count
&& !r
; i
++)
353 a
= app
->players
.item
[i
].playlist_start
;
354 b
= app
->players
.item
[i
].playlist_length
;
361 if(idx
>= a
&& idx
<= b
) r
= 1;
367 static int idxs_in_players_range(omnplay_instance_t
* app
, int start
, int stop
)
371 for(i
= 0; i
< app
->players
.count
&& !r
; i
++)
375 a
= app
->players
.item
[i
].playlist_start
;
376 b
= app
->players
.item
[i
].playlist_length
;
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
))
394 static void omnplay_playlist_block(omnplay_instance_t
* app
, control_buttons_t button
)
396 int start
, stop
, r
, i
;
397 int* list
= omnplay_selected_idxs_playlist(app
);
402 pthread_mutex_lock(&app
->playlist
.lock
);
403 pthread_mutex_lock(&app
->players
.lock
);
406 stop
= list
[list
[0]];
408 if(!idxs_in_players_range(app
, start
, stop
))
410 int loop
= (button
== BUTTON_PLAYLIST_BLOCK_LOOP
)?OMNPLAY_PLAYLIST_BLOCK_LOOP
:0;
412 /* update selected item */
413 for(i
= start
; i
<= stop
; i
++)
415 int t
= OMNPLAY_PLAYLIST_BLOCK_BODY
| loop
;
417 if(i
== start
) t
|= OMNPLAY_PLAYLIST_BLOCK_BEGIN
;
418 if(i
== stop
) t
|= OMNPLAY_PLAYLIST_BLOCK_END
;
420 app
->playlist
.item
[i
].type
= (playlist_item_type_t
)t
;
422 omnplay_playlist_draw_item(app
, i
);
425 /* update border items */
426 if(start
&& !(app
->playlist
.item
[start
- 1].type
& OMNPLAY_PLAYLIST_BLOCK_END
))
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);
432 if((stop
+ 1) < app
->playlist
.count
&& !(app
->playlist
.item
[stop
+ 1].type
& OMNPLAY_PLAYLIST_BLOCK_BEGIN
))
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);
440 g_warning("omnplay_playlist_block: range [%d %d] do OVERLAP player\n",
443 pthread_mutex_unlock(&app
->players
.lock
);
444 pthread_mutex_unlock(&app
->playlist
.lock
);
449 static int get_first_selected_item_playlist(omnplay_instance_t
* app
)
452 int* list
= omnplay_selected_idxs_playlist(app
);
459 static int get_playlist_block(omnplay_instance_t
* app
, int idx
, int* start_ptr
, int* stop_ptr
)
463 for(start
= idx
; start
>= 0; start
--)
464 if(app
->playlist
.item
[start
].type
& OMNPLAY_PLAYLIST_BLOCK_BEGIN
)
467 for(stop
= idx
; stop
< app
->playlist
.count
; stop
++)
468 if(app
->playlist
.item
[stop
].type
& OMNPLAY_PLAYLIST_BLOCK_END
)
471 g_warning("get_playlist_block: range %d -> %d\n", start
, stop
);
473 /* check block range */
474 if(start
>= 0 && stop
< app
->playlist
.count
)
478 return (stop
- start
+ 1);
484 static omnplay_player_t
*get_player_at_pos(omnplay_instance_t
* app
, int pos
)
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
];
493 static void omnplay_playlist_delete_items(omnplay_instance_t
* app
, int* idxs
, int count
, int sel
)
497 pthread_mutex_lock(&app
->playlist
.lock
);
498 pthread_mutex_lock(&app
->players
.lock
);
500 for(j
= 0; j
< count
; j
++)
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
)
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
);
516 /* shift playlist items */
519 &app
->playlist
.item
[idx
],
520 &app
->playlist
.item
[idx
+ 1],
521 (app
->playlist
.count
- idx
- 1) * sizeof(playlist_item_t
)
524 /* decrement items count */
525 app
->playlist
.count
--;
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
--;
535 /* redraw playlist */
536 omnplay_playlist_draw(app
);
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
);
548 pthread_mutex_unlock(&app
->players
.lock
);
549 pthread_mutex_unlock(&app
->playlist
.lock
);
552 static void omnplay_playlist_item_del(omnplay_instance_t
* app
)
557 list
= omnplay_selected_idxs_playlist(app
);
560 list2
= (int*)malloc(sizeof(int) * list
[0]);
562 for(i
= 0, c
= 0; i
< list
[0]; i
++)
564 /* check for playing block */
565 if(idx_in_players_range(app
, list
[i
+ 1]))
569 list2
[c
++] = list
[i
+ 1];
573 omnplay_playlist_delete_items(app
, list2
, c
, 1);
579 void omnplay_playlist_relink(omnplay_instance_t
* app
)
584 pthread_mutex_lock(&app
->playlist
.lock
);
585 list
= omnplay_selected_idxs_playlist(app
);
588 for(i
= 0, c
= 0; i
< list
[0]; i
++)
590 /* check for playing block */
591 if(idx_in_players_range(app
, list
[i
+ 1]))
594 omnplay_library_relink_item(app
, &app
->playlist
.item
[list
[i
+ 1]]);
599 pthread_mutex_unlock(&app
->playlist
.lock
);
601 /* redraw playlist */
602 omnplay_playlist_draw(app
);
605 static int omnplay_playlist_insert_check(omnplay_instance_t
* app
, int idx
, playlist_item_type_t
* t
)
607 *t
= OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE
;
609 /* before or after playlist */
610 if(!idx
|| idx
== app
->playlist
.count
)
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
)
618 /* check for playing block */
619 if(idx_in_players_range(app
, idx
))
622 if(app
->playlist
.item
[idx
].type
& OMNPLAY_PLAYLIST_BLOCK_LOOP
)
623 *t
= OMNPLAY_PLAYLIST_ITEM_LOOP_BODY
;
625 *t
= OMNPLAY_PLAYLIST_ITEM_BLOCK_BODY
;
630 static void omnplay_playlist_insert_items(omnplay_instance_t
* app
, int idx
,
631 playlist_item_t
* items
, int count
)
636 pthread_mutex_lock(&app
->playlist
.lock
);
637 pthread_mutex_lock(&app
->players
.lock
);
639 /* shift playlist items */
642 &app
->playlist
.item
[idx
+ count
],
643 &app
->playlist
.item
[idx
],
644 (app
->playlist
.count
- idx
) * sizeof(playlist_item_t
)
650 &app
->playlist
.item
[idx
],
652 count
* sizeof(playlist_item_t
)
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
;
660 /* increment items count */
661 app
->playlist
.count
+= count
;
663 /* redraw playlist */
664 omnplay_playlist_draw(app
);
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
);
672 pthread_mutex_unlock(&app
->players
.lock
);
673 pthread_mutex_unlock(&app
->playlist
.lock
);
676 static void omnplay_playlist_item_add(omnplay_instance_t
* app
, int after
)
679 playlist_item_t item
;
680 playlist_item_type_t t
;
682 /* find insert position */
683 idx
= get_first_selected_item_playlist(app
);
689 if(!omnplay_playlist_insert_check(app
, idx
, &t
))
692 g_warning("allowed insert into idx=%d\n", idx
);
695 memset(&item
, 0, sizeof(playlist_item_t
));
696 if(ui_playlist_item_dialog(app
, &item
))
698 omnplay_library_normalize_item(app
, &item
);
700 omnplay_playlist_insert_items(app
, idx
, &item
, 1);
704 static void omnplay_playlist_item_edit(omnplay_instance_t
* app
)
707 playlist_item_t item
;
709 /* find insert position */
710 idx
= get_first_selected_item_playlist(app
);
715 /* check for playing block */
716 if(idx_in_players_range(app
, idx
))
719 item
= app
->playlist
.item
[idx
];
721 if(ui_playlist_item_dialog(app
, &item
))
723 omnplay_library_normalize_item(app
, &item
);
724 app
->playlist
.item
[idx
] = item
;
725 omnplay_playlist_draw_item(app
, idx
);
729 static void omnplay_ctl(omnplay_instance_t
* app
, control_buttons_t button
)
732 int idx
, start
, stop
;
733 omnplay_player_t
*player
;
735 pthread_mutex_lock(&app
->playlist
.lock
);
737 idx
= get_first_selected_item_playlist(app
);
741 pthread_mutex_unlock(&app
->playlist
.lock
);
745 g_warning("cue: selected item is %d\n", idx
);
747 if(get_playlist_block(app
, idx
, &start
, &stop
) < 0)
749 pthread_mutex_unlock(&app
->playlist
.lock
);
753 g_warning("cue: range %d -> %d\n", start
, stop
);
755 player
= get_player_at_pos(app
, start
);
759 pthread_mutex_unlock(&app
->playlist
.lock
);
763 pthread_mutex_lock(&app
->players
.lock
);
765 if(BUTTON_PLAYER_STOP
== button
|| BUTTON_PLAYER_CUE
== button
)
768 OmPlrStop((OmPlrHandle
)player
->handle
);
770 /* detach previous clips */
771 player
->playlist_length
= -1;
772 OmPlrDetachAllClips((OmPlrHandle
)player
->handle
);
775 if(BUTTON_PLAYER_CUE
== button
)
779 /* Attach clips to timeline */
780 for(i
= start
, c
= 0, o
= 0; i
<= stop
; i
++)
785 clip
.maxMsTracks
= 0;
786 clip
.size
= sizeof(clip
);
787 r
= OmPlrClipGetInfo((OmPlrHandle
)player
->handle
, app
->playlist
.item
[i
].id
, &clip
);
793 g_warning("OmPlrClipGetInfo(%s): firstFrame=%d, lastFrame=%d\n",
794 app
->playlist
.item
[i
].id
, clip
.firstFrame
, clip
.lastFrame
);
796 /* should we fix playlist clip timings */
798 app
->playlist
.item
[i
].in
>= clip
.firstFrame
&&
799 app
->playlist
.item
[i
].in
+ app
->playlist
.item
[i
].dur
<= clip
.lastFrame
) ||
800 !app
->playlist
.item
[i
].dur
)
802 g_warning("cue: item [%s] will be updated [%d;%d]->[%d;%d]\n",
803 app
->playlist
.item
[i
].id
,
804 app
->playlist
.item
[i
].in
, app
->playlist
.item
[i
].dur
,
805 clip
.firstFrame
, clip
.lastFrame
- clip
.firstFrame
);
807 app
->playlist
.item
[i
].in
= clip
.firstFrame
;
808 app
->playlist
.item
[i
].dur
= clip
.lastFrame
- clip
.firstFrame
;
809 omnplay_playlist_draw_item(app
, i
);
812 r
= OmPlrAttach((OmPlrHandle
)player
->handle
,
813 app
->playlist
.item
[i
].id
,
814 app
->playlist
.item
[i
].in
,
815 app
->playlist
.item
[i
].in
+ app
->playlist
.item
[i
].dur
,
816 0, omPlrShiftModeAfter
, &l
);
821 g_warning("cue: failed with %d, %s\n", r
, OmPlrGetErrorString((OmPlrError
)r
));
822 app
->playlist
.item
[i
].omn_idx
= -1;
823 app
->playlist
.item
[i
].omn_offset
= -1;
824 app
->playlist
.item
[i
].error
|= PLAYLIST_ITEM_ERROR_CUE
;
828 app
->playlist
.item
[i
].omn_idx
= c
;
829 app
->playlist
.item
[i
].omn_offset
= o
;
830 app
->playlist
.item
[i
].error
&= 0xF ^ PLAYLIST_ITEM_ERROR_CUE
;
832 /* save selected item offset */
836 o
+= app
->playlist
.item
[i
].dur
;
844 /* Set timeline min/max */
845 OmPlrSetMinPosMin((OmPlrHandle
)player
->handle
);
846 OmPlrSetMaxPosMax((OmPlrHandle
)player
->handle
);
848 /* Set timeline position */
850 hs
.size
= sizeof(OmPlrStatus
);
851 OmPlrGetPlayerStatus((OmPlrHandle
)player
->handle
, &hs
);
852 OmPlrSetPos((OmPlrHandle
)player
->handle
, hs
.minPos
+ p
);
855 if(app
->playlist
.item
[start
].type
& OMNPLAY_PLAYLIST_BLOCK_LOOP
)
856 OmPlrLoop((OmPlrHandle
)player
->handle
, hs
.minPos
, hs
.maxPos
);
858 OmPlrLoop((OmPlrHandle
)player
->handle
, hs
.minPos
, hs
.minPos
);
860 player
->playlist_start
= start
;
861 player
->playlist_length
= stop
- start
+ 1;
864 OmPlrCuePlay((OmPlrHandle
)player
->handle
, 0.0);
868 if(BUTTON_PLAYER_PLAY
== button
)
871 OmPlrPlay((OmPlrHandle
)player
->handle
, 1.0);
874 if(BUTTON_PLAYER_PAUSE
== button
)
876 OmPlrPlay((OmPlrHandle
)player
->handle
, 0.0);
878 pthread_mutex_unlock(&app
->players
.lock
);
880 pthread_mutex_unlock(&app
->playlist
.lock
);
883 static void omnplay_playlist_item_swap(omnplay_instance_t
* app
, int dir
)
885 int sel
, a
, b
, e
= 1;
887 playlist_item_t item
;
889 /* find insert position */
890 sel
= get_first_selected_item_playlist(app
);
907 /* check for playing block */
908 if(idx_in_players_range(app
, a
) || idx_in_players_range(app
, b
))
911 pthread_mutex_lock(&app
->playlist
.lock
);
912 pthread_mutex_lock(&app
->players
.lock
);
915 item
= app
->playlist
.item
[a
];
916 app
->playlist
.item
[a
] = app
->playlist
.item
[b
];
917 app
->playlist
.item
[b
] = item
;
920 if(app
->playlist
.item
[a
].type
!= app
->playlist
.item
[b
].type
)
923 app
->playlist
.item
[a
].type
= OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE
;
924 app
->playlist
.item
[b
].type
= OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE
;
927 /* redraw main items */
928 omnplay_playlist_draw_item(app
, a
);
929 omnplay_playlist_draw_item(app
, b
);
931 /* fix block types */
934 app
->playlist
.item
[a
- 1].type
= (playlist_item_type_t
)(app
->playlist
.item
[a
- 1].type
|
935 OMNPLAY_PLAYLIST_BLOCK_END
);
936 omnplay_playlist_draw_item(app
, a
- 1);
938 if(b
+ 1 < app
->playlist
.count
&& !e
)
940 app
->playlist
.item
[b
+ 1].type
= (playlist_item_type_t
)(app
->playlist
.item
[b
+ 1].type
|
941 OMNPLAY_PLAYLIST_BLOCK_BEGIN
);
942 omnplay_playlist_draw_item(app
, b
+ 1);
946 path
= gtk_tree_path_new_from_indices(sel
, -1);
947 gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(app
->playlist_grid
)), path
);
948 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app
->playlist_grid
), path
, NULL
, FALSE
);
949 gtk_tree_path_free(path
);
951 pthread_mutex_unlock(&app
->players
.lock
);
952 pthread_mutex_unlock(&app
->playlist
.lock
);
955 static void omnplay_library_add(omnplay_instance_t
* app
, int after
)
958 playlist_item_t
* items
;
959 playlist_item_type_t t
;
961 /* find insert position */
962 idx
= get_first_selected_item_playlist(app
);
968 if(!omnplay_playlist_insert_check(app
, idx
, &t
))
971 items
= omnplay_library_get_selected(app
, &c
);
976 for(i
= 0; i
< c
; i
++)
981 omnplay_playlist_insert_items(app
, idx
, items
, c
);
986 static gboolean
omnplay_button_click(omnplay_instance_t
* app
, control_buttons_t button
)
990 case BUTTON_PLAYLIST_ITEM_ADD
:
991 omnplay_playlist_item_add(app
, 0);
993 case BUTTON_PLAYLIST_ITEM_DEL
:
994 omnplay_playlist_item_del(app
);
996 case BUTTON_PLAYLIST_ITEM_EDIT
:
997 omnplay_playlist_item_edit(app
);
999 case BUTTON_PLAYLIST_LOAD
:
1000 omnplay_playlist_load(app
);
1002 case BUTTON_PLAYLIST_SAVE
:
1003 omnplay_playlist_save(app
);
1005 case BUTTON_PLAYLIST_BLOCK_SINGLE
:
1006 case BUTTON_PLAYLIST_BLOCK_LOOP
:
1007 omnplay_playlist_block(app
, button
);
1009 case BUTTON_PLAYLIST_ITEM_UP
:
1010 omnplay_playlist_item_swap(app
, -1);
1012 case BUTTON_PLAYLIST_ITEM_DOWN
:
1013 omnplay_playlist_item_swap(app
, +1);
1015 case BUTTON_PLAYER_CUE
:
1016 case BUTTON_PLAYER_PLAY
:
1017 case BUTTON_PLAYER_PAUSE
:
1018 case BUTTON_PLAYER_STOP
:
1019 omnplay_ctl(app
, button
);
1021 case BUTTON_LIBRARY_ADD
:
1022 omnplay_library_add(app
, 0);
1024 case BUTTON_LIBRARY_REFRESH
:
1025 omnplay_library_refresh(app
);
1027 case BUTTON_LIBRARY_FIND
:
1028 omnplay_library_search(app
, 0);
1030 case BUTTON_LIBRARY_FIND_NEXT
:
1031 omnplay_library_search(app
, 1);
1033 case BUTTON_PLAYLIST_RELINK
:
1034 omnplay_playlist_relink(app
);
1041 static gboolean
on_button_click(GtkWidget
*button
, gpointer user_data
)
1044 omnplay_instance_t
* app
= (omnplay_instance_t
*)user_data
;
1046 for(i
= 1; i
< BUTTON_LAST
; i
++)
1047 if(app
->buttons
[i
] == button
)
1048 return omnplay_button_click(app
, (control_buttons_t
)i
);
1053 static void omnplay_playlist_item_copy(omnplay_instance_t
* app
)
1057 list
= omnplay_selected_idxs_playlist(app
);
1060 for(i
= 0; i
< list
[0]; i
++)
1061 app
->clipboard
.item
[i
] = app
->playlist
.item
[list
[i
+ 1]];
1062 app
->clipboard
.count
= list
[0];
1067 static void omnplay_playlist_item_paste(omnplay_instance_t
* app
, int after
)
1070 playlist_item_t
* items
;
1071 playlist_item_type_t t
;
1073 /* find insert position */
1074 idx
= get_first_selected_item_playlist(app
);
1080 if(!omnplay_playlist_insert_check(app
, idx
, &t
))
1084 if(app
->clipboard
.count
)
1086 for(i
= 0; i
< app
->clipboard
.count
; i
++)
1088 app
->clipboard
.item
[i
].type
= t
;
1089 app
->clipboard
.item
[i
].error
= 0;
1091 omnplay_playlist_insert_items(app
, idx
, app
->clipboard
.item
, app
->clipboard
.count
);
1095 static gboolean
on_playlist_grid_key(GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
)
1097 omnplay_instance_t
* app
= (omnplay_instance_t
*)data
;
1099 switch(event
->keyval
)
1103 if(event
->state
& GDK_CONTROL_MASK
)
1105 omnplay_playlist_item_copy(app
);
1111 if(event
->state
& GDK_CONTROL_MASK
)
1113 omnplay_playlist_item_paste(app
, 0);
1119 if(event
->state
& GDK_CONTROL_MASK
)
1121 omnplay_playlist_item_copy(app
);
1122 omnplay_playlist_item_del(app
);
1128 if(event
->state
& GDK_CONTROL_MASK
)
1130 omnplay_playlist_save(app
);
1136 if(event
->state
& GDK_CONTROL_MASK
)
1138 omnplay_playlist_load(app
);
1142 case GDK_KEY_uparrow
:
1143 if(event
->state
& GDK_CONTROL_MASK
)
1145 omnplay_playlist_item_swap(app
, -1);
1149 case GDK_KEY_downarrow
:
1150 if(event
->state
& GDK_CONTROL_MASK
)
1152 omnplay_playlist_item_swap(app
, -1);
1157 omnplay_ctl(app
, BUTTON_PLAYER_PLAY
);
1159 case GDK_KEY_Return
:
1160 omnplay_ctl(app
, BUTTON_PLAYER_CUE
);
1162 case GDK_KEY_Insert
:
1163 omnplay_playlist_item_add(app
, 0);
1165 case GDK_KEY_Delete
:
1166 omnplay_playlist_item_del(app
);
1170 omnplay_playlist_item_edit(app
);
1177 static gboolean
on_library_grid_key(GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
)
1179 omnplay_instance_t
* app
= (omnplay_instance_t
*)data
;
1181 switch(event
->keyval
)
1185 if(event
->state
& GDK_CONTROL_MASK
)
1188 playlist_item_t
* items
;
1190 items
= omnplay_library_get_selected(app
, &count
);
1196 for(i
= 0; i
< count
; i
++)
1197 app
->clipboard
.item
[i
] = items
[i
];
1199 app
->clipboard
.count
= count
;
1207 if(event
->state
& GDK_CONTROL_MASK
)
1209 g_warning("CTRL+v\n");
1215 if(event
->state
& GDK_CONTROL_MASK
)
1217 g_warning("CTRL+x\n");
1221 case GDK_KEY_BackSpace
:
1222 omnplay_library_add(app
, 0);
1225 omnplay_library_refresh(app
);
1232 static gboolean
on_library_grid_button(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
1234 // g_warning("on_library_grid_button: event->button=%d, event->type=%d", event->button, event->type);
1236 if(event
->button
==1 && event
->type
==GDK_2BUTTON_PRESS
)
1238 omnplay_library_add((omnplay_instance_t
* )data
, 0);
1245 static gboolean
on_playlist_grid_button(GtkWidget
*widget
, GdkEventButton
*event
, gpointer data
)
1247 omnplay_instance_t
* app
= (omnplay_instance_t
*)data
;
1249 // g_warning("on_playlist_grid_button");
1251 if(event
->button
==1 && event
->type
==GDK_2BUTTON_PRESS
)
1253 omnplay_playlist_item_edit(app
);
1262 static void library_grid_drag_data_get_cb(GtkWidget
*widget
, GdkDragContext
*context
,
1263 GtkSelectionData
*selection_data
, guint info
, guint time
, gpointer userdata
)
1266 playlist_item_t
* items
;
1267 omnplay_instance_t
* app
= (omnplay_instance_t
*)userdata
;
1269 g_warning("library_grid_drag_data_get_cb");
1271 items
= omnplay_library_get_selected(app
, &c
);
1276 gtk_selection_data_set(selection_data
, selection_data
->target
, 8,
1277 (const guchar
*)items
, sizeof(playlist_item_t
) * c
);
1282 static void playlist_grid_drag_data_get_cb(GtkWidget
*widget
, GdkDragContext
*context
,
1283 GtkSelectionData
*selection_data
, guint info
, guint time
, gpointer userdata
)
1286 playlist_item_t
* items
;
1287 omnplay_instance_t
* app
= (omnplay_instance_t
*)userdata
;
1289 list
= omnplay_selected_idxs_playlist(app
);
1292 /* clear delete flag */
1293 for(i
= 0; i
< app
->playlist
.count
; i
++)
1294 app
->playlist
.item
[i
].del
= 0;
1296 items
= (playlist_item_t
*)malloc(sizeof(playlist_item_t
) * list
[0]);
1297 for(i
= 0; i
< list
[0]; i
++)
1299 items
[i
] = app
->playlist
.item
[list
[i
+ 1]];
1300 if(context
->action
== GDK_ACTION_MOVE
)
1301 app
->playlist
.item
[list
[i
+ 1]].del
= 1;
1303 gtk_selection_data_set(selection_data
, selection_data
->target
, 8,
1304 (const guchar
*)items
, sizeof(playlist_item_t
) * list
[0]);
1310 static void library_grid_drag_begin_cb(GtkWidget
*widget
, GdkDragContext
*context
, gpointer userdata
)
1312 g_warning("library_grid_drag_begin_cb");
1313 gtk_drag_source_set_icon_stock(widget
, GTK_STOCK_DND
);
1316 static void playlist_grid_drag_begin_cb(GtkWidget
*widget
, GdkDragContext
*context
, gpointer userdata
)
1318 g_warning("playlist_grid_drag_begin_cb");
1319 gtk_drag_source_set_icon_stock(widget
, GTK_STOCK_DND
);
1322 static void playlist_grid_drag_data_received(GtkWidget
*widget
, GdkDragContext
*context
,
1323 gint x
, gint y
, GtkSelectionData
*selection_data
, guint info
, guint time
, gpointer userdata
)
1326 playlist_item_type_t t
;
1327 playlist_item_t
* items
;
1328 GtkTreePath
*path
= NULL
;
1329 omnplay_instance_t
* app
= (omnplay_instance_t
*)userdata
;
1331 g_warning("playlist_grid_drag_data_received: context->action=%d", context
->action
);
1333 items
= (playlist_item_t
*)gtk_selection_data_get_data(selection_data
);
1334 c
= gtk_selection_data_get_length(selection_data
);
1336 if(c
% sizeof(playlist_item_t
))
1338 g_warning("playlist_grid_drag_data_received: ODD ITEMS");
1342 c
/= sizeof(playlist_item_t
);
1344 if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget
), x
, y
, &path
, NULL
, NULL
, NULL
))
1346 idx
= gtk_tree_path_get_indices(path
)[0];
1347 gtk_tree_path_free(path
);
1349 g_warning("playlist_grid_drag_data_received: gtk_tree_path_get_indice[0]=%d", idx
);
1351 /* normalize, FIX ME */
1352 idx
--; if(idx
< 0) idx
= 0;
1355 idx
= app
->playlist
.count
;
1357 g_warning("playlist_grid_drag_data_received: idx=%d", idx
);
1359 if(omnplay_playlist_insert_check(app
, idx
, &t
))
1361 for(i
= 0; i
< c
; i
++)
1366 omnplay_playlist_insert_items(app
, idx
, items
, c
);
1370 /* Finish the drag */
1371 gtk_drag_finish(context
, TRUE
, FALSE
, time
);
1374 static void playlist_grid_drag_data_delete(GtkWidget
*widget
, GdkDragContext
*context
, gpointer userdata
)
1377 omnplay_instance_t
* app
= (omnplay_instance_t
*)userdata
;
1379 g_warning("playlist_grid_drag_data_delete");
1381 list
= (int*)malloc(sizeof(int) * MAX_PLAYLIST_ITEMS
);
1383 for(i
= 0, c
= 0; i
< app
->playlist
.count
; i
++)
1384 if(app
->playlist
.item
[i
].del
)
1385 if(!idx_in_players_range(app
, i
))
1389 g_warning("playlist_grid_drag_data_delete: i=%d, c=%d", i
, c
);
1393 omnplay_playlist_delete_items(app
, list
, c
, 0);
1399 * http://www.mail-archive.com/mahogany-users@lists.sourceforge.net/msg00286.html
1401 static gboolean
playlist_grid_drag_motion(GtkWidget
*widget
, GdkDragContext
*context
,
1402 gint x
, gint y
, guint time
, gpointer data
)
1405 GtkWidget
*source_widget
;
1407 g_warning("playlist_grid_drag_motion");
1409 /* Get source widget and check if it is the same as the
1410 * destination widget.
1412 source_widget
= gtk_drag_get_source_widget(context
);
1413 same
= ((source_widget
== widget
) ? TRUE
: FALSE
);
1415 /* Put additional checks here, perhaps if same is FALSE then
1416 * set the default drag to GDK_ACTION_COPY.
1419 /* Say we just want to allow GDK_ACTION_MOVE, first we check
1420 * if that is in the list of allowed actions on the dc. If
1421 * so then we set it to that. Note if the user holds down the
1422 * ctrl key then the only flag in dc->actions will be
1423 * GDK_ACTION_COPY. The constraint for dc->actions is that
1424 * specified from the given actions in gtk_drag_dest_set() and
1425 * gtk_drag_source_set().
1429 if(context
->actions
== GDK_ACTION_MOVE
)
1430 gdk_drag_status(context
, GDK_ACTION_COPY
, time
);
1432 gdk_drag_status(context
, GDK_ACTION_MOVE
, time
);
1435 gdk_drag_status(context
, context
->actions
, time
);
1440 void omnplay_init(omnplay_instance_t
* app
)
1443 pthread_mutexattr_t attr
;
1445 pthread_mutexattr_init(&attr
);
1446 pthread_mutexattr_settype(&attr
, PTHREAD_MUTEX_RECURSIVE
);
1448 gtk_signal_connect( GTK_OBJECT( app
->window
), "delete-event",
1449 GTK_SIGNAL_FUNC(on_main_window_delete_event
), app
);
1451 gtk_signal_connect( GTK_OBJECT( app
->window
), "destroy",
1452 GTK_SIGNAL_FUNC(on_main_window_destroy
), app
);
1454 gtk_widget_add_events(app
->playlist_grid
, GDK_BUTTON_PRESS_MASK
);
1455 gtk_widget_add_events(app
->playlist_grid
, GDK_KEY_PRESS_MASK
);
1456 gtk_signal_connect(GTK_OBJECT(app
->playlist_grid
), "key-press-event",
1457 GTK_SIGNAL_FUNC(on_playlist_grid_key
), app
);
1459 gtk_widget_add_events(app
->library_grid
, GDK_BUTTON_PRESS_MASK
);
1460 gtk_widget_add_events(app
->library_grid
, GDK_KEY_PRESS_MASK
);
1461 gtk_signal_connect(GTK_OBJECT(app
->library_grid
), "key-press-event",
1462 GTK_SIGNAL_FUNC(on_library_grid_key
), app
);
1464 gtk_signal_connect(GTK_OBJECT(app
->playlist_grid
), "button-press-event",
1465 GTK_SIGNAL_FUNC(on_playlist_grid_button
), app
);
1467 gtk_signal_connect(GTK_OBJECT(app
->library_grid
), "button-press-event",
1468 GTK_SIGNAL_FUNC(on_library_grid_button
), app
);
1471 pthread_mutex_init(&app
->players
.lock
, &attr
);
1472 pthread_mutex_init(&app
->playlist
.lock
, &attr
);
1473 pthread_mutex_init(&app
->library
.lock
, &attr
);
1475 /* create a omneon status thread */
1476 for(i
= 0; i
< app
->players
.count
; i
++)
1477 app
->players
.item
[i
].thread
= g_thread_create(
1478 omnplay_thread_proc
, &app
->players
.item
[i
], TRUE
, NULL
);
1480 /* attach buttons click */
1481 for(i
= 1; i
< BUTTON_LAST
; i
++)
1482 gtk_signal_connect(GTK_OBJECT(app
->buttons
[i
]), "clicked",
1483 GTK_SIGNAL_FUNC( on_button_click
), app
);
1486 omnplay_library_load(app
);
1488 pthread_mutexattr_destroy(&attr
);
1490 /* setup drag n drop source/target */
1491 static GtkTargetEntry drag_targets
[] = { { (char*) "application/playlist_item_t", 0, 0 } };
1493 gtk_drag_source_set(app
->library_grid
, GDK_BUTTON1_MASK
,
1494 drag_targets
, 1, (GdkDragAction
)(GDK_ACTION_COPY
));
1496 gtk_drag_source_set(app
->playlist_grid
, GDK_BUTTON1_MASK
,
1497 drag_targets
, 1, (GdkDragAction
)(GDK_ACTION_COPY
| GDK_ACTION_MOVE
));
1499 gtk_drag_dest_set(app
->playlist_grid
, (GtkDestDefaults
)(GTK_DEST_DEFAULT_HIGHLIGHT
| GTK_DEST_DEFAULT_DROP
),
1500 drag_targets
, 1, (GdkDragAction
)(GDK_ACTION_COPY
| GDK_ACTION_MOVE
));
1502 g_signal_connect (app
->library_grid
, "drag_data_get", G_CALLBACK(library_grid_drag_data_get_cb
), app
);
1503 g_signal_connect (app
->playlist_grid
, "drag_data_get", G_CALLBACK(playlist_grid_drag_data_get_cb
), app
);
1504 g_signal_connect (app
->library_grid
, "drag_begin", G_CALLBACK(library_grid_drag_begin_cb
), app
);
1505 g_signal_connect (app
->playlist_grid
, "drag_begin", G_CALLBACK(playlist_grid_drag_begin_cb
), app
);
1506 g_signal_connect (app
->playlist_grid
, "drag_data_received", G_CALLBACK (playlist_grid_drag_data_received
), app
);
1507 g_signal_connect (app
->playlist_grid
, "drag_data_delete", G_CALLBACK (playlist_grid_drag_data_delete
), app
);
1508 g_signal_connect (app
->playlist_grid
, "drag_motion", G_CALLBACK (playlist_grid_drag_motion
), app
);
1511 void omnplay_release(omnplay_instance_t
* app
)
1517 for(i
= 0; i
< app
->players
.count
; i
++)
1518 /* create a omneon status thread */
1519 g_thread_join(app
->players
.item
[i
].thread
);
1522 pthread_mutex_destroy(&app
->players
.lock
);
1525 pthread_mutex_destroy(&app
->playlist
.lock
);
1528 omnplay_library_save(app
);
1530 /* destroy library lock */
1531 pthread_mutex_destroy(&app
->library
.lock
);
1534 void omnplay_playlist_normalize(omnplay_instance_t
* app
)
1538 /* normalize playlist */
1539 for(i
= 0; i
< app
->playlist
.count
; i
++)
1540 if(omnplay_library_normalize_item(app
, &app
->playlist
.item
[i
]))
1541 omnplay_playlist_draw_item(app
, i
);
1544 void omnplay_set_status(omnplay_instance_t
* app
, char* str
)
1546 gdk_threads_enter();
1547 gtk_label_set_text(GTK_LABEL(app
->status_label
), str
);
1549 gdk_threads_leave();