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 extern GtkTargetEntry drag_targets
[];
46 void library_release(instance_t
* app
)
48 mvcp_close(app
->library
.handle
[0]);
49 mvcp_parser_close(app
->library
.handle
[1]);
52 static void library_add_fake(instance_t
* app
, GtkTreeStore
*tree_store
, GtkTreeIter
* parent
)
55 gtk_tree_store_append(tree_store
, &iter
, parent
);
56 gtk_tree_store_set(tree_store
, &iter
, -1);
59 static int library_init_load(instance_t
* app
)
62 GtkTreeStore
*tree_store
;
64 tree_store
= GTK_TREE_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(app
->library_tree
)));
65 gtk_tree_store_clear(tree_store
);
67 gtk_tree_store_append(tree_store
, &iter
, NULL
);
68 gtk_tree_store_set(tree_store
, &iter
,
69 0, app
->library
.icons
[0],
77 library_add_fake(app
, tree_store
, &iter
);
79 gtk_tree_view_collapse_all(GTK_TREE_VIEW(app
->library_tree
));
84 static void library_add_item(instance_t
* app
, GtkTreeStore
*treestore
, GtkTreeIter
*iter
,
85 mvcp_dir_entry e
, mvcp_list_entry p
)
87 GtkTreeIter
this, child
;
91 gtk_tree_store_prepend(treestore
, &this, iter
);
93 gtk_tree_store_set(treestore
, &this,
94 0, app
->library
.icons
[0],
103 gtk_tree_store_append(treestore
, &child
, &this);
104 gtk_tree_store_set(treestore
, &child
, -1);
111 frames2tc(p
->size
, p
->fps
, dur
);
113 strcpy(dur
, "<file>");
115 gtk_tree_store_append(treestore
, &this, iter
);
117 gtk_tree_store_set(treestore
, &this,
118 0, app
->library
.icons
[1],
129 static void on_library_row_expanded
131 GtkTreeView
*treeview
,
144 instance_t
* app
= (instance_t
*)user_data
;
146 // g_warning("on_library_row_expanded: HERE");
148 /* Set busy cursor */
149 cursor
= gdk_cursor_new(GDK_WATCH
);
150 gdk_window_set_cursor(gtk_widget_get_toplevel(GTK_WIDGET(treeview
))->window
, cursor
);
151 gdk_cursor_unref(cursor
);
154 model
= gtk_tree_view_get_model(treeview
);
157 gtk_tree_model_iter_children(GTK_TREE_MODEL(model
), &fake
, iter
);
159 /* request mvcp entry */
160 gtk_tree_model_get(GTK_TREE_MODEL(model
), iter
,
164 /* setup root path */
171 dir
= mvcp_dir_init(app
->library
.handle
[0], p
);
172 for (i
= 0; i
< mvcp_dir_count(dir
); i
++)
174 mvcp_dir_entry_t dir_entry
;
175 mvcp_list_entry_t
*list_e
= NULL
, list_entry
;
177 if(mvcp_ok
!= mvcp_dir_get(dir
, i
, &dir_entry
))
180 // g_warning("on_library_row_expanded: path=[%s], entry.dur=[%d], entry.full=[%s], entry.name[%s]",
181 // p, entry.dir, entry.full, entry.name);
183 e
= (mvcp_dir_entry_t
*)malloc(sizeof(mvcp_dir_entry_t
));
186 if(!e
->dir
&& mvcp_ok
== mvcp_probe_clip( app
->library
.handle
[0], e
->full
, &list_entry
))
188 list_e
= (mvcp_list_entry_t
*)malloc(sizeof(mvcp_list_entry_t
));
189 *list_e
= list_entry
;
192 library_add_item(app
, GTK_TREE_STORE(model
), iter
, e
, list_e
);
196 gdk_window_set_cursor(gtk_widget_get_toplevel(GTK_WIDGET(treeview
))->window
, NULL
);
198 /* delete fake item */
199 gtk_tree_store_remove(GTK_TREE_STORE(model
), &fake
);
202 static void on_library_row_collapsed
204 GtkTreeView
*treeview
,
212 // g_warning("on_library_row_collapsed: HERE");
214 /* delete all items */
215 model
= gtk_tree_view_get_model(treeview
);
216 while (gtk_tree_model_iter_children(GTK_TREE_MODEL(model
), &child
, iter
))
219 mvcp_list_entry_t
*l
;
221 /* request mvcp entry */
222 gtk_tree_model_get(GTK_TREE_MODEL(model
), &child
,
231 gtk_tree_store_remove(GTK_TREE_STORE(model
), &child
);
234 /* add a fake element */
235 library_add_fake(user_data
, GTK_TREE_STORE(model
), iter
);
238 static void library_drag_data_get_cb(GtkWidget
*widget
, GdkDragContext
*context
,
239 GtkSelectionData
*selection_data
, guint info
, guint time
, gpointer userdata
)
242 playlist_item_t
* items
;
243 instance_t
* app
= (instance_t
*)userdata
;
245 g_warning("library_drag_data_get_cb");
247 items
= library_get_selected_items(app
, &c
);
252 gtk_selection_data_set(selection_data
, selection_data
->target
, 8,
253 (const guchar
*)items
, sizeof(playlist_item_t
) * c
);
258 static void library_drag_begin_cb(GtkWidget
*widget
, GdkDragContext
*context
, gpointer userdata
)
260 g_warning("library_drag_begin_cb");
261 gtk_drag_source_set_icon_stock(widget
, GTK_STOCK_DND
);
265 void library_init(instance_t
* app
)
267 /* connect to library */
268 app
->library
.handle
[1] = mvcp_parser_init_remote(app
->players
.host
, app
->library
.port
);
269 app
->library
.handle
[0] = mvcp_init(app
->library
.handle
[1]);
270 if(mvcp_connect(app
->library
.handle
[0]) != mvcp_ok
)
272 g_warning("library_init: failed to connect to server %s", app
->players
.host
);
277 app
->library
.icons
[0] = create_pixbuf("Axialis_Team_playlist_open_16x16.png");
278 app
->library
.icons
[1] = create_pixbuf("Axialis_Team_playlist_save_16x16.png");
281 library_init_load(app
);
283 /* allow drag source */
284 gtk_drag_source_set(app
->library_tree
, GDK_BUTTON1_MASK
,
285 drag_targets
, 1, (GdkDragAction
)(GDK_ACTION_COPY
));
288 gtk_signal_connect(GTK_OBJECT(app
->library_tree
), "row-expanded",
289 GTK_SIGNAL_FUNC(on_library_row_expanded
), app
);
290 gtk_signal_connect(GTK_OBJECT(app
->library_tree
), "row-collapsed",
291 GTK_SIGNAL_FUNC(on_library_row_collapsed
), app
);
292 g_signal_connect(GTK_OBJECT(app
->library_tree
), "drag_data_get",
293 G_CALLBACK(library_drag_data_get_cb
), app
);
294 g_signal_connect(GTK_OBJECT(app
->library_tree
), "drag_begin",
295 G_CALLBACK(library_drag_begin_cb
), app
);
299 static void library_get_selected_items_iter
308 mvcp_dir_entry_t
*dir
;
309 mvcp_list_entry_t
*list
;
310 playlist_item_t
** pitems
= (playlist_item_t
**)data
;
311 playlist_item_t
* items
= *pitems
;
313 /* request pointers to list and dir entries of library items */
314 gtk_tree_model_get(model
, iter
,
319 /* check if defined */
325 items
= (playlist_item_t
*)malloc(sizeof(playlist_item_t
));
326 memset(items
, 0, sizeof(playlist_item_t
));
329 /* find numbers of items in list */
330 for(l
= 0; items
[l
].id
[0]; l
++);
331 g_warning("library_get_selected_items_iter: l=%d", l
);
334 items
= (playlist_item_t
*)realloc(items
, (l
+ 2) * sizeof(playlist_item_t
));
336 /* clean last item */
337 memset(&items
[l
+ 1], 0, sizeof(playlist_item_t
));
340 memset(&items
[l
+ 0], 0, sizeof(playlist_item_t
));
341 strncpy(items
[l
].id
, dir
->name
, PATH_MAX
);
342 strncpy(items
[l
].title
, dir
->full
, PATH_MAX
);
343 items
[l
].dur
= list
->size
;
349 playlist_item_t
* library_get_selected_items(instance_t
* app
, int *count
)
352 playlist_item_t
* items
= NULL
;
354 GtkTreeSelection
*selection
;
356 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(app
->library_tree
));
359 gtk_tree_selection_selected_foreach(
361 library_get_selected_items_iter
,
365 for(; items
[l
].id
[0]; l
++);
372 int library_normalize_item(instance_t
* app
, playlist_item_t
* item
)
376 playlist_item_t
* lib
;
377 playlist_item_t prev
;
379 pthread_mutex_lock(&app
->library
.lock
);
383 lib
= omnplay_library_find(app
, item
->id
);
391 strcpy(item
->title
, lib
->title
);
395 if(item
->in
< lib
->in
|| item
->in
>= (lib
->in
+ lib
->dur
))
401 if(!item
->dur
|| (item
->in
+ item
->dur
) > (lib
->in
+ lib
->dur
))
403 item
->dur
= lib
->in
+ lib
->dur
- item
->in
;
408 g_warning("omnplay_library_normalize_item: [%s,%d,%d]->[%s,%d,%d]\n",
409 prev
.title
, prev
.in
, prev
.dur
, item
->title
, item
->in
, item
->dur
);
414 item
->error
= PLAYLIST_ITEM_ERROR_LIB
;
417 pthread_mutex_unlock(&app
->library
.lock
);
425 int library_relink_item(instance_t
* app
, playlist_item_t
* item
)
429 playlist_item_t
* lib
;
431 pthread_mutex_lock(&app
->library
.lock
);
433 lib
= omnplay_library_find(app
, item
->id
);
440 strcpy(item
->title
, lib
->title
);
441 item
->dur
= lib
->dur
;
447 item
->error
= PLAYLIST_ITEM_ERROR_LIB
;
450 pthread_mutex_unlock(&app
->library
.lock
);
460 playlist_item_t
* omnplay_library_find(omnplay_instance_t
* app
, char* id
)
463 playlist_item_t
* item
= NULL
;
465 pthread_mutex_lock(&app
->library
.lock
);
467 for(i
= 0; i
< app
->library
.count
&& !item
; i
++)
468 if(!strcasecmp(id
, app
->library
.item
[i
].id
))
469 item
= &app
->library
.item
[i
];
471 pthread_mutex_unlock(&app
->library
.lock
);
477 void omnplay_library_sort(omnplay_instance_t
* app
)
480 playlist_item_t item
;
482 for(i
= 0; i
< app
->library
.count
; i
++)
485 for(j
= i
+ 1, m
= i
; j
< app
->library
.count
; j
++)
486 if(strcasecmp(app
->library
.item
[j
].id
, app
->library
.item
[m
].id
) < 0)
491 item
= app
->library
.item
[i
];
492 app
->library
.item
[i
] = app
->library
.item
[m
];
493 app
->library
.item
[m
] = item
;
498 int omnplay_library_load_file(playlist_item_t
* items
, int *pcount
, char* filename
)
504 playlist_item_t item
;
506 /* allocate space for strings and items */
507 l
= malloc(PATH_MAX
);
511 /* open and process file */
512 if((f
= fopen(filename
, "rt")))
514 while(!feof(f
) && c
< limit
)
516 char *s
, *sp_r
, *sp_b
;
519 memset(l
, 0, PATH_MAX
);
520 fgets(l
, PATH_MAX
, f
);
522 /* remove newlines */
523 if( (s
= strchr(l
, '\n')) ) *s
= 0;
524 if( (s
= strchr(l
, '\r')) ) *s
= 0;
526 /* check for empty line */
527 if(l
[0] && l
[0] != '#' && l
[0] != '|')
529 memset(&item
, 0, sizeof(playlist_item_t
));
531 for(i
= 0, sp_b
= l
; (NULL
!= (sp_r
= strtok(sp_b
, "\t"))); i
++, sp_b
= NULL
)
535 case 0: strncpy(item
.id
, sp_r
, PATH_MAX
); break;
536 case 1: tc2frames(sp_r
, 25.0, &item
.in
); break;
537 case 2: tc2frames(sp_r
, 25.0, &item
.dur
); break;
538 case 3: strncpy(item
.title
, sp_r
, PATH_MAX
); break;
546 g_warning("omnplay_library_load_file: ignored line [%s]\n", l
);
559 g_warning("omnplay_library_load_file: loaded [%d] items from [%s] file, limit [%d]\n", c
, filename
, limit
);
565 static void omnplay_library_save_file(playlist_item_t
* item
, int count
, char* filename
)
570 if((f
= fopen(filename
, "wt")))
572 char tc_in
[32], tc_dur
[32];
574 for(i
= 0; i
< count
; i
++)
575 fprintf(f
, "%s\t%s\t%s\t%s\n",
577 frames2tc(item
[i
].in
, 25.0, tc_in
),
578 frames2tc(item
[i
].dur
, 25.0, tc_dur
),
581 g_warning("omnplay_library_save_file: written [%d] lines to file [%s]\n", count
, filename
);
585 void omnplay_library_save(omnplay_instance_t
* app
)
587 pthread_mutex_lock(&app
->library
.lock
);
589 if(app
->library
.filename
[0])
590 omnplay_library_save_file(app
->library
.item
, app
->library
.count
,
591 app
->library
.filename
);
593 pthread_mutex_unlock(&app
->library
.lock
);
596 static void omnplay_get_content_cb(omnplay_instance_t
* app
, playlist_item_t
* item
, void* data
)
598 if(!(app
->library
.id_display_idx
% app
->library
.id_display_rate
))
599 omnplay_set_status(app
, item
->id
);
600 app
->library
.id_display_idx
++;
603 static void* omnplay_library_refresh_proc(void* data
)
605 omnplay_instance_t
* app
= (omnplay_instance_t
*)data
;
607 playlist_item_t
* items
;
610 gtk_widget_set_sensitive(app
->window
, FALSE
);
614 omnplay_set_status(app
, "Updating library...");
616 items
= (playlist_item_t
*)malloc(sizeof(playlist_item_t
) * MAX_LIBRARY_ITEMS
);
618 count
= omnplay_get_content(app
, items
, MAX_LIBRARY_ITEMS
, omnplay_get_content_cb
, NULL
);
622 omnplay_set_status(app
, "Quering whois...");
624 if(app
->library
.whois
[0])
625 omnplay_whois_list(app
, items
, &count
);
627 omnplay_set_status(app
, "Setting library...");
629 pthread_mutex_lock(&app
->library
.lock
);
631 for(i
= 0; i
< count
; i
++)
632 app
->library
.item
[i
] = items
[i
];
634 app
->library
.count
= count
;
636 omnplay_library_sort(app
);
638 pthread_mutex_unlock(&app
->library
.lock
);
641 omnplay_library_draw(app
);
646 omnplay_set_status(app
, "Normalizing playlist...");
651 omnplay_playlist_normalize(app
);
655 omnplay_set_status(app
, "");
658 gtk_widget_set_sensitive(app
->window
, TRUE
);
666 void omnplay_library_refresh(omnplay_instance_t
* app
)
668 if(app
->library
.refresh_thread
)
669 g_thread_join(app
->library
.refresh_thread
);
671 app
->library
.refresh_thread
= g_thread_create(
672 omnplay_library_refresh_proc
, app
, TRUE
, NULL
);
675 void omnplay_library_draw(omnplay_instance_t
* app
)
679 GtkListStore
*list_store
;
682 list_store
= GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(app
->library_grid
)));
683 gtk_list_store_clear(list_store
);
685 pthread_mutex_lock(&app
->library
.lock
);
687 for(i
= 0;i
< app
->library
.count
; i
++)
689 gtk_list_store_append(list_store
, &iter
);
691 gtk_list_store_set(list_store
, &iter
,
692 0, app
->library
.item
[i
].id
,
693 1, frames2tc(app
->library
.item
[i
].dur
, 25.0, tc
),
694 2, app
->library
.item
[i
].title
,
701 pthread_mutex_unlock(&app
->library
.lock
);
704 static void get_selected_idx_library_proc(GtkTreeModel
*model
, GtkTreePath
*path
, GtkTreeIter
*iter
, gpointer data
)
706 int idx
, *list
= (int*)data
;
707 gtk_tree_model_get(model
, iter
, 3, &idx
, -1);
708 list
[list
[0] + 1] = idx
;
709 list
[0] = list
[0] + 1;
712 static int* get_selected_idx_library(omnplay_instance_t
* app
)
715 GtkTreeSelection
*selection
;
717 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(app
->library_grid
));
720 list
= (int*)malloc(sizeof(int) * (MAX_LIBRARY_ITEMS
+ 1));
721 memset(list
, 0, sizeof(int) * (MAX_LIBRARY_ITEMS
+ 1));
723 gtk_tree_selection_selected_foreach(
725 get_selected_idx_library_proc
,
739 playlist_item_t
* omnplay_library_get_selected(omnplay_instance_t
* app
, int *count
)
742 playlist_item_t
* items
= NULL
;
744 pthread_mutex_lock(&app
->library
.lock
);
748 idxs
= get_selected_idx_library(app
);
755 items
= (playlist_item_t
*)malloc(sizeof(playlist_item_t
) * (idxs
[0] + 1));
757 /* clear last item */
758 memset(&items
[idxs
[0]], 0, sizeof(playlist_item_t
));
761 for(i
= 0; i
< idxs
[0]; i
++)
762 items
[i
] = app
->library
.item
[idxs
[i
+ 1]];
768 pthread_mutex_unlock(&app
->library
.lock
);
773 void omnplay_library_search(omnplay_instance_t
* app
, int next
)
780 pthread_mutex_lock(&app
->library
.lock
);
782 idxs
= get_selected_idx_library(app
);
783 if(idxs
) idx
= idxs
[1];
789 search
= gtk_entry_get_text(GTK_ENTRY(app
->library
.search
));
793 for(i
= idx
; i
< app
->library
.count
; i
++)
794 if( strcasestr(app
->library
.item
[i
].id
, search
) ||
795 strcasestr(app
->library
.item
[i
].title
, search
))
798 if(i
< app
->library
.count
)
800 g_warning("found at pos=%d\n", i
);
803 path
= gtk_tree_path_new_from_indices(i
, -1);
804 gtk_tree_selection_select_path(gtk_tree_view_get_selection(
805 GTK_TREE_VIEW(app
->library_grid
)), path
);
806 gtk_tree_view_set_cursor(GTK_TREE_VIEW(app
->library_grid
), path
, NULL
, FALSE
);
807 gtk_tree_path_free(path
);
811 pthread_mutex_unlock(&app
->library
.lock
);