relink funcionality added
[melted_gui] / 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 int omnplay_library_relink_item(omnplay_instance_t* app, playlist_item_t* item)
95 {
96 int r = 0;
97 playlist_item_t* lib;
98
99 pthread_mutex_lock(&app->library.lock);
100
101 lib = omnplay_library_find(app, item->id);
102
103 item->error = 0;
104
105 if(lib)
106 {
107 r = 1;
108 strcpy(item->title, lib->title);
109 item->dur = lib->dur;
110 item->in = lib->in;
111 }
112 else
113 {
114 r = 1;
115 item->error = PLAYLIST_ITEM_ERROR_LIB;
116 };
117
118 pthread_mutex_unlock(&app->library.lock);
119
120 return r;
121 };
122
123 void omnplay_library_sort(omnplay_instance_t* app)
124 {
125 int i, j, m;
126 playlist_item_t item;
127
128 for(i = 0; i < app->library.count; i++)
129 {
130 /* find max */
131 for(j = i + 1, m = i; j < app->library.count; j++)
132 if(strcasecmp(app->library.item[j].id, app->library.item[m].id) < 0)
133 m = j;
134
135 if(m != i)
136 {
137 item = app->library.item[i];
138 app->library.item[i] = app->library.item[m];
139 app->library.item[m] = item;
140 };
141 };
142 };
143
144 int omnplay_library_load_file(playlist_item_t* items, int *pcount, char* filename)
145 {
146 int i, c = 0, r = 0;
147 FILE* f;
148 char *l;
149 int limit = *pcount;
150 playlist_item_t item;
151
152 /* allocate space for strings and items */
153 l = malloc(PATH_MAX);
154
155 *pcount = 0;
156
157 /* open and process file */
158 if((f = fopen(filename, "rt")))
159 {
160 while(!feof(f) && c < limit)
161 {
162 char *s, *sp_r, *sp_b;
163
164 /* load string */
165 memset(l, 0, PATH_MAX);
166 fgets(l, PATH_MAX, f);
167
168 /* remove newlines */
169 if( (s = strchr(l, '\n')) ) *s = 0;
170 if( (s = strchr(l, '\r')) ) *s = 0;
171
172 /* check for empty line */
173 if(l[0] && l[0] != '#' && l[0] != '|')
174 {
175 memset(&item, 0, sizeof(playlist_item_t));
176
177 for(i = 0, sp_b = l; (NULL != (sp_r = strtok(sp_b, "\t"))); i++, sp_b = NULL)
178 {
179 switch(i)
180 {
181 case 0: strncpy(item.id, sp_r, PATH_MAX); break;
182 case 1: tc2frames(sp_r, 25.0, &item.in); break;
183 case 2: tc2frames(sp_r, 25.0, &item.dur); break;
184 case 3: strncpy(item.title, sp_r, PATH_MAX); break;
185 };
186 };
187
188 /* insert item */
189 items[c++] = item;
190 }
191 else
192 g_warning("omnplay_library_load_file: ignored line [%s]\n", l);
193 }
194
195 fclose(f);
196 }
197 else
198 r = -1;
199
200 /* free data */
201 free(l);
202
203 *pcount = c;
204
205 g_warning("omnplay_library_load_file: loaded [%d] items from [%s] file, limit [%d]\n", c, filename, limit);
206
207 return r;
208 };
209
210 void omnplay_library_load(omnplay_instance_t* app)
211 {
212 pthread_mutex_lock(&app->library.lock);
213
214 if(app->library.filename[0])
215 {
216 app->library.count = MAX_LIBRARY_ITEMS;
217 omnplay_library_load_file(app->library.item, &app->library.count, app->library.filename);
218 };
219
220 omnplay_library_sort(app);
221
222 pthread_mutex_unlock(&app->library.lock);
223
224 omnplay_library_draw(app);
225 };
226
227 static void omnplay_library_save_file(playlist_item_t* item, int count, char* filename)
228 {
229 int i;
230 FILE* f;
231
232 if((f = fopen(filename, "wt")))
233 {
234 char tc_in[32], tc_dur[32];
235
236 for(i = 0; i < count; i++)
237 fprintf(f, "%s\t%s\t%s\t%s\n",
238 item[i].id,
239 frames2tc(item[i].in, 25.0, tc_in),
240 frames2tc(item[i].dur, 25.0, tc_dur),
241 item[i].title);
242 fclose(f);
243 g_warning("omnplay_library_save_file: written [%d] lines to file [%s]\n", count, filename);
244 };
245 };
246
247 void omnplay_library_save(omnplay_instance_t* app)
248 {
249 pthread_mutex_lock(&app->library.lock);
250
251 if(app->library.filename[0])
252 omnplay_library_save_file(app->library.item, app->library.count,
253 app->library.filename);
254
255 pthread_mutex_unlock(&app->library.lock);
256 };
257
258 static void omnplay_get_content_cb(omnplay_instance_t* app, playlist_item_t* item, void* data)
259 {
260 if(!(app->library.id_display_idx % app->library.id_display_rate))
261 omnplay_set_status(app, item->id);
262 app->library.id_display_idx++;
263 };
264
265 static void* omnplay_library_refresh_proc(void* data)
266 {
267 omnplay_instance_t* app = (omnplay_instance_t*)data;
268 int count, i;
269 playlist_item_t* items;
270
271 gdk_threads_enter();
272 gtk_widget_set_sensitive(app->window, FALSE);
273 gdk_flush();
274 gdk_threads_leave();
275
276 omnplay_set_status(app, "Updating library...");
277
278 items = (playlist_item_t*)malloc(sizeof(playlist_item_t) * MAX_LIBRARY_ITEMS);
279
280 count = omnplay_get_content(app, items, MAX_LIBRARY_ITEMS, omnplay_get_content_cb, NULL);
281
282 if(count > 0)
283 {
284 omnplay_set_status(app, "Quering whois...");
285
286 if(app->library.whois[0])
287 omnplay_whois_list(app, items, &count);
288
289 omnplay_set_status(app, "Setting library...");
290
291 pthread_mutex_lock(&app->library.lock);
292
293 for(i = 0; i < count; i++)
294 app->library.item[i] = items[i];
295
296 app->library.count = count;
297
298 omnplay_library_sort(app);
299
300 pthread_mutex_unlock(&app->library.lock);
301
302 gdk_threads_enter();
303 omnplay_library_draw(app);
304 gdk_flush();
305 gdk_threads_leave();
306 };
307
308 omnplay_set_status(app, "Normalizing playlist...");
309
310 free(items);
311
312 gdk_threads_enter();
313 omnplay_playlist_normalize(app);
314 gdk_flush();
315 gdk_threads_leave();
316
317 omnplay_set_status(app, "");
318
319 gdk_threads_enter();
320 gtk_widget_set_sensitive(app->window, TRUE);
321 gdk_flush();
322 gdk_threads_leave();
323
324
325 return NULL;
326 };
327
328 void omnplay_library_refresh(omnplay_instance_t* app)
329 {
330 if(app->library.refresh_thread)
331 g_thread_join(app->library.refresh_thread);
332
333 app->library.refresh_thread = g_thread_create(
334 omnplay_library_refresh_proc, app, TRUE, NULL);
335 };
336
337 void omnplay_library_draw(omnplay_instance_t* app)
338 {
339 int i;
340 char tc[12];
341 GtkListStore *list_store;
342 GtkTreeIter iter;
343
344 list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(app->library_grid)));
345 gtk_list_store_clear(list_store);
346
347 pthread_mutex_lock(&app->library.lock);
348
349 for(i = 0;i < app->library.count; i++)
350 {
351 gtk_list_store_append(list_store, &iter);
352
353 gtk_list_store_set(list_store, &iter,
354 0, app->library.item[i].id,
355 1, frames2tc(app->library.item[i].dur, 25.0, tc),
356 2, app->library.item[i].title,
357 3, i,
358 4, FALSE,
359 5, "red",
360 -1 );
361 }
362
363 pthread_mutex_unlock(&app->library.lock);
364 };
365
366 static void get_selected_idx_library_proc(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
367 {
368 int idx, *list = (int*)data;
369 gtk_tree_model_get(model, iter, 3, &idx, -1);
370 list[list[0] + 1] = idx;
371 list[0] = list[0] + 1;
372 };
373
374 static int* get_selected_idx_library(omnplay_instance_t* app)
375 {
376 int* list = NULL;
377 GtkTreeSelection *selection;
378
379 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(app->library_grid));
380 if(selection)
381 {
382 list = (int*)malloc(sizeof(int) * (MAX_LIBRARY_ITEMS + 1));
383 memset(list, 0, sizeof(int) * (MAX_LIBRARY_ITEMS + 1));
384
385 gtk_tree_selection_selected_foreach(
386 selection,
387 get_selected_idx_library_proc,
388 list);
389
390 if(!list[0])
391 {
392 free(list);
393 list = NULL;
394 };
395 };
396
397 return list;
398 };
399
400
401 playlist_item_t* omnplay_library_get_selected(omnplay_instance_t* app, int *count)
402 {
403 int* idxs;
404 playlist_item_t* items = NULL;
405
406 pthread_mutex_lock(&app->library.lock);
407
408 *count = 0;
409
410 idxs = get_selected_idx_library(app);
411
412 if(idxs)
413 {
414 int i;
415
416 /* alloc items */
417 items = (playlist_item_t*)malloc(sizeof(playlist_item_t) * (idxs[0] + 1));
418
419 /* clear last item */
420 memset(&items[idxs[0]], 0, sizeof(playlist_item_t));
421
422 /* copy items */
423 for(i = 0; i < idxs[0]; i++)
424 items[i] = app->library.item[idxs[i + 1]];
425
426 *count = idxs[0];
427 free(idxs);
428 };
429
430 pthread_mutex_unlock(&app->library.lock);
431
432 return items;
433 };
434
435 void omnplay_library_search(omnplay_instance_t* app, int next)
436 {
437 int idx = 0, i;
438 int* idxs;
439 const char *search;
440 GtkTreePath* path;
441
442 pthread_mutex_lock(&app->library.lock);
443
444 idxs = get_selected_idx_library(app);
445 if(idxs) idx = idxs[1];
446 free(idxs);
447
448 if(!next) idx = 0;
449 else idx++;
450
451 search = gtk_entry_get_text(GTK_ENTRY(app->library.search));
452
453 if(search[0])
454 {
455 for(i = idx; i < app->library.count; i++)
456 if( strcasestr(app->library.item[i].id, search) ||
457 strcasestr(app->library.item[i].title, search))
458 break;
459
460 if(i < app->library.count)
461 {
462 g_warning("found at pos=%d\n", i);
463
464 /* select */
465 path = gtk_tree_path_new_from_indices(i, -1);
466 gtk_tree_selection_select_path(gtk_tree_view_get_selection(
467 GTK_TREE_VIEW(app->library_grid)), path);
468 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app->library_grid), path, NULL, FALSE);
469 gtk_tree_path_free(path);
470 };
471 };
472
473 pthread_mutex_unlock(&app->library.lock);
474 };