2 * playlist.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.
28 #include <gdk/gdkkeysyms.h>
35 int playlist_item_index(instance_t
* app
, int start
, int idx
)
37 if(start
< 0 || start
>= app
->playlist
.count
)
42 if(app
->playlist
.item
[start
].omn_idx
== idx
)
45 if(app
->playlist
.item
[start
].type
& PLAYLIST_BLOCK_END
)
57 static int load_file_ply(omnplay_instance_t
* app
, char* filename
)
60 char *ID
, *CH
, *B
, *IN
, *OUT
, *DUR
, *REST
, *l
;
62 playlist_item_t
* items
;
64 /* allocate space for strings and items */
65 items
= malloc(sizeof(playlist_item_t
) * MAX_PLAYLIST_ITEMS
);
66 memset(items
, 0, sizeof(playlist_item_t
) * MAX_PLAYLIST_ITEMS
);
67 ID
= malloc(PATH_MAX
);
68 CH
= malloc(PATH_MAX
);
70 IN
= malloc(PATH_MAX
);
71 OUT
= malloc(PATH_MAX
);
72 DUR
= malloc(PATH_MAX
);
73 REST
= malloc(PATH_MAX
);
76 /* open and process file */
77 f
= fopen(filename
, "rt");
85 memset(l
, 0, PATH_MAX
);
86 fgets(l
, PATH_MAX
, f
);
89 if( (s
= strchr(l
, '\n')) ) *s
= 0;
90 if( (s
= strchr(l
, '\r')) ) *s
= 0;
91 if( (s
= strchr(l
, '\t')) ) *s
= 0;
93 /* check for empty line */
94 if(l
[0] && l
[0] != '#')
96 if (6 != sscanf(l
, "%128[^,],%128[^,],%128[^,],%128[^,],%128[^,],%128[^,],%s",
97 ID
, CH
, B
, IN
, OUT
, DUR
, REST
))
101 tc2frames(IN
, 25.0, &items
[count
].in
);
102 tc2frames(DUR
, 25.0, &items
[count
].dur
);
103 strncpy(items
[count
].id
, ID
, PATH_MAX
);
104 items
[count
].player
= atol(CH
) - 1;
107 case 1: items
[count
].type
= OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE
; break;
108 case 2: items
[count
].type
= OMNPLAY_PLAYLIST_ITEM_LOOP_BEGIN
; break;
109 case 3: items
[count
].type
= OMNPLAY_PLAYLIST_ITEM_LOOP_BODY
; break;
110 case 4: items
[count
].type
= OMNPLAY_PLAYLIST_ITEM_LOOP_END
; break;
111 case 6: items
[count
].type
= OMNPLAY_PLAYLIST_ITEM_BLOCK_END
; break;
114 items
[count
].type
= OMNPLAY_PLAYLIST_ITEM_BLOCK_BEGIN
;
115 else if(items
[count
- 1].type
== OMNPLAY_PLAYLIST_ITEM_BLOCK_BEGIN
||
116 items
[count
- 1].type
== OMNPLAY_PLAYLIST_ITEM_BLOCK_BODY
)
117 items
[count
].type
= OMNPLAY_PLAYLIST_ITEM_BLOCK_BODY
;
119 items
[count
].type
= OMNPLAY_PLAYLIST_ITEM_BLOCK_BEGIN
;
123 items
[count
].type
= b
- 1024;
128 switch(items
[count
].type
)
130 case OMNPLAY_PLAYLIST_ITEM_BLOCK_BEGIN
: n
= "BLOCK_BEGIN"; break;
131 case OMNPLAY_PLAYLIST_ITEM_BLOCK_BODY
: n
= "BLOCK_BODY"; break;
132 case OMNPLAY_PLAYLIST_ITEM_BLOCK_END
: n
= "BLOCK_END"; break;
133 case OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE
: n
= "BLOCK_SINGLE"; break;
134 case OMNPLAY_PLAYLIST_ITEM_LOOP_BEGIN
: n
= "LOOP_BEGIN"; break;
135 case OMNPLAY_PLAYLIST_ITEM_LOOP_BODY
: n
= "LOOP_BODY"; break;
136 case OMNPLAY_PLAYLIST_ITEM_LOOP_END
: n
= "LOOP_END"; break;
137 case OMNPLAY_PLAYLIST_ITEM_LOOP_SINGLE
: n
= "LOOP_SINGLE"; break;
139 fprintf(stderr
, "src=[%s]\ndst=[idx=%d,block=%s,block_id=%d,in=%d,out=%d]\n",
140 l
, count
, n
, items
[count
].type
, items
[count
].in
, items
[count
].dur
);
152 /* add loaded items to playlist */
155 pthread_mutex_lock(&app
->playlist
.lock
);
156 for(i
= 0; i
< count
&& app
->playlist
.count
+ 1 < MAX_PLAYLIST_ITEMS
; i
++)
158 omnplay_library_normalize_item(app
, &items
[i
]);
159 app
->playlist
.item
[app
->playlist
.count
++] = items
[i
];
161 app
->playlist
.ver_curr
++;
162 pthread_mutex_unlock(&app
->playlist
.lock
);
178 void omnplay_playlist_load(omnplay_instance_t
* app
)
182 GtkFileFilter
*filter
;
184 dialog
= gtk_file_chooser_dialog_new("Open File",
185 GTK_WINDOW (app
->window
),
186 GTK_FILE_CHOOSER_ACTION_OPEN
,
187 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
188 GTK_STOCK_OPEN
, GTK_RESPONSE_ACCEPT
,
191 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog
),
192 (app
->playlist
.path
)?app
->playlist
.path
:getenv("HOME"));
194 filter
= gtk_file_filter_new();
195 gtk_file_filter_set_name(filter
, "Playlist formatted (*.ply)");
196 gtk_file_filter_add_pattern(filter
, "*.ply");
197 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER (dialog
), filter
);
198 filter
= gtk_file_filter_new();
199 gtk_file_filter_set_name(filter
, "All types (*.*)");
200 gtk_file_filter_add_pattern(filter
, "*.*");
201 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER (dialog
), filter
);
203 r
= gtk_dialog_run(GTK_DIALOG(dialog
));
205 if(r
== GTK_RESPONSE_ACCEPT
)
209 filename
= gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog
));
211 r
= load_file_ply(app
, filename
);
214 omnplay_playlist_draw(app
);
216 if(app
->playlist
.path
)
217 g_free(app
->playlist
.path
);
218 if((app
->playlist
.path
= filename
))
220 char* e
= strrchr(app
->playlist
.path
, '/');
225 gtk_widget_destroy (dialog
);
228 static int save_file_ply(omnplay_instance_t
* app
, char* filename
)
232 char tc1
[12], tc2
[12], tc3
[12];
233 char* fname
= filename
;
235 filename
= (char*)malloc(PATH_MAX
);
236 strncpy(filename
, fname
, PATH_MAX
);
237 i
= strlen(filename
);
238 if(i
< 4 || strcasecmp(filename
+ i
- 4, ".ply"))
239 strcat(filename
, ".ply");
241 if((f
= fopen(filename
, "wt")))
243 for(i
= 0; i
< app
->playlist
.count
; i
++)
244 fprintf(f
, "%s,%d,%d,%s,%s,%s,,,,,,,,\n",
245 app
->playlist
.item
[i
].id
,
246 app
->playlist
.item
[i
].player
+ 1,
247 app
->playlist
.item
[i
].type
+ 1024,
248 frames2tc(app
->playlist
.item
[i
].in
, 25.0, tc1
),
249 frames2tc(app
->playlist
.item
[i
].in
+ app
->playlist
.item
[i
].dur
, 25.0, tc2
),
250 frames2tc(app
->playlist
.item
[i
].dur
, 25.0, tc3
));
258 void omnplay_playlist_save(omnplay_instance_t
* app
)
262 GtkFileFilter
*filter
;
264 dialog
= gtk_file_chooser_dialog_new("Save File",
265 GTK_WINDOW (app
->window
),
266 GTK_FILE_CHOOSER_ACTION_SAVE
,
267 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
268 GTK_STOCK_SAVE
, GTK_RESPONSE_ACCEPT
,
271 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog
), TRUE
);
273 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog
),
274 (app
->playlist
.path
)?app
->playlist
.path
:getenv("HOME"));
276 filter
= gtk_file_filter_new();
277 gtk_file_filter_set_name(filter
, "Playlist formatted (*.ply)");
278 gtk_file_filter_add_pattern(filter
, "*.ply");
279 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER (dialog
), filter
);
280 g_object_set_data(G_OBJECT(filter
), "id", GINT_TO_POINTER(0));
281 filter
= gtk_file_filter_new();
282 gtk_file_filter_set_name(filter
, "Text (*.txt)");
283 gtk_file_filter_add_pattern(filter
, "*.*");
284 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER (dialog
), filter
);
285 g_object_set_data(G_OBJECT(filter
), "id", GINT_TO_POINTER(1));
287 r
= gtk_dialog_run(GTK_DIALOG(dialog
));
289 if(r
== GTK_RESPONSE_ACCEPT
)
293 filename
= gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog
));
295 r
= GPOINTER_TO_INT(g_object_get_data(G_OBJECT(gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(dialog
))), "id"));
297 r
= save_file_ply(app
, filename
);
299 if(app
->playlist
.path
)
300 g_free(app
->playlist
.path
);
301 if((app
->playlist
.path
= filename
))
303 char* e
= strrchr(app
->playlist
.path
, '/');
308 gtk_widget_destroy (dialog
);
312 void omnplay_playlist_draw(omnplay_instance_t
* app
)
316 char tc1
[12], tc2
[12];
317 GtkListStore
*list_store
;
320 sels
= omnplay_selected_idxs_playlist(app
);
322 list_store
= GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(app
->playlist_grid
)));
323 gtk_list_store_clear(list_store
);
325 pthread_mutex_lock(&app
->playlist
.lock
);
327 for(i
= 0;i
< app
->playlist
.count
; i
++)
331 if(OMNPLAY_PLAYLIST_BLOCK_BEGIN
& app
->playlist
.item
[i
].type
)
332 snprintf(ch
, sizeof(ch
), "%c", 'A' + app
->playlist
.item
[i
].player
);
336 gtk_list_store_append(list_store
, &iter
);
338 gtk_list_store_set(list_store
, &iter
,
340 1, app
->playlist
.block_icons
[app
->playlist
.item
[i
].type
],
342 3, app
->playlist
.item
[i
].id
,
343 4, frames2tc(app
->playlist
.item
[i
].in
, 25.0, tc1
),
344 5, frames2tc(app
->playlist
.item
[i
].dur
, 25.0, tc2
),
345 6, app
->playlist
.item
[i
].title
,
347 8, (app
->playlist
.item
[i
].error
!= 0),
348 9, (app
->playlist
.item
[i
].error
& PLAYLIST_ITEM_ERROR_LIB
)?
"red":"orange",
352 app
->playlist
.ver_prev
= app
->playlist
.ver_curr
;
359 path
= gtk_tree_path_new_from_indices(sels
[1], -1);
360 gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(app
->playlist_grid
)), path
);
361 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app
->playlist_grid
), path
, NULL
, FALSE
);
362 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(app
->playlist_grid
), path
, NULL
, FALSE
, 0, 0);
363 gtk_tree_path_free(path
);
368 pthread_mutex_unlock(&app
->playlist
.lock
);
371 typedef struct omnplay_playlist_draw_item_desc
373 GtkListStore
*list_store
;
374 omnplay_instance_t
* app
;
376 } omnplay_playlist_draw_item_t
;
378 static gboolean
omnplay_playlist_draw_item_proc(
379 GtkTreeModel
*model
, GtkTreePath
*path
, GtkTreeIter
*iter
, gpointer user_data
)
382 char tc1
[12], tc2
[12];
384 omnplay_playlist_draw_item_t
* item
= (omnplay_playlist_draw_item_t
*)user_data
;
385 omnplay_instance_t
* app
= item
->app
;
387 gtk_tree_model_get(model
, iter
, 7, &i
, -1);
389 if(i
!= item
->idx
) return FALSE
;
391 if(OMNPLAY_PLAYLIST_BLOCK_BEGIN
& app
->playlist
.item
[i
].type
)
392 snprintf(ch
, sizeof(ch
), "%c", 'A' + app
->playlist
.item
[i
].player
);
396 gtk_list_store_set(item
->list_store
, iter
,
398 1, app
->playlist
.block_icons
[app
->playlist
.item
[i
].type
],
400 3, app
->playlist
.item
[i
].id
,
401 4, frames2tc(app
->playlist
.item
[i
].in
, 25.0, tc1
),
402 5, frames2tc(app
->playlist
.item
[i
].dur
, 25.0, tc2
),
403 6, app
->playlist
.item
[i
].title
,
405 8, (app
->playlist
.item
[i
].error
!= 0),
406 9, (app
->playlist
.item
[i
].error
& PLAYLIST_ITEM_ERROR_LIB
)?
"red":"orange",
412 void omnplay_playlist_draw_item(omnplay_instance_t
* app
, int idx
)
414 GtkListStore
*list_store
;
415 omnplay_playlist_draw_item_t item
;
417 list_store
= GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(app
->playlist_grid
)));
419 pthread_mutex_lock(&app
->playlist
.lock
);
423 item
.list_store
= list_store
;
424 gtk_tree_model_foreach(GTK_TREE_MODEL(list_store
), omnplay_playlist_draw_item_proc
, &item
);
426 pthread_mutex_unlock(&app
->playlist
.lock
);
429 static gboolean
omnplay_playlist_draw_item_rem_proc(
430 GtkTreeModel
*model
, GtkTreePath
*path
, GtkTreeIter
*iter
, gpointer user_data
)
433 void** args
= (void**)user_data
;
434 GtkListStore
*list_store
= (GtkListStore
*)args
[1];
435 int idx
= (int)args
[2];
436 char* rem
= (char*)args
[3];
438 gtk_tree_model_get(model
, iter
, 7, &i
, -1);
440 if(i
!= idx
) return FALSE
;
442 gtk_list_store_set(list_store
, iter
, 0, rem
, -1);
447 void omnplay_playlist_draw_item_rem(omnplay_instance_t
* app
, int idx
, char* rem
)
450 GtkListStore
*list_store
;
452 list_store
= GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(app
->playlist_grid
)));
454 item
[0] = (void*)app
;
455 item
[1] = (void*)list_store
;
456 item
[2] = (void*)idx
;
457 item
[3] = (void*)rem
;
459 gtk_tree_model_foreach(GTK_TREE_MODEL(list_store
), omnplay_playlist_draw_item_rem_proc
, item
);