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>
37 https://gitorious.org/mingw-unofficial/msys/source/956185bef3c2dafb0e6f5f9eadd925ec55139f2e:rt/src/winsup/cygwin/strsep.cc
39 static char * strsep (char **stringp
, const char *delim
)
42 register const char *spanp
;
45 if((s
= *stringp
) == NULL
)
54 if((sc
= *spanp
++) == c
)
72 static int load_file_ply(omnplay_instance_t
* app
, char* filename
)
77 playlist_item_t
* items
;
79 g_warning("%s:%d filename = [%s]", __FUNCTION__
, __LINE__
, filename
);
81 /* open and process file */
82 f
= fopen(filename
, "rt");
85 g_warning("%s:%d failed to open filename = [%s]", __FUNCTION__
, __LINE__
, filename
);
89 /* allocate space for strings and items */
90 items
= malloc(sizeof(playlist_item_t
) * MAX_PLAYLIST_ITEMS
);
91 memset(items
, 0, sizeof(playlist_item_t
) * MAX_PLAYLIST_ITEMS
);
97 char *save
, *str
, *tok
;
102 fgets(l
, PATH_MAX
, f
);
104 /* remove newlines */
105 while((s
= strchr(l
, '\n'))) *s
= 0;
106 while((s
= strchr(l
, '\r'))) *s
= 0;
107 while((s
= strchr(l
, '\t'))) *s
= 0;
109 /* check for empty line */
110 if(!l
[0] || l
[0] == '#')
113 /* process split tokens */
114 memset(&item
, 0, sizeof(item
));
116 str
= save
= strdup(l
);
117 while((tok
= strsep(&str
, ",")) != NULL
)
119 g_warning("%s:%d tok[%d]=[%s]", __FUNCTION__
, __LINE__
, i
, tok
);
122 strncpy(item
.id
, tok
, PATH_MAX
);
124 item
.player
= atol(tok
) - 1;
130 case 1: item
.type
= OMNPLAY_PLAYLIST_ITEM_BLOCK_SINGLE
; break;
131 case 2: item
.type
= OMNPLAY_PLAYLIST_ITEM_LOOP_BEGIN
; break;
132 case 3: item
.type
= OMNPLAY_PLAYLIST_ITEM_LOOP_BODY
; break;
133 case 4: item
.type
= OMNPLAY_PLAYLIST_ITEM_LOOP_END
; break;
134 case 6: item
.type
= OMNPLAY_PLAYLIST_ITEM_BLOCK_END
; break;
137 item
.type
= OMNPLAY_PLAYLIST_ITEM_BLOCK_BEGIN
;
140 items
[count
- 1].type
== OMNPLAY_PLAYLIST_ITEM_BLOCK_BEGIN
142 items
[count
- 1].type
== OMNPLAY_PLAYLIST_ITEM_BLOCK_BODY
144 item
.type
= OMNPLAY_PLAYLIST_ITEM_BLOCK_BODY
;
146 item
.type
= OMNPLAY_PLAYLIST_ITEM_BLOCK_BEGIN
;
150 item
.type
= b
- 1024;
154 tc2frames(tok
, 25.0, &item
.in
);
156 tc2frames(tok
, 25.0, &item
.dur
);
163 g_warning("%s:%d count=[%d] id={%s} in={%d} dur={%d}", __FUNCTION__
, __LINE__
, count
, item
.id
, item
.in
, item
.dur
);
168 g_warning("%s:%d i = %d", __FUNCTION__
, __LINE__
, i
);
173 /* add loaded items to playlist */
176 pthread_mutex_lock(&app
->playlist
.lock
);
177 for(i
= 0; i
< count
&& app
->playlist
.count
+ 1 < MAX_PLAYLIST_ITEMS
; i
++)
179 omnplay_library_normalize_item(app
, &items
[i
]);
180 app
->playlist
.item
[app
->playlist
.count
++] = items
[i
];
182 app
->playlist
.ver_curr
++;
183 pthread_mutex_unlock(&app
->playlist
.lock
);
193 void omnplay_playlist_load(omnplay_instance_t
* app
)
197 GtkFileFilter
*filter
;
199 dialog
= gtk_file_chooser_dialog_new("Open File",
200 GTK_WINDOW (app
->window
),
201 GTK_FILE_CHOOSER_ACTION_OPEN
,
202 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
203 GTK_STOCK_OPEN
, GTK_RESPONSE_ACCEPT
,
206 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog
),
207 (app
->playlist
.path
)?app
->playlist
.path
:getenv("HOME"));
209 filter
= gtk_file_filter_new();
210 gtk_file_filter_set_name(filter
, "Playlist formatted (*.ply)");
211 gtk_file_filter_add_pattern(filter
, "*.ply");
212 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER (dialog
), filter
);
213 filter
= gtk_file_filter_new();
214 gtk_file_filter_set_name(filter
, "All types (*.*)");
215 gtk_file_filter_add_pattern(filter
, "*.*");
216 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER (dialog
), filter
);
218 r
= gtk_dialog_run(GTK_DIALOG(dialog
));
220 if(r
== GTK_RESPONSE_ACCEPT
)
224 filename
= gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog
));
226 r
= load_file_ply(app
, filename
);
229 omnplay_playlist_draw(app
);
231 if(app
->playlist
.path
)
232 g_free(app
->playlist
.path
);
233 if((app
->playlist
.path
= filename
))
235 char* e
= strrchr(app
->playlist
.path
, '/');
240 gtk_widget_destroy (dialog
);
243 static int save_file_ply(omnplay_instance_t
* app
, char* filename
)
247 char tc1
[12], tc2
[12], tc3
[12];
248 char* fname
= filename
;
250 filename
= (char*)malloc(PATH_MAX
);
251 strncpy(filename
, fname
, PATH_MAX
);
252 i
= strlen(filename
);
253 if(i
< 4 || strcasecmp(filename
+ i
- 4, ".ply"))
254 strcat(filename
, ".ply");
256 if((f
= fopen(filename
, "wt")))
258 for(i
= 0; i
< app
->playlist
.count
; i
++)
259 fprintf(f
, "%s,%d,%d,%s,%s,%s,,,,,,,,\n",
260 app
->playlist
.item
[i
].id
,
261 app
->playlist
.item
[i
].player
+ 1,
262 app
->playlist
.item
[i
].type
+ 1024,
263 frames2tc(app
->playlist
.item
[i
].in
, 25.0, tc1
),
264 frames2tc(app
->playlist
.item
[i
].in
+ app
->playlist
.item
[i
].dur
, 25.0, tc2
),
265 frames2tc(app
->playlist
.item
[i
].dur
, 25.0, tc3
));
273 void omnplay_playlist_save(omnplay_instance_t
* app
)
277 GtkFileFilter
*filter
;
279 dialog
= gtk_file_chooser_dialog_new("Save File",
280 GTK_WINDOW (app
->window
),
281 GTK_FILE_CHOOSER_ACTION_SAVE
,
282 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
283 GTK_STOCK_SAVE
, GTK_RESPONSE_ACCEPT
,
286 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog
), TRUE
);
288 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog
),
289 (app
->playlist
.path
)?app
->playlist
.path
:getenv("HOME"));
291 filter
= gtk_file_filter_new();
292 gtk_file_filter_set_name(filter
, "Playlist formatted (*.ply)");
293 gtk_file_filter_add_pattern(filter
, "*.ply");
294 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER (dialog
), filter
);
295 g_object_set_data(G_OBJECT(filter
), "id", GINT_TO_POINTER(0));
296 filter
= gtk_file_filter_new();
297 gtk_file_filter_set_name(filter
, "Text (*.txt)");
298 gtk_file_filter_add_pattern(filter
, "*.*");
299 gtk_file_chooser_add_filter(GTK_FILE_CHOOSER (dialog
), filter
);
300 g_object_set_data(G_OBJECT(filter
), "id", GINT_TO_POINTER(1));
302 r
= gtk_dialog_run(GTK_DIALOG(dialog
));
304 if(r
== GTK_RESPONSE_ACCEPT
)
308 filename
= gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog
));
310 r
= GPOINTER_TO_INT(g_object_get_data(G_OBJECT(gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(dialog
))), "id"));
312 r
= save_file_ply(app
, filename
);
314 if(app
->playlist
.path
)
315 g_free(app
->playlist
.path
);
316 if((app
->playlist
.path
= filename
))
318 char* e
= strrchr(app
->playlist
.path
, '/');
323 gtk_widget_destroy (dialog
);
327 void omnplay_playlist_draw(omnplay_instance_t
* app
)
331 char tc1
[12], tc2
[12];
332 GtkListStore
*list_store
;
335 sels
= omnplay_selected_idxs_playlist(app
);
337 list_store
= GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(app
->playlist_grid
)));
338 gtk_list_store_clear(list_store
);
340 pthread_mutex_lock(&app
->playlist
.lock
);
342 for(i
= 0;i
< app
->playlist
.count
; i
++)
346 if(OMNPLAY_PLAYLIST_BLOCK_BEGIN
& app
->playlist
.item
[i
].type
)
347 snprintf(ch
, sizeof(ch
), "%c", 'A' + app
->playlist
.item
[i
].player
);
351 gtk_list_store_append(list_store
, &iter
);
353 gtk_list_store_set(list_store
, &iter
,
355 1, app
->playlist
.block_icons
[app
->playlist
.item
[i
].type
],
357 3, app
->playlist
.item
[i
].id
,
358 4, frames2tc(app
->playlist
.item
[i
].in
, 25.0, tc1
),
359 5, frames2tc(app
->playlist
.item
[i
].dur
, 25.0, tc2
),
360 6, app
->playlist
.item
[i
].title
,
362 8, (app
->playlist
.item
[i
].error
!= 0),
363 9, (app
->playlist
.item
[i
].error
& PLAYLIST_ITEM_ERROR_LIB
)?
"red":"orange",
367 app
->playlist
.ver_prev
= app
->playlist
.ver_curr
;
374 path
= gtk_tree_path_new_from_indices(sels
[1], -1);
375 gtk_tree_selection_select_path(gtk_tree_view_get_selection(GTK_TREE_VIEW(app
->playlist_grid
)), path
);
376 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app
->playlist_grid
), path
, NULL
, FALSE
);
377 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(app
->playlist_grid
), path
, NULL
, FALSE
, 0, 0);
378 gtk_tree_path_free(path
);
383 pthread_mutex_unlock(&app
->playlist
.lock
);
386 typedef struct omnplay_playlist_draw_item_desc
388 GtkListStore
*list_store
;
389 omnplay_instance_t
* app
;
391 } omnplay_playlist_draw_item_t
;
393 static gboolean
omnplay_playlist_draw_item_proc(
394 GtkTreeModel
*model
, GtkTreePath
*path
, GtkTreeIter
*iter
, gpointer user_data
)
397 char tc1
[12], tc2
[12];
399 omnplay_playlist_draw_item_t
* item
= (omnplay_playlist_draw_item_t
*)user_data
;
400 omnplay_instance_t
* app
= item
->app
;
402 gtk_tree_model_get(model
, iter
, 7, &i
, -1);
404 if(i
!= item
->idx
) return FALSE
;
406 if(OMNPLAY_PLAYLIST_BLOCK_BEGIN
& app
->playlist
.item
[i
].type
)
407 snprintf(ch
, sizeof(ch
), "%c", 'A' + app
->playlist
.item
[i
].player
);
411 gtk_list_store_set(item
->list_store
, iter
,
413 1, app
->playlist
.block_icons
[app
->playlist
.item
[i
].type
],
415 3, app
->playlist
.item
[i
].id
,
416 4, frames2tc(app
->playlist
.item
[i
].in
, 25.0, tc1
),
417 5, frames2tc(app
->playlist
.item
[i
].dur
, 25.0, tc2
),
418 6, app
->playlist
.item
[i
].title
,
420 8, (app
->playlist
.item
[i
].error
!= 0),
421 9, (app
->playlist
.item
[i
].error
& PLAYLIST_ITEM_ERROR_LIB
)?
"red":"orange",
427 void omnplay_playlist_draw_item(omnplay_instance_t
* app
, int idx
)
429 GtkListStore
*list_store
;
430 omnplay_playlist_draw_item_t item
;
432 list_store
= GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(app
->playlist_grid
)));
434 pthread_mutex_lock(&app
->playlist
.lock
);
438 item
.list_store
= list_store
;
439 gtk_tree_model_foreach(GTK_TREE_MODEL(list_store
), omnplay_playlist_draw_item_proc
, &item
);
441 pthread_mutex_unlock(&app
->playlist
.lock
);
444 static gboolean
omnplay_playlist_draw_item_rem_proc(
445 GtkTreeModel
*model
, GtkTreePath
*path
, GtkTreeIter
*iter
, gpointer user_data
)
448 void** args
= (void**)user_data
;
449 GtkListStore
*list_store
= (GtkListStore
*)args
[1];
450 int idx
= (int)args
[2];
451 char* rem
= (char*)args
[3];
453 gtk_tree_model_get(model
, iter
, 7, &i
, -1);
455 if(i
!= idx
) return FALSE
;
457 gtk_list_store_set(list_store
, iter
, 0, rem
, -1);
462 void omnplay_playlist_draw_item_rem(omnplay_instance_t
* app
, int idx
, char* rem
)
465 GtkListStore
*list_store
;
467 list_store
= GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(app
->playlist_grid
)));
469 item
[0] = (void*)app
;
470 item
[1] = (void*)list_store
;
471 item
[2] = (void*)idx
;
472 item
[3] = (void*)rem
;
474 gtk_tree_model_foreach(GTK_TREE_MODEL(list_store
), omnplay_playlist_draw_item_rem_proc
, item
);