2 * library.c -- GTK+ 2 melted gui
3 * Copyright (C) 2012 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.
32 #include <gdk/gdkkeysyms.h>
36 #include <mvcp/mvcp.h>
37 #include <mvcp/mvcp_remote.h>
44 void library_release(instance_t
* app
)
46 mvcp_close(app
->library
.handle
[0]);
47 mvcp_parser_close(app
->library
.handle
[1]);
50 static void library_add_fake(instance_t
* app
, GtkTreeStore
*tree_store
, GtkTreeIter
* parent
)
53 gtk_tree_store_append(tree_store
, &iter
, parent
);
54 gtk_tree_store_set(tree_store
, &iter
, -1);
57 static int library_init_load(instance_t
* app
)
60 GtkTreeStore
*tree_store
;
62 tree_store
= GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(app
->library_tree
)));
63 gtk_tree_store_clear(tree_store
);
65 gtk_tree_store_append(tree_store
, &iter
, NULL
);
66 gtk_tree_store_set(tree_store
, &iter
,
67 0, app
->library
.icons
[0],
75 library_add_fake(app
, tree_store
, &iter
);
77 gtk_tree_view_collapse_all(GTK_TREE_VIEW(app
->library_tree
));
82 static void library_add_item(instance_t
* app
, GtkTreeStore
*treestore
, GtkTreeIter
*iter
,
83 mvcp_dir_entry e
, mvcp_list_entry p
)
85 GtkTreeIter
this, child
;
89 gtk_tree_store_prepend(treestore
, &this, iter
);
91 gtk_tree_store_set(treestore
, &this,
92 0, app
->library
.icons
[0],
101 gtk_tree_store_append(treestore
, &child
, &this);
102 gtk_tree_store_set(treestore
, &child
, -1);
109 frames2tc(p
->size
, p
->fps
, dur
);
111 strcpy(dur
, "<file>");
113 gtk_tree_store_append(treestore
, &this, iter
);
115 gtk_tree_store_set(treestore
, &this,
116 0, app
->library
.icons
[1],
127 static void on_library_row_expanded
129 GtkTreeView
*treeview
,
142 instance_t
* app
= (instance_t
*)user_data
;
144 // g_warning("on_library_row_expanded: HERE");
146 /* Set busy cursor */
147 cursor
= gdk_cursor_new(GDK_WATCH
);
148 gdk_window_set_cursor(gtk_widget_get_toplevel(GTK_WIDGET(treeview
))->window
, cursor
);
149 gdk_cursor_unref(cursor
);
152 model
= gtk_tree_view_get_model(treeview
);
155 gtk_tree_model_iter_children(GTK_TREE_MODEL(model
), &fake
, iter
);
157 /* request mvcp entry */
158 gtk_tree_model_get(GTK_TREE_MODEL(model
), iter
,
162 /* setup root path */
169 dir
= mvcp_dir_init(app
->library
.handle
[0], p
);
170 for (i
= 0; i
< mvcp_dir_count(dir
); i
++)
172 mvcp_dir_entry_t dir_entry
;
173 mvcp_list_entry_t
*list_e
= NULL
, list_entry
;
175 if(mvcp_ok
!= mvcp_dir_get(dir
, i
, &dir_entry
))
178 // g_warning("on_library_row_expanded: path=[%s], entry.dur=[%d], entry.full=[%s], entry.name[%s]",
179 // p, entry.dir, entry.full, entry.name);
181 e
= (mvcp_dir_entry_t
*)malloc(sizeof(mvcp_dir_entry_t
));
184 if(!e
->dir
&& mvcp_ok
== mvcp_probe_clip( app
->library
.handle
[0], e
->full
, &list_entry
))
186 list_e
= (mvcp_list_entry_t
*)malloc(sizeof(mvcp_list_entry_t
));
187 *list_e
= list_entry
;
190 library_add_item(app
, GTK_TREE_STORE(model
), iter
, e
, list_e
);
194 gdk_window_set_cursor(gtk_widget_get_toplevel(GTK_WIDGET(treeview
))->window
, NULL
);
196 /* delete fake item */
197 gtk_tree_store_remove(GTK_TREE_STORE(model
), &fake
);
200 static void on_library_row_collapsed
202 GtkTreeView
*treeview
,
210 // g_warning("on_library_row_collapsed: HERE");
212 /* delete all items */
213 model
= gtk_tree_view_get_model(treeview
);
214 while (gtk_tree_model_iter_children(GTK_TREE_MODEL(model
), &child
, iter
))
217 mvcp_list_entry_t
*l
;
219 /* request mvcp entry */
220 gtk_tree_model_get(GTK_TREE_MODEL(model
), &child
,
229 gtk_tree_store_remove(GTK_TREE_STORE(model
), &child
);
232 /* add a fake element */
233 library_add_fake(user_data
, GTK_TREE_STORE(model
), iter
);
236 void library_init(instance_t
* app
)
238 /* connect to library */
239 app
->library
.handle
[1] = mvcp_parser_init_remote(app
->players
.host
, app
->library
.port
);
240 app
->library
.handle
[0] = mvcp_init(app
->library
.handle
[1]);
241 if(mvcp_connect(app
->library
.handle
[0]) != mvcp_ok
)
243 g_warning("library_init: failed to connect to server %s", app
->players
.host
);
248 app
->library
.icons
[0] = create_pixbuf("Axialis_Team_playlist_open_16x16.png");
249 app
->library
.icons
[1] = create_pixbuf("Axialis_Team_playlist_save_16x16.png");
252 library_init_load(app
);
255 gtk_signal_connect(GTK_OBJECT(app
->library_tree
), "row-expanded",
256 GTK_SIGNAL_FUNC(on_library_row_expanded
), app
);
257 gtk_signal_connect(GTK_OBJECT(app
->library_tree
), "row-collapsed",
258 GTK_SIGNAL_FUNC(on_library_row_collapsed
), app
);
261 pthread_mutex_lock(&app
->library
.lock
);
263 if(app
->library
.filename
[0])
265 app
->library
.count
= MAX_LIBRARY_ITEMS
;
266 omnplay_library_load_file(app
->library
.item
, &app
->library
.count
, app
->library
.filename
);
269 omnplay_library_sort(app
);
271 pthread_mutex_unlock(&app
->library
.lock
);
273 omnplay_library_draw(app
);
278 playlist_item_t
* omnplay_library_find(omnplay_instance_t
* app
, char* id
)
281 playlist_item_t
* item
= NULL
;
283 pthread_mutex_lock(&app
->library
.lock
);
285 for(i
= 0; i
< app
->library
.count
&& !item
; i
++)
286 if(!strcasecmp(id
, app
->library
.item
[i
].id
))
287 item
= &app
->library
.item
[i
];
289 pthread_mutex_unlock(&app
->library
.lock
);
294 int omnplay_library_normalize_item(omnplay_instance_t
* app
, playlist_item_t
* item
)
297 playlist_item_t
* lib
;
298 playlist_item_t prev
;
300 pthread_mutex_lock(&app
->library
.lock
);
304 lib
= omnplay_library_find(app
, item
->id
);
312 strcpy(item
->title
, lib
->title
);
316 if(item
->in
< lib
->in
|| item
->in
>= (lib
->in
+ lib
->dur
))
322 if(!item
->dur
|| (item
->in
+ item
->dur
) > (lib
->in
+ lib
->dur
))
324 item
->dur
= lib
->in
+ lib
->dur
- item
->in
;
329 g_warning("omnplay_library_normalize_item: [%s,%d,%d]->[%s,%d,%d]\n",
330 prev
.title
, prev
.in
, prev
.dur
, item
->title
, item
->in
, item
->dur
);
335 item
->error
= PLAYLIST_ITEM_ERROR_LIB
;
338 pthread_mutex_unlock(&app
->library
.lock
);
343 int omnplay_library_relink_item(omnplay_instance_t
* app
, playlist_item_t
* item
)
346 playlist_item_t
* lib
;
348 pthread_mutex_lock(&app
->library
.lock
);
350 lib
= omnplay_library_find(app
, item
->id
);
357 strcpy(item
->title
, lib
->title
);
358 item
->dur
= lib
->dur
;
364 item
->error
= PLAYLIST_ITEM_ERROR_LIB
;
367 pthread_mutex_unlock(&app
->library
.lock
);
372 void omnplay_library_sort(omnplay_instance_t
* app
)
375 playlist_item_t item
;
377 for(i
= 0; i
< app
->library
.count
; i
++)
380 for(j
= i
+ 1, m
= i
; j
< app
->library
.count
; j
++)
381 if(strcasecmp(app
->library
.item
[j
].id
, app
->library
.item
[m
].id
) < 0)
386 item
= app
->library
.item
[i
];
387 app
->library
.item
[i
] = app
->library
.item
[m
];
388 app
->library
.item
[m
] = item
;
393 int omnplay_library_load_file(playlist_item_t
* items
, int *pcount
, char* filename
)
399 playlist_item_t item
;
401 /* allocate space for strings and items */
402 l
= malloc(PATH_MAX
);
406 /* open and process file */
407 if((f
= fopen(filename
, "rt")))
409 while(!feof(f
) && c
< limit
)
411 char *s
, *sp_r
, *sp_b
;
414 memset(l
, 0, PATH_MAX
);
415 fgets(l
, PATH_MAX
, f
);
417 /* remove newlines */
418 if( (s
= strchr(l
, '\n')) ) *s
= 0;
419 if( (s
= strchr(l
, '\r')) ) *s
= 0;
421 /* check for empty line */
422 if(l
[0] && l
[0] != '#' && l
[0] != '|')
424 memset(&item
, 0, sizeof(playlist_item_t
));
426 for(i
= 0, sp_b
= l
; (NULL
!= (sp_r
= strtok(sp_b
, "\t"))); i
++, sp_b
= NULL
)
430 case 0: strncpy(item
.id
, sp_r
, PATH_MAX
); break;
431 case 1: tc2frames(sp_r
, 25.0, &item
.in
); break;
432 case 2: tc2frames(sp_r
, 25.0, &item
.dur
); break;
433 case 3: strncpy(item
.title
, sp_r
, PATH_MAX
); break;
441 g_warning("omnplay_library_load_file: ignored line [%s]\n", l
);
454 g_warning("omnplay_library_load_file: loaded [%d] items from [%s] file, limit [%d]\n", c
, filename
, limit
);
460 static void omnplay_library_save_file(playlist_item_t
* item
, int count
, char* filename
)
465 if((f
= fopen(filename
, "wt")))
467 char tc_in
[32], tc_dur
[32];
469 for(i
= 0; i
< count
; i
++)
470 fprintf(f
, "%s\t%s\t%s\t%s\n",
472 frames2tc(item
[i
].in
, 25.0, tc_in
),
473 frames2tc(item
[i
].dur
, 25.0, tc_dur
),
476 g_warning("omnplay_library_save_file: written [%d] lines to file [%s]\n", count
, filename
);
480 void omnplay_library_save(omnplay_instance_t
* app
)
482 pthread_mutex_lock(&app
->library
.lock
);
484 if(app
->library
.filename
[0])
485 omnplay_library_save_file(app
->library
.item
, app
->library
.count
,
486 app
->library
.filename
);
488 pthread_mutex_unlock(&app
->library
.lock
);
491 static void omnplay_get_content_cb(omnplay_instance_t
* app
, playlist_item_t
* item
, void* data
)
493 if(!(app
->library
.id_display_idx
% app
->library
.id_display_rate
))
494 omnplay_set_status(app
, item
->id
);
495 app
->library
.id_display_idx
++;
498 static void* omnplay_library_refresh_proc(void* data
)
500 omnplay_instance_t
* app
= (omnplay_instance_t
*)data
;
502 playlist_item_t
* items
;
505 gtk_widget_set_sensitive(app
->window
, FALSE
);
509 omnplay_set_status(app
, "Updating library...");
511 items
= (playlist_item_t
*)malloc(sizeof(playlist_item_t
) * MAX_LIBRARY_ITEMS
);
513 count
= omnplay_get_content(app
, items
, MAX_LIBRARY_ITEMS
, omnplay_get_content_cb
, NULL
);
517 omnplay_set_status(app
, "Quering whois...");
519 if(app
->library
.whois
[0])
520 omnplay_whois_list(app
, items
, &count
);
522 omnplay_set_status(app
, "Setting library...");
524 pthread_mutex_lock(&app
->library
.lock
);
526 for(i
= 0; i
< count
; i
++)
527 app
->library
.item
[i
] = items
[i
];
529 app
->library
.count
= count
;
531 omnplay_library_sort(app
);
533 pthread_mutex_unlock(&app
->library
.lock
);
536 omnplay_library_draw(app
);
541 omnplay_set_status(app
, "Normalizing playlist...");
546 omnplay_playlist_normalize(app
);
550 omnplay_set_status(app
, "");
553 gtk_widget_set_sensitive(app
->window
, TRUE
);
561 void omnplay_library_refresh(omnplay_instance_t
* app
)
563 if(app
->library
.refresh_thread
)
564 g_thread_join(app
->library
.refresh_thread
);
566 app
->library
.refresh_thread
= g_thread_create(
567 omnplay_library_refresh_proc
, app
, TRUE
, NULL
);
570 void omnplay_library_draw(omnplay_instance_t
* app
)
574 GtkListStore
*list_store
;
577 list_store
= GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(app
->library_grid
)));
578 gtk_list_store_clear(list_store
);
580 pthread_mutex_lock(&app
->library
.lock
);
582 for(i
= 0;i
< app
->library
.count
; i
++)
584 gtk_list_store_append(list_store
, &iter
);
586 gtk_list_store_set(list_store
, &iter
,
587 0, app
->library
.item
[i
].id
,
588 1, frames2tc(app
->library
.item
[i
].dur
, 25.0, tc
),
589 2, app
->library
.item
[i
].title
,
596 pthread_mutex_unlock(&app
->library
.lock
);
599 static void get_selected_idx_library_proc(GtkTreeModel
*model
, GtkTreePath
*path
, GtkTreeIter
*iter
, gpointer data
)
601 int idx
, *list
= (int*)data
;
602 gtk_tree_model_get(model
, iter
, 3, &idx
, -1);
603 list
[list
[0] + 1] = idx
;
604 list
[0] = list
[0] + 1;
607 static int* get_selected_idx_library(omnplay_instance_t
* app
)
610 GtkTreeSelection
*selection
;
612 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(app
->library_grid
));
615 list
= (int*)malloc(sizeof(int) * (MAX_LIBRARY_ITEMS
+ 1));
616 memset(list
, 0, sizeof(int) * (MAX_LIBRARY_ITEMS
+ 1));
618 gtk_tree_selection_selected_foreach(
620 get_selected_idx_library_proc
,
634 playlist_item_t
* omnplay_library_get_selected(omnplay_instance_t
* app
, int *count
)
637 playlist_item_t
* items
= NULL
;
639 pthread_mutex_lock(&app
->library
.lock
);
643 idxs
= get_selected_idx_library(app
);
650 items
= (playlist_item_t
*)malloc(sizeof(playlist_item_t
) * (idxs
[0] + 1));
652 /* clear last item */
653 memset(&items
[idxs
[0]], 0, sizeof(playlist_item_t
));
656 for(i
= 0; i
< idxs
[0]; i
++)
657 items
[i
] = app
->library
.item
[idxs
[i
+ 1]];
663 pthread_mutex_unlock(&app
->library
.lock
);
668 void omnplay_library_search(omnplay_instance_t
* app
, int next
)
675 pthread_mutex_lock(&app
->library
.lock
);
677 idxs
= get_selected_idx_library(app
);
678 if(idxs
) idx
= idxs
[1];
684 search
= gtk_entry_get_text(GTK_ENTRY(app
->library
.search
));
688 for(i
= idx
; i
< app
->library
.count
; i
++)
689 if( strcasestr(app
->library
.item
[i
].id
, search
) ||
690 strcasestr(app
->library
.item
[i
].title
, search
))
693 if(i
< app
->library
.count
)
695 g_warning("found at pos=%d\n", i
);
698 path
= gtk_tree_path_new_from_indices(i
, -1);
699 gtk_tree_selection_select_path(gtk_tree_view_get_selection(
700 GTK_TREE_VIEW(app
->library_grid
)), path
);
701 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app
->library_grid
), path
, NULL
, FALSE
);
702 gtk_tree_path_free(path
);
706 pthread_mutex_unlock(&app
->library
.lock
);