makes lib refresh status window works (not hang) under mingw32
[omnplay] / src / library.c
1 /*
2 * playlist.c -- GTK+ 2 omnplay
3 * Copyright (C) 2011 Maksym Veremeyenko <verem@m1stereo.tv>
4 *
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.
9 *
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.
14 *
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.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23
24 #ifndef _GNU_SOURCE
25 #define _GNU_SOURCE
26 #endif
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <gtk/gtk.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <pthread.h>
34 #include <string.h>
35
36 #include "omnplay.h"
37 #include "ui.h"
38 #include "timecode.h"
39
40 playlist_item_t* omnplay_library_find(omnplay_instance_t* app, char* id)
41 {
42 int i;
43 playlist_item_t* item = NULL;
44
45 pthread_mutex_lock(&app->library.lock);
46
47 for(i = 0; i < app->library.count && !item; i++)
48 if(!strcasecmp(id, app->library.item[i].id))
49 item = &app->library.item[i];
50
51 pthread_mutex_unlock(&app->library.lock);
52
53 return item;
54 };
55
56 int omnplay_library_normalize_item(omnplay_instance_t* app, playlist_item_t* item)
57 {
58 int r = 0;
59 playlist_item_t* lib;
60
61 pthread_mutex_lock(&app->library.lock);
62
63 lib = omnplay_library_find(app, item->id);
64
65 item->error = 0;
66
67 if(lib)
68 {
69
70 if(!item->title[0])
71 {
72 strcpy(item->title, lib->title);
73 r = 1;
74 };
75
76 if(!item->dur || item->in < lib->in || (item->in + item->dur) > (lib->in + lib->dur))
77 {
78 item->dur = lib->dur;
79 item->in = lib->in;
80 r = 1;
81 };
82 }
83 else
84 {
85 r = 1;
86 item->error = PLAYLIST_ITEM_ERROR_LIB;
87 };
88
89 pthread_mutex_unlock(&app->library.lock);
90
91 return r;
92 };
93
94 void omnplay_library_sort(omnplay_instance_t* app)
95 {
96 int i, j, m;
97 playlist_item_t item;
98
99 for(i = 0; i < app->library.count; i++)
100 {
101 /* find max */
102 for(j = i + 1, m = i; j < app->library.count; j++)
103 if(strcasecmp(app->library.item[j].id, app->library.item[m].id) < 0)
104 m = j;
105
106 if(m != i)
107 {
108 item = app->library.item[i];
109 app->library.item[i] = app->library.item[m];
110 app->library.item[m] = item;
111 };
112 };
113 };
114
115 int omnplay_library_load_file(playlist_item_t* items, int *pcount, char* filename)
116 {
117 int i, c = 0, r = 0;
118 FILE* f;
119 char *l;
120 int limit = *pcount;
121 playlist_item_t item;
122
123 /* allocate space for strings and items */
124 l = malloc(PATH_MAX);
125
126 *pcount = 0;
127
128 /* open and process file */
129 if((f = fopen(filename, "rt")))
130 {
131 while( !feof(f) && c < (limit -1))
132 {
133 char *s, *sp_r, *sp_b;
134
135 /* load string */
136 memset(l, 0, PATH_MAX);
137 fgets(l, PATH_MAX, f);
138
139 /* remove newlines */
140 if( (s = strchr(l, '\n')) ) *s = 0;
141 if( (s = strchr(l, '\r')) ) *s = 0;
142
143 /* check for empty line */
144 if(l[0] && l[0] != '#' && l[0] != '|')
145 {
146 memset(&item, 0, sizeof(playlist_item_t));
147
148 for(i = 0, sp_b = l; (NULL != (sp_r = strtok(sp_b, "\t"))); i++, sp_b = NULL)
149 {
150 switch(i)
151 {
152 case 0: strncpy(item.id, sp_r, PATH_MAX); break;
153 case 1: tc2frames(sp_r, 25.0, &item.in); break;
154 case 2: tc2frames(sp_r, 25.0, &item.dur); break;
155 case 3: strncpy(item.title, sp_r, PATH_MAX); break;
156 };
157 };
158
159 /* insert item */
160 items[c++] = item;
161 };
162 }
163
164 fclose(f);
165 }
166 else
167 r = -1;
168
169 /* free data */
170 free(l);
171
172 *pcount = c;
173
174 return r;
175 };
176
177 void omnplay_library_load(omnplay_instance_t* app)
178 {
179 pthread_mutex_lock(&app->library.lock);
180
181 if(app->library.filename[0])
182 {
183 app->library.count = MAX_LIBRARY_ITEMS;
184 omnplay_library_load_file(app->library.item, &app->library.count, app->library.filename);
185 };
186
187 omnplay_library_sort(app);
188
189 pthread_mutex_unlock(&app->library.lock);
190
191 omnplay_library_draw(app);
192 };
193
194 static void omnplay_library_save_file(playlist_item_t* item, int count, char* filename)
195 {
196 int i;
197 FILE* f;
198
199 if((f = fopen(filename, "wt")))
200 {
201 char tc_in[32], tc_dur[32];
202
203 for(i = 0; i < count; i++)
204 fprintf(f, "%s\t%s\t%s\t%s\n",
205 item[i].id,
206 frames2tc(item[i].in, 25.0, tc_in),
207 frames2tc(item[i].dur, 25.0, tc_dur),
208 item[i].title);
209
210 fclose(f);
211 };
212 };
213
214 void omnplay_library_save(omnplay_instance_t* app)
215 {
216 pthread_mutex_lock(&app->library.lock);
217
218 if(app->library.filename[0])
219 omnplay_library_save_file(app->library.item, app->library.count,
220 app->library.filename);
221
222 pthread_mutex_unlock(&app->library.lock);
223 };
224
225 static void omnplay_get_content_cb(omnplay_instance_t* app, playlist_item_t* item, void* data)
226 {
227 gdk_threads_enter();
228 gtk_label_set_text(GTK_LABEL(data), item->id);
229 gdk_flush();
230 gdk_threads_leave();
231 };
232
233 static void* omnplay_library_refresh_proc(void* data)
234 {
235 GtkWidget *refresh_ui[2];
236 omnplay_instance_t* app = (omnplay_instance_t*)data;
237 int count, i;
238 playlist_item_t* items;
239
240 #ifndef _WIN32
241 gdk_threads_enter();
242 #endif /* _WIN32 */
243 /* create UI for monitoring update */
244 ui_library_refresh(app, &refresh_ui[0], &refresh_ui[1]);
245 gtk_widget_show_all(refresh_ui[0]);
246 gtk_window_present(GTK_WINDOW(refresh_ui[0]));
247 #ifndef _WIN32
248 gdk_flush();
249 gdk_threads_leave();
250 #endif /* _WIN32 */
251
252 items = (playlist_item_t*)malloc(sizeof(playlist_item_t) * MAX_LIBRARY_ITEMS);
253
254 count = omnplay_get_content(app, items, MAX_LIBRARY_ITEMS, omnplay_get_content_cb, refresh_ui[1]);
255
256 if(count > 0)
257 {
258 gdk_threads_enter();
259 gtk_label_set_text(GTK_LABEL(refresh_ui[1]), "Quering whois...");
260 gdk_flush();
261 gdk_threads_leave();
262
263 if(app->library.whois[0])
264 omnplay_whois_list(app, items, &count);
265
266 pthread_mutex_lock(&app->library.lock);
267
268 for(i = 0; i < count; i++)
269 app->library.item[i] = items[i];
270
271 app->library.count = count;
272
273 omnplay_library_sort(app);
274
275 pthread_mutex_unlock(&app->library.lock);
276
277 gdk_threads_enter();
278 omnplay_library_draw(app);
279 gdk_flush();
280 gdk_threads_leave();
281 };
282
283 free(items);
284
285 gdk_threads_enter();
286 omnplay_playlist_normalize(app);
287 gdk_flush();
288 gdk_threads_leave();
289
290 #ifndef _WIN32
291 gdk_threads_enter();
292 #endif /* _WIN32 */
293 gtk_widget_destroy(refresh_ui[0]);
294 #ifndef _WIN32
295 gdk_flush();
296 gdk_threads_leave();
297 #endif /* _WIN32 */
298
299 return NULL;
300 };
301
302 void omnplay_library_refresh(omnplay_instance_t* app)
303 {
304 if(app->library.refresh_thread_r)
305 pthread_join(app->library.refresh_thread, NULL);
306 app->library.refresh_thread_r = 1;
307
308 pthread_create(&app->library.refresh_thread, NULL,
309 omnplay_library_refresh_proc, app);
310 };
311
312 void omnplay_library_draw(omnplay_instance_t* app)
313 {
314 int i;
315 char tc[12];
316 GtkListStore *list_store;
317 GtkTreeIter iter;
318
319 list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(app->library_grid)));
320 gtk_list_store_clear(list_store);
321
322 pthread_mutex_lock(&app->library.lock);
323
324 for(i = 0;i < app->library.count; i++)
325 {
326 gtk_list_store_append(list_store, &iter);
327
328 gtk_list_store_set(list_store, &iter,
329 0, app->library.item[i].id,
330 1, frames2tc(app->library.item[i].dur, 25.0, tc),
331 2, app->library.item[i].title,
332 3, i,
333 4, FALSE,
334 5, "red",
335 -1 );
336 }
337
338 pthread_mutex_unlock(&app->library.lock);
339 };
340
341 static void get_selected_idx_library_proc(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
342 {
343 int idx, *list = (int*)data;
344 gtk_tree_model_get(model, iter, 3, &idx, -1);
345 list[list[0] + 1] = idx;
346 list[0] = list[0] + 1;
347 };
348
349 static int* get_selected_idx_library(omnplay_instance_t* app)
350 {
351 int* list = NULL;
352 GtkTreeSelection *selection;
353
354 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(app->library_grid));
355 if(selection)
356 {
357 list = (int*)malloc(sizeof(int) * (MAX_LIBRARY_ITEMS + 1));
358 memset(list, 0, sizeof(int) * (MAX_LIBRARY_ITEMS + 1));
359
360 gtk_tree_selection_selected_foreach(
361 selection,
362 get_selected_idx_library_proc,
363 list);
364
365 if(!list[0])
366 {
367 free(list);
368 list = NULL;
369 };
370 };
371
372 return list;
373 };
374
375
376 playlist_item_t* omnplay_library_get_selected(omnplay_instance_t* app, int *count)
377 {
378 int* idxs;
379 playlist_item_t* items = NULL;
380
381 pthread_mutex_lock(&app->library.lock);
382
383 *count = 0;
384
385 idxs = get_selected_idx_library(app);
386
387 if(idxs)
388 {
389 int i;
390
391 /* alloc items */
392 items = (playlist_item_t*)malloc(sizeof(playlist_item_t) * (idxs[0] + 1));
393
394 /* clear last item */
395 memset(&items[idxs[0]], 0, sizeof(playlist_item_t));
396
397 /* copy items */
398 for(i = 0; i < idxs[0]; i++)
399 items[i] = app->library.item[idxs[i + 1]];
400
401 *count = idxs[0];
402 free(idxs);
403 };
404
405 pthread_mutex_unlock(&app->library.lock);
406
407 return items;
408 };
409
410 void omnplay_library_search(omnplay_instance_t* app, int next)
411 {
412 int idx = 0, i;
413 int* idxs;
414 const char *search;
415 GtkTreePath* path;
416
417 pthread_mutex_lock(&app->library.lock);
418
419 idxs = get_selected_idx_library(app);
420 if(idxs) idx = idxs[1];
421 free(idxs);
422
423 if(!next) idx = 0;
424 else idx++;
425
426 search = gtk_entry_get_text(GTK_ENTRY(app->library.search));
427
428 if(search[0])
429 {
430 for(i = idx; i < app->library.count; i++)
431 if( strcasestr(app->library.item[i].id, search) ||
432 strcasestr(app->library.item[i].title, search))
433 break;
434
435 if(i < app->library.count)
436 {
437 g_warning("found at pos=%d\n", i);
438
439 /* select */
440 path = gtk_tree_path_new_from_indices(i, -1);
441 gtk_tree_selection_select_path(gtk_tree_view_get_selection(
442 GTK_TREE_VIEW(app->library_grid)), path);
443 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app->library_grid), path, NULL, FALSE);
444 gtk_tree_path_free(path);
445 };
446 };
447
448 pthread_mutex_unlock(&app->library.lock);
449 };