From cfbef984de0dc53c8b882d7706556656b9934de2 Mon Sep 17 00:00:00 2001 From: Maksym Veremeyenko Date: Thu, 5 Jul 2012 11:17:46 +0300 Subject: [PATCH] minimal playlist operating implemented --- src/instance.c | 44 ++---- src/instance.h | 21 ++- src/library.c | 6 +- src/playlist.c | 558 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- src/playlist.h | 188 +++++++++++++++++++- src/ui.c | 123 +++++++++++++ src/ui.h | 3 + 7 files changed, 875 insertions(+), 68 deletions(-) diff --git a/src/instance.c b/src/instance.c index 05400b3..2bd06b0 100644 --- a/src/instance.c +++ b/src/instance.c @@ -38,6 +38,7 @@ #include "timecode.h" #include "player.h" #include "library.h" +#include "playlist.h" GtkTargetEntry drag_targets[] = { { (char*) "application/playlist_item_t", 0, 0 } }; @@ -154,9 +155,6 @@ void instance_init(instance_t* app) int i; pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - gtk_signal_connect( GTK_OBJECT( app->window ), "delete-event", GTK_SIGNAL_FUNC(on_main_window_delete_event), app); @@ -182,6 +180,8 @@ void instance_init(instance_t* app) #endif /* create lock */ + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&app->players.lock, &attr); pthread_mutex_init(&app->playlist.lock, &attr); pthread_mutex_init(&app->library.lock, &attr); @@ -196,30 +196,11 @@ void instance_init(instance_t* app) gtk_signal_connect(GTK_OBJECT(app->buttons[i]), "clicked", GTK_SIGNAL_FUNC( on_button_click), app ); - /* load library */ + /* init library */ library_init(app); -#if 0 - /* setup drag n drop source/target */ - static GtkTargetEntry drag_targets[] = { { (char*) "application/playlist_item_t", 0, 0 } }; - - gtk_drag_source_set(app->library_grid, GDK_BUTTON1_MASK, - drag_targets, 1, (GdkDragAction)(GDK_ACTION_COPY)); - - gtk_drag_source_set(app->playlist_grid, GDK_BUTTON1_MASK, - drag_targets, 1, (GdkDragAction)(GDK_ACTION_COPY | GDK_ACTION_MOVE)); - - gtk_drag_dest_set(app->playlist_grid, (GtkDestDefaults)(GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP), - drag_targets, 1, (GdkDragAction)(GDK_ACTION_COPY | GDK_ACTION_MOVE)); - - g_signal_connect (app->library_grid, "drag_data_get", G_CALLBACK(library_grid_drag_data_get_cb), app); - g_signal_connect (app->playlist_grid, "drag_data_get", G_CALLBACK(playlist_grid_drag_data_get_cb), app); - g_signal_connect (app->library_grid, "drag_begin", G_CALLBACK(library_grid_drag_begin_cb), app); - g_signal_connect (app->playlist_grid, "drag_begin", G_CALLBACK(playlist_grid_drag_begin_cb), app); - g_signal_connect (app->playlist_grid, "drag_data_received", G_CALLBACK (playlist_grid_drag_data_received), app); - g_signal_connect (app->playlist_grid, "drag_data_delete", G_CALLBACK (playlist_grid_drag_data_delete), app); - g_signal_connect (app->playlist_grid, "drag_motion", G_CALLBACK (playlist_grid_drag_motion), app); -#endif + /* init playlist */ + playlist_init(app); }; void instance_release(instance_t* app) @@ -228,23 +209,22 @@ void instance_release(instance_t* app) app->f_exit = 1; - library_release(app); - /* stop unit monitoring threads */ for(i = 0; i < app->players.count; i++) player_stop(app, i); + /* release laylist */ + playlist_release(app); + + /* release library */ + library_release(app); + /* destroy lock */ pthread_mutex_destroy(&app->players.lock); /* destroy lock */ pthread_mutex_destroy(&app->playlist.lock); -#if 0 - /* load library */ - omnplay_library_save(app); -#endif - /* destroy library lock */ pthread_mutex_destroy(&app->library.lock); }; diff --git a/src/instance.h b/src/instance.h index c3e14ae..be3fa87 100644 --- a/src/instance.h +++ b/src/instance.h @@ -98,18 +98,25 @@ typedef enum playlist_item_type #define PLAYLIST_ITEM_ERROR_LIB 1 #define PLAYLIST_ITEM_ERROR_CUE 2 +/** + * @anchor playlist_item_t + * + * fu + */ typedef struct playlist_item { - char id[PATH_MAX]; - char title[PATH_MAX]; +/*@{*/ + char id[PATH_MAX]; /**< id of item, i.e. internal id or filename */ + char title[PATH_MAX]; /**< title */ int in; int dur; - int player; - playlist_item_type_t type; - int omn_idx; + int player; /**< player index that item currenly associated, -1 otherwise */ + playlist_item_type_t type; /**< block type of item */ + int int_idx; /**< internal playlist index */ int omn_offset; - int error; - int del; + int error; /**< flag indicates if any error occured with item */ + int del; /**< */ +/*@}*/ } playlist_item_t; #define MAX_PLAYERS 4 diff --git a/src/library.c b/src/library.c index 19e4637..c7b6013 100644 --- a/src/library.c +++ b/src/library.c @@ -327,7 +327,7 @@ static void library_get_selected_items_iter }; /* find numbers of items in list */ - for(l = 0; items[l].id[0]; l++); l--; + for(l = 0; items[l].id[0]; l++); g_warning("library_get_selected_items_iter: l=%d", l); /* realloc items */ @@ -362,9 +362,7 @@ playlist_item_t* library_get_selected_items(instance_t* app, int *count) &items); if(items) - { - for(; items[l].id[0]; l++); l--; - }; + for(; items[l].id[0]; l++); }; *count = l; diff --git a/src/playlist.c b/src/playlist.c index 4ff0a4b..a3be0e6 100644 --- a/src/playlist.c +++ b/src/playlist.c @@ -32,14 +32,229 @@ #include "ui.h" #include "timecode.h" -int playlist_item_index(instance_t* app, int start, int idx) +extern GtkTargetEntry drag_targets[]; + +static void playlist_get_selected_items_idx_iter +( + GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data +) +{ + int idx, l; + int **plist = (int**)data; + int *list = *plist; + + gtk_tree_model_get(model, iter, 7, &idx, -1); + + if(!list) + { + list = (int*)malloc(sizeof(int)); + list[0] = -1; + }; + + /* find numbers of items in list */ + for(l = 0; -1 != list[l]; l++); + g_warning("playlist_get_selected_items_idx_iter: l=%d", l); + + /* realloc items */ + list = (int*)realloc(list, (l + 2) * sizeof(int)); + + /* clean last item */ + list[l + 1] = -1; + + /* setup items */ + list[l] = idx; + + *plist = list; +}; + +int* playlist_get_selected_items_idx(instance_t* app, int *count) +{ + int* list = NULL, l = 0; + GtkTreeSelection *selection; + + selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(app->playlist_grid)); + if(selection) + { + gtk_tree_selection_selected_foreach( + selection, + playlist_get_selected_items_idx_iter, + &list); + + if(list) + for(l = 0; -1 != list[l]; l++); + }; + + *count = l; + return list; +}; + + +static void playlist_drag_data_get_cb(GtkWidget *widget, GdkDragContext *context, + GtkSelectionData *selection_data, guint info, guint time, gpointer userdata) +{ + int *list, i, c; + playlist_item_t* items; + instance_t* app = (instance_t*)userdata; + + g_warning("playlist_drag_data_get_cb"); + + list = playlist_get_selected_items_idx(app, &c); + if(!list) return; + + /* clear delete flag */ + for(i = 0; i < app->playlist.count; i++) + app->playlist.item[i].del = 0; + + items = (playlist_item_t*)malloc(sizeof(playlist_item_t) * c); + for(i = 0; i < c; i++) + { + items[i] = app->playlist.item[list[i]]; + if(context->action == GDK_ACTION_MOVE) + app->playlist.item[list[i]].del = 1; + } + gtk_selection_data_set(selection_data, selection_data->target, 8, + (const guchar *)items, sizeof(playlist_item_t) * c); + + free(items); + free(list); +}; + +static void playlist_drag_begin_cb(GtkWidget *widget, GdkDragContext *context, gpointer userdata) +{ + g_warning("playlist_drag_begin_cb"); + gtk_drag_source_set_icon_stock(widget, GTK_STOCK_DND); +}; + +static void playlist_drag_data_received(GtkWidget *widget, GdkDragContext *context, + gint x, gint y, GtkSelectionData *selection_data, guint info, guint time, gpointer userdata) +{ + int c, i, idx; + playlist_item_type_t t; + playlist_item_t* items; + GtkTreePath *path = NULL; + instance_t* app = (instance_t*)userdata; + + g_warning("playlist_drag_data_received: context->action=%d", context->action); + + items = (playlist_item_t*)gtk_selection_data_get_data(selection_data); + c = gtk_selection_data_get_length(selection_data); + + if(c % sizeof(playlist_item_t)) + { + g_warning("playlist_drag_data_received: ODD ITEMS"); + } + else + { + c /= sizeof(playlist_item_t); + + if(gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), x, y, &path, NULL, NULL, NULL)) + { + idx = gtk_tree_path_get_indices(path)[0]; + gtk_tree_path_free(path); + + g_warning("playlist_drag_data_received: gtk_tree_path_get_indice[0]=%d", idx); + + /* normalize, FIX ME */ + idx--; if(idx < 0) idx = 0; + } + else + idx = app->playlist.count; + + g_warning("playlist_drag_data_received: idx=%d", idx); + + if(playlist_insert_check(app, idx, &t)) + { + for(i = 0; i < c; i++) + { + items[i].type = t; + items[i].error = 0; + }; + playlist_insert_items(app, idx, items, c); + }; + }; + + /* Finish the drag */ + gtk_drag_finish(context, TRUE, FALSE, time); +}; + +static void playlist_drag_data_delete(GtkWidget *widget, GdkDragContext *context, gpointer userdata) +{ + int c, i, *list; + instance_t* app = (instance_t*)userdata; + + g_warning("playlist_drag_data_delete"); + + list = (int*)malloc(sizeof(int) * MAX_PLAYLIST_ITEMS); + + for(i = 0, c = 0; i < app->playlist.count; i++) + if(app->playlist.item[i].del) + if(!playlist_idx_cued(app, i, NULL)) + { + /* save index */ + list[c++] = i; + g_warning("playlist_drag_data_delete: i=%d, c=%d", i, c); + }; + + if(c) + playlist_delete_items(app, list, c, 0); + + free(list); +}; + +/* + * http://www.mail-archive.com/mahogany-users@lists.sourceforge.net/msg00286.html + */ +static gboolean playlist_drag_motion(GtkWidget *widget, GdkDragContext *context, + gint x, gint y, guint time, gpointer data) +{ + gboolean same; + GtkWidget *source_widget; + + g_warning("playlist_grid_drag_motion"); + + /* Get source widget and check if it is the same as the + * destination widget. + */ + source_widget = gtk_drag_get_source_widget(context); + same = ((source_widget == widget) ? TRUE : FALSE); + + /* Put additional checks here, perhaps if same is FALSE then + * set the default drag to GDK_ACTION_COPY. + */ + + /* Say we just want to allow GDK_ACTION_MOVE, first we check + * if that is in the list of allowed actions on the dc. If + * so then we set it to that. Note if the user holds down the + * ctrl key then the only flag in dc->actions will be + * GDK_ACTION_COPY. The constraint for dc->actions is that + * specified from the given actions in gtk_drag_dest_set() and + * gtk_drag_source_set(). + */ + if(same) + { + if(context->actions == GDK_ACTION_MOVE) + gdk_drag_status(context, GDK_ACTION_COPY, time); + else + gdk_drag_status(context, GDK_ACTION_MOVE, time); + } + else + gdk_drag_status(context, context->actions, time); + + return(TRUE); +} + + +int playlist_item_index(instance_t* app, int start, int int_idx) { if(start < 0 || start >= app->playlist.count) return -1; while(1) { - if(app->playlist.item[start].omn_idx == idx) + if(app->playlist.item[start].int_idx == int_idx) return start; if(app->playlist.item[start].type & PLAYLIST_BLOCK_END) @@ -52,6 +267,305 @@ int playlist_item_index(instance_t* app, int start, int idx) }; +void playlist_init(instance_t* app) +{ + gtk_drag_source_set(app->playlist_grid, GDK_BUTTON1_MASK, + drag_targets, 1, (GdkDragAction)(GDK_ACTION_COPY | GDK_ACTION_MOVE)); + + gtk_drag_dest_set(app->playlist_grid, (GtkDestDefaults)(GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP), + drag_targets, 1, (GdkDragAction)(GDK_ACTION_COPY | GDK_ACTION_MOVE)); + + g_signal_connect (app->playlist_grid, "drag_data_get", G_CALLBACK(playlist_drag_data_get_cb), app); + g_signal_connect (app->playlist_grid, "drag_begin", G_CALLBACK(playlist_drag_begin_cb), app); + g_signal_connect (app->playlist_grid, "drag_data_received", G_CALLBACK (playlist_drag_data_received), app); + g_signal_connect (app->playlist_grid, "drag_data_delete", G_CALLBACK (playlist_drag_data_delete), app); + g_signal_connect (app->playlist_grid, "drag_motion", G_CALLBACK (playlist_drag_motion), app); +}; + +void playlist_release(instance_t* app) +{ +}; + +int playlist_idx_cued(instance_t* app, int idx, int* player_idx) +{ + int i; + + for(i = 0; i < app->players.count; i++) + { + int a, b; + + a = app->players.item[i].playlist_start; + b = app->players.item[i].playlist_length; + + if(b <= 0) + continue; + + b = a + b - 1; + + if(idx >= a && idx <= b) + { + if(player_idx) + *player_idx = i; + return 1; + }; + }; + + return 0; +}; + +int playlist_range_cued(instance_t* app, int start, int stop) +{ + int i; + + for(i = start; i <= stop; i++) + if(playlist_idx_cued(app, i, NULL)) + return 1; + + return 0; +}; + +void playlist_block(instance_t* app, int loop) +{ + int start, stop, i, c; + int* list = playlist_get_selected_items_idx(app, &c); + + if(!list) + return; + + pthread_mutex_lock(&app->playlist.lock); + pthread_mutex_lock(&app->players.lock); + + start = list[0]; + stop = list[c - 1]; + + if(!playlist_range_cued(app, start, stop)) + { + /* update selected item */ + for(i = start; i <= stop; i++) + { + int t = PLAYLIST_BLOCK_BODY | loop; + + if(i == start) t |= PLAYLIST_BLOCK_BEGIN; + if(i == stop) t |= PLAYLIST_BLOCK_END; + + app->playlist.item[i].type = (playlist_item_type_t)t; + + ui_playlist_draw_item(app, i); + }; + + /* update border items */ + if(start && !(app->playlist.item[start - 1].type & PLAYLIST_BLOCK_END)) + { + app->playlist.item[start - 1].type = (playlist_item_type_t)(PLAYLIST_BLOCK_END + | app->playlist.item[start - 1].type); + ui_playlist_draw_item(app, start - 1); + }; + if((stop + 1) < app->playlist.count && !(app->playlist.item[stop + 1].type & PLAYLIST_BLOCK_BEGIN)) + { + app->playlist.item[stop + 1].type = (playlist_item_type_t)(PLAYLIST_BLOCK_BEGIN + | app->playlist.item[stop + 1].type); + ui_playlist_draw_item(app, stop + 1); + }; + } + else + g_warning("omnplay_playlist_block: range [%d %d] do OVERLAP player\n", + start, stop); + + pthread_mutex_unlock(&app->players.lock); + pthread_mutex_unlock(&app->playlist.lock); + + free(list); +}; + +int playlist_get_first_selected_item_idx(instance_t* app) +{ + int idx, c; + int* list = playlist_get_selected_items_idx(app, &c); + if(!list) + return -1; + idx = list[0]; + free(list); + return idx; +}; + +int playlist_get_block(instance_t* app, int idx, int* pstart, int* pstop) +{ + int start, stop; + + for(start = idx; start >= 0; start--) + if(app->playlist.item[start].type & PLAYLIST_BLOCK_BEGIN) + break; + + for(stop = idx; stop < app->playlist.count; stop++) + if(app->playlist.item[stop].type & PLAYLIST_BLOCK_END) + break; + + g_warning("playlist_get_block: range %d -> %d\n", start, stop); + + /* check block range */ + if(start >= 0 && stop < app->playlist.count) + { + *pstart = start; + *pstop = stop; + return (stop - start + 1); + }; + + return -1; +}; + +player_t *playlist_get_player_at_pos(instance_t* app, int pos) +{ + /* check player range */ + if(app->playlist.item[pos].player > -1 && app->playlist.item[pos].player < app->players.count) + return &app->players.item[app->playlist.item[pos].player]; + + return NULL; +}; + +void playlist_delete_items(instance_t* app, int* idxs, int count, int sel) +{ + int i, j, idx; + + pthread_mutex_lock(&app->playlist.lock); + pthread_mutex_lock(&app->players.lock); + + for(j = 0; j < count; j++) + { + idx = idxs[j] - j; + + /* fix block types */ + if( app->playlist.item[idx].type != PLAYLIST_ITEM_BLOCK_BODY && + app->playlist.item[idx].type != PLAYLIST_ITEM_LOOP_BODY) + { + if(idx) + app->playlist.item[idx - 1].type = (playlist_item_type_t)(app->playlist.item[idx - 1].type | + PLAYLIST_BLOCK_END); + if(idx + 1 < app->playlist.count) + app->playlist.item[idx + 1].type = (playlist_item_type_t)(app->playlist.item[idx + 1].type | + PLAYLIST_BLOCK_BEGIN); + }; + + /* shift playlist items */ + memmove + ( + &app->playlist.item[idx], + &app->playlist.item[idx + 1], + (app->playlist.count - idx - 1) * sizeof(playlist_item_t) + ); + + /* decrement items count */ + app->playlist.count--; + + /* increment servers indexes */ + for(i = 0; i < app->players.count; i++) + if(app->players.item[i].playlist_start >= idx) + app->players.item[i].playlist_start--; + }; + + /* redraw playlist */ + ui_playlist_draw(app); + + /* select */ + if(sel) + ui_playlist_select_item(app, idxs[0]); + + pthread_mutex_unlock(&app->players.lock); + pthread_mutex_unlock(&app->playlist.lock); +}; + +void playlist_delete_selected_items(instance_t* app) +{ + int i, cnt1, cnt2; + int *list1, *list2; + + list1 = playlist_get_selected_items_idx(app, &cnt1); + if(!list1) return; + + list2 = (int*)malloc(sizeof(int) * cnt1); + + for(i = 0, cnt2 = 0; i < cnt1; i++) + { + /* check for playing block */ + if(playlist_idx_cued(app, list1[i], NULL)) + continue; + + /* save index */ + list2[cnt2++] = list1[i]; + }; + + if(cnt2) + playlist_delete_items(app, list2, cnt2, 1); + + free(list2); + free(list1); +}; + +int playlist_insert_check(instance_t* app, int idx, playlist_item_type_t* t) +{ + *t = PLAYLIST_ITEM_BLOCK_SINGLE; + + /* before or after playlist */ + if(!idx || idx == app->playlist.count) + return 1; + + /* check for block borders */ + if( app->playlist.item[idx - 1].type & PLAYLIST_BLOCK_END && + app->playlist.item[idx + 0].type & PLAYLIST_BLOCK_BEGIN) + return 1; + + /* check for playing block */ + if(playlist_idx_cued(app, idx, NULL)) + return 0; + + if(app->playlist.item[idx].type & PLAYLIST_BLOCK_LOOP) + *t = PLAYLIST_ITEM_LOOP_BODY; + else + *t = PLAYLIST_ITEM_BLOCK_BODY; + + return 1; +}; + +void playlist_insert_items(instance_t* app, int idx, playlist_item_t* items, int count) +{ + int i; + + pthread_mutex_lock(&app->playlist.lock); + pthread_mutex_lock(&app->players.lock); + + /* shift playlist items */ + memmove + ( + &app->playlist.item[idx + count], + &app->playlist.item[idx], + (app->playlist.count - idx) * sizeof(playlist_item_t) + ); + + /* copy new items */ + memcpy + ( + &app->playlist.item[idx], + items, + count * sizeof(playlist_item_t) + ); + + /* increment servers indexes */ + for(i = 0; i < app->players.count; i++) + if(app->players.item[i].playlist_start >= idx) + app->players.item[i].playlist_start += idx; + + /* increment items count */ + app->playlist.count += count; + + /* redraw playlist */ + ui_playlist_draw(app); + + /* select */ + ui_playlist_select_item(app, idx); + + pthread_mutex_unlock(&app->players.lock); + pthread_mutex_unlock(&app->playlist.lock); +}; + #if 0 static int load_file_ply(omnplay_instance_t* app, char* filename) @@ -104,19 +618,19 @@ static int load_file_ply(omnplay_instance_t* app, char* filename) items[count].player = atol(CH) - 1; switch(b) { - case 1: items[count].type = OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE; break; - case 2: items[count].type = OMNPLAY_PLAYLIST_ITEM_LOOP_BEGIN; break; - case 3: items[count].type = OMNPLAY_PLAYLIST_ITEM_LOOP_BODY; break; - case 4: items[count].type = OMNPLAY_PLAYLIST_ITEM_LOOP_END; break; - case 6: items[count].type = OMNPLAY_PLAYLIST_ITEM_BLOCK_END; break; + case 1: items[count].type = PLAYLIST_ITEM_BLOCK_SINGLE; break; + case 2: items[count].type = PLAYLIST_ITEM_LOOP_BEGIN; break; + case 3: items[count].type = PLAYLIST_ITEM_LOOP_BODY; break; + case 4: items[count].type = PLAYLIST_ITEM_LOOP_END; break; + case 6: items[count].type = PLAYLIST_ITEM_BLOCK_END; break; case 0: if(!count) - items[count].type = OMNPLAY_PLAYLIST_ITEM_BLOCK_BEGIN; - else if(items[count - 1].type == OMNPLAY_PLAYLIST_ITEM_BLOCK_BEGIN || - items[count - 1].type == OMNPLAY_PLAYLIST_ITEM_BLOCK_BODY) - items[count].type = OMNPLAY_PLAYLIST_ITEM_BLOCK_BODY; + items[count].type = PLAYLIST_ITEM_BLOCK_BEGIN; + else if(items[count - 1].type == PLAYLIST_ITEM_BLOCK_BEGIN || + items[count - 1].type == PLAYLIST_ITEM_BLOCK_BODY) + items[count].type = PLAYLIST_ITEM_BLOCK_BODY; else - items[count].type = OMNPLAY_PLAYLIST_ITEM_BLOCK_BEGIN; + items[count].type = PLAYLIST_ITEM_BLOCK_BEGIN; break; default: if(b >= 1024) @@ -127,14 +641,14 @@ static int load_file_ply(omnplay_instance_t* app, char* filename) char* n; switch(items[count].type) { - case OMNPLAY_PLAYLIST_ITEM_BLOCK_BEGIN: n = "BLOCK_BEGIN"; break; - case OMNPLAY_PLAYLIST_ITEM_BLOCK_BODY: n = "BLOCK_BODY"; break; - case OMNPLAY_PLAYLIST_ITEM_BLOCK_END: n = "BLOCK_END"; break; - case OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE: n = "BLOCK_SINGLE"; break; - case OMNPLAY_PLAYLIST_ITEM_LOOP_BEGIN: n = "LOOP_BEGIN"; break; - case OMNPLAY_PLAYLIST_ITEM_LOOP_BODY: n = "LOOP_BODY"; break; - case OMNPLAY_PLAYLIST_ITEM_LOOP_END: n = "LOOP_END"; break; - case OMNPLAY_PLAYLIST_ITEM_LOOP_SINGLE: n = "LOOP_SINGLE"; break; + case PLAYLIST_ITEM_BLOCK_BEGIN: n = "BLOCK_BEGIN"; break; + case PLAYLIST_ITEM_BLOCK_BODY: n = "BLOCK_BODY"; break; + case PLAYLIST_ITEM_BLOCK_END: n = "BLOCK_END"; break; + case PLAYLIST_ITEM_BLOCK_SINGLE: n = "BLOCK_SINGLE"; break; + case PLAYLIST_ITEM_LOOP_BEGIN: n = "LOOP_BEGIN"; break; + case PLAYLIST_ITEM_LOOP_BODY: n = "LOOP_BODY"; break; + case PLAYLIST_ITEM_LOOP_END: n = "LOOP_END"; break; + case PLAYLIST_ITEM_LOOP_SINGLE: n = "LOOP_SINGLE"; break; }; fprintf(stderr, "src=[%s]\ndst=[idx=%d,block=%s,block_id=%d,in=%d,out=%d]\n", l, count, n, items[count].type, items[count].in, items[count].dur); @@ -328,7 +842,7 @@ void omnplay_playlist_draw(omnplay_instance_t* app) { char ch[3]; - if(OMNPLAY_PLAYLIST_BLOCK_BEGIN & app->playlist.item[i].type) + if(PLAYLIST_BLOCK_BEGIN & app->playlist.item[i].type) snprintf(ch, sizeof(ch), "%c", 'A' + app->playlist.item[i].player); else ch[0] = 0; @@ -388,7 +902,7 @@ static gboolean omnplay_playlist_draw_item_proc( if(i != item->idx) return FALSE; - if(OMNPLAY_PLAYLIST_BLOCK_BEGIN & app->playlist.item[i].type) + if(PLAYLIST_BLOCK_BEGIN & app->playlist.item[i].type) snprintf(ch, sizeof(ch), "%c", 'A' + app->playlist.item[i].player); else ch[0] = 0; diff --git a/src/playlist.h b/src/playlist.h index fe9e22b..7d0910b 100644 --- a/src/playlist.h +++ b/src/playlist.h @@ -27,11 +27,193 @@ extern "C" { #endif /* __cplusplus */ -/* - find local playlist index by given start search position value and remote playlist item index - used to associate remote item with local + +void playlist_init(instance_t* app); +void playlist_release(instance_t* app); + + +/** + * @anchor playlist_idx_cued + * + * function check if given index is in the any player usage + * + * @param[in] app application instance handle + * @param[in] idx index of playlist item + * @param[out] player_idx index of player where item used + * + * @return 1 for success and set player_idx to proper value and zero + * if item is not used in any player + * + * @{ + */ +int playlist_idx_cued(instance_t* app, int idx, int* player_idx); +/** @} */ + +/** + * @anchor playlist_range_cued + * + * function check if given range is in the any player usage + * + * @param[in] app application instance handle + * @param[in] start index of first playlist item + * @param[in] stop index of last playlist item + * + * @return 1 for success and zero if range is not used in any player + * + * @note idxs_in_players_range + * @{ + */ +int playlist_range_cued(instance_t* app, int start, int stop); +/** @} */ + + +/** + * @anchor playlist_insert_check + * + * function check if possible to insert into the current playlist position + * + * @param[in] app application instance handle + * @param[in] idx index of playlist to insert + * @param[out] t playlist type will be associated with insert position + * + * @return 1 for success and zero otherwise + * + * @{ + */ +int playlist_insert_check(instance_t* app, int idx, playlist_item_type_t* t); +/** @} */ + + +/** + * @anchor playlist_insert_items + * + * function check if possible to insert into the current playlist position + * + * @param[in] app application instance handle + * @param[in] idx index of playlist to insert + * @param[in] items list of playlist items + * @param[in] count number of items in the list of playlist + * + * @{ + */ +void playlist_insert_items(instance_t* app, int idx, playlist_item_t* items, int count); +/** @} */ + +/** + * @anchor playlist_get_selected_items_idx + * + * function tries to get list of indexes of selected items + * + * @param[in] app application instance handle + * @param[out] count number of items in list + * + * @return NULL if no items selected otherwise return pointer + * to the list of array of integers with items indexes selected + * and set count variable to the number of items in list + * + * @{ + */ +int* playlist_get_selected_items_idx(instance_t* app, int *count); +/** @} */ + +/** + * @anchor playlist_get_block + * + * function tries to find block range that include given playlist index + * + * @param[in] app application instance handle + * @param[in] idx playlist item index + * @param[out] pstart pointer where index of first item of block will be stored + * @param[out] pstart pointer where index of last item of block will be stored + * + * @return -1 if no range found, otherwise number of items in a block + * + * @{ + */ +int playlist_get_block(instance_t* app, int idx, int* pstart, int* pstop); +/** @} */ + + +/** + * @anchor playlist_get_player_at_pos + * + * function find a player associated with playlist item + * + * @param[in] app application instance handle + * @param[in] idx playlist item index + * + * @return NULL if item not assiciated with player, otherwise player struct + * + * @{ + */ +player_t *playlist_get_player_at_pos(instance_t* app, int pos); +/** @} */ + +/** + * @anchor playlist_get_first_selected_item_idx + * + * function tries to get index of first selected item + * + * @param[in] app application instance handle + * + * @return -1 if no items selected otherwise return index of + * selected item + * + * @{ + */ +int playlist_get_first_selected_item_idx(instance_t* app); +/** @} */ + + +/** + * @anchor playlist_delete_items + * + * function delete list of items + * + * @param[in] app application instance handle + * @param[in] idxs list of indexes to delete + * @param[in] count number of indexes in the list + * @param[in] sel flag used to change playlist selection + * on first item after delete operation + * + * @{ + */ +void playlist_delete_items(instance_t* app, int* idxs, int count, int sel); +/** @} */ + +/** + * @anchor playlist_delete_selected_items + * + * function delete selected items + * + * @param[in] app application instance handle + * + * @{ + */ +void playlist_delete_selected_items(instance_t* app); +/** @} */ + +/** + * @anchor playlist_item_index + * + * @{ */ int playlist_item_index(instance_t* app, int start, int idx); +/** @} */ + + +/** + * @anchor playlist_block + * + * function change block type of events + * + * @param[in] app application instance handle + * @param[in] loop flag indicating that loop type will be used + * + * @{ + */ +void playlist_block(instance_t* app, int loop); +/** @} */ #if 0 void omnplay_playlist_load(omnplay_instance_t* app); diff --git a/src/ui.c b/src/ui.c index ef556b7..b7c230e 100644 --- a/src/ui.c +++ b/src/ui.c @@ -35,6 +35,7 @@ #include "ui_buttons.h" #include "support.h" #include "timecode.h" +#include "playlist.h" typedef struct column_desc { @@ -861,3 +862,125 @@ void ui_playlist_draw_item_rem(instance_t* app, int idx, char* rem) gdk_flush(); gdk_threads_leave(); }; + +void ui_playlist_select_item(instance_t* app, int idx) +{ + GtkTreePath* path; + + path = gtk_tree_path_new_from_indices(idx, -1); + + gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(app->playlist_grid)), path); + gtk_tree_view_set_cursor(GTK_TREE_VIEW(app->playlist_grid), path, NULL, FALSE); + gtk_tree_path_free(path); +}; + +void ui_playlist_draw(instance_t* app) +{ + int i; + int sel; + char tc1[12], tc2[12]; + GtkListStore *list_store; + GtkTreeIter iter; + + sel = playlist_get_first_selected_item_idx(app); + + list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(app->playlist_grid))); + gtk_list_store_clear(list_store); + + pthread_mutex_lock(&app->playlist.lock); + + for(i = 0;i < app->playlist.count; i++) + { + char ch[3]; + + if(PLAYLIST_BLOCK_BEGIN & app->playlist.item[i].type) + snprintf(ch, sizeof(ch), "%c", 'A' + app->playlist.item[i].player); + else + ch[0] = 0; + + gtk_list_store_append(list_store, &iter); + + gtk_list_store_set(list_store, &iter, + 0, "", + 1, app->playlist.block_icons[app->playlist.item[i].type], + 2, ch, + 3, app->playlist.item[i].id, + 4, frames2tc(app->playlist.item[i].in, 25.0, tc1), + 5, frames2tc(app->playlist.item[i].dur, 25.0, tc2), + 6, app->playlist.item[i].title, + 7, i, + 8, (app->playlist.item[i].error != 0), + 9, (app->playlist.item[i].error & PLAYLIST_ITEM_ERROR_LIB)?"red":"orange", + -1 ); + } + + app->playlist.ver_prev = app->playlist.ver_curr; + + if(sel >= 0) + ui_playlist_select_item(app, sel); + + pthread_mutex_unlock(&app->playlist.lock); +}; + +typedef struct ui_playlist_draw_item_desc +{ + GtkListStore *list_store; + instance_t* app; + int idx; +} ui_playlist_draw_item_t; + +static gboolean ui_playlist_draw_item_iter +( + GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data +) +{ + int i; + char tc1[12], tc2[12]; + char ch[3]; + ui_playlist_draw_item_t* item = (ui_playlist_draw_item_t*)user_data; + instance_t* app = item->app; + + gtk_tree_model_get(model, iter, 7, &i, -1); + + if(i != item->idx) return FALSE; + + if(PLAYLIST_BLOCK_BEGIN & app->playlist.item[i].type) + snprintf(ch, sizeof(ch), "%c", 'A' + app->playlist.item[i].player); + else + ch[0] = 0; + + gtk_list_store_set(item->list_store, iter, + 0, "", + 1, app->playlist.block_icons[app->playlist.item[i].type], + 2, ch, + 3, app->playlist.item[i].id, + 4, frames2tc(app->playlist.item[i].in, 25.0, tc1), + 5, frames2tc(app->playlist.item[i].dur, 25.0, tc2), + 6, app->playlist.item[i].title, + 7, i, + 8, (app->playlist.item[i].error != 0), + 9, (app->playlist.item[i].error & PLAYLIST_ITEM_ERROR_LIB)?"red":"orange", + -1 ); + + return TRUE; +}; + +void ui_playlist_draw_item(instance_t* app, int idx) +{ + GtkListStore *list_store; + ui_playlist_draw_item_t item; + + list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(app->playlist_grid))); + + pthread_mutex_lock(&app->playlist.lock); + + item.idx = idx; + item.app = app; + item.list_store = list_store; + gtk_tree_model_foreach(GTK_TREE_MODEL(list_store), ui_playlist_draw_item_iter, &item); + + pthread_mutex_unlock(&app->playlist.lock); +}; diff --git a/src/ui.h b/src/ui.h index ed3d19b..4ed4492 100644 --- a/src/ui.h +++ b/src/ui.h @@ -30,7 +30,10 @@ extern "C" GtkWidget* ui_create(instance_t* app); void ui_update_player(player_t* player, char *tc_cur, char *tc_rem, char *state, char *status, char *clip); void ui_playlist_draw_item_rem(instance_t* app, int idx, char* rem); +void ui_playlist_draw_item(instance_t* app, int idx); +void ui_playlist_select_item(instance_t* app, int idx); //int ui_playlist_item_dialog(omnplay_instance_t* app, playlist_item_t* item); +void ui_playlist_draw(instance_t* app); #ifdef __cplusplus }; -- 1.7.4.4