2 #include <gmpc/plugin.h>
3 #include <gmpc/playlist3-messages.h>
4 #include <gmpc/gmpc-metaimage.h>
6 #include <libmpd/libmpd-internal.h>
10 static gchar
* albumview_format_time(unsigned long seconds
);
11 static void albumview_browser_save_myself(void);
12 /* Allow gmpc to check the version the plugin is compiled against */
13 int plugin_api_version
= PLUGIN_API_VERSION
;
14 GtkTreeModel
*albumview_model
= NULL
;
16 void update_view(void);
20 gmpcPlBrowserPlugin albumview_gbp
= {
24 .selected
= albumview_selected
,
26 .unselected
= albumview_unselected
,
31 * Define the plugin structure
37 .version
= {PLUGIN_MAJOR_VERSION
,PLUGIN_MINOR_VERSION
,PLUGIN_MICRO_VERSION
},
39 .plugin_type
= GMPC_PLUGIN_PL_BROWSER
,
41 .init
= albumview_plugin_init
,
42 /** playlist extention struct */
43 .browser
= &albumview_gbp
,
44 /** Connection changed */
45 .mpd_connection_changed
= albumview_connection_changed
,
47 .get_enabled
= albumview_get_enabled
,
48 .set_enabled
= albumview_set_enabled
,
50 .save_yourself
= albumview_browser_save_myself
53 static GtkTreeRowReference
*albumview_ref
= NULL
;
54 static GtkWidget
*albumview_vbox
= NULL
, *albumview_tree
= NULL
,*albumview_combo
= NULL
;
55 static gboolean cancel_query
= FALSE
;
60 int albumview_get_enabled(void)
62 return cfg_get_single_value_as_int_with_default(config
, "albumview", "enable", TRUE
);
65 void albumview_set_enabled(int enabled
)
67 cfg_set_single_value_as_int(config
, "albumview", "enable", enabled
);
70 if(albumview_ref
== NULL
)
72 albumview_add(GTK_WIDGET(playlist3_get_category_tree_view()));
77 GtkTreePath
*path
= gtk_tree_row_reference_get_path(albumview_ref
);
78 GtkTreeModel
*model
= gtk_tree_row_reference_get_model(albumview_ref
);
81 if (gtk_tree_model_get_iter(model
, &iter
, path
)){
82 gtk_list_store_remove(GTK_LIST_STORE(model
), &iter
);
84 gtk_tree_path_free(path
);
85 gtk_tree_row_reference_free(albumview_ref
);
92 * Playlist browser functions
94 static void albumview_add(GtkWidget
*category_tree
)
97 GtkTreeModel
*model
= GTK_TREE_MODEL(playlist3_get_category_tree_store());
101 * don't do anything if we are disabled
103 if(!cfg_get_single_value_as_int_with_default(config
, "albumview", "enable", TRUE
)) return;
105 * Add ourslef to the list
107 pos
= cfg_get_single_value_as_int_with_default(config
, "albumview","position",2);
108 playlist3_insert_browser(&iter
, pos
);
109 gtk_list_store_set(GTK_LIST_STORE(model
), &iter
,
110 PL3_CAT_TYPE
, plugin
.id
,
111 PL3_CAT_TITLE
,"Album View",
112 PL3_CAT_ICON_ID
, "gtk-open",
115 * remove odl reference if exists
118 gtk_tree_row_reference_free(albumview_ref
);
119 albumview_ref
= NULL
;
122 * create reference to ourself in the list
124 path
= gtk_tree_model_get_path(GTK_TREE_MODEL(model
), &iter
);
126 albumview_ref
= gtk_tree_row_reference_new(model
, path
);
127 gtk_tree_path_free(path
);
130 static void albumview_browser_save_myself(void)
134 GtkTreePath
*path
= gtk_tree_row_reference_get_path(albumview_ref
);
137 gint
*indices
= gtk_tree_path_get_indices(path
);
138 debug_printf(DEBUG_INFO
,"Saving myself to position: %i\n", indices
[0]);
139 cfg_set_single_value_as_int(config
, "albumview","position",indices
[0]);
140 gtk_tree_path_free(path
);
145 GtkWidget
*entry
= NULL
;
146 int album_size
= 200;
147 int supported_columns
= 1;
148 void size_changed(GtkWidget
*widget
, GtkAllocation
*alloc
)
150 int columns
= (alloc
->width
-20)/(album_size
+25);
151 if(columns
!= supported_columns
)
153 supported_columns
= columns
;
154 printf("update columns: %i %i %i\n", alloc
->width
-20,columns
, album_size
);
155 if(entry
&& GTK_WIDGET_IS_SENSITIVE(entry
))
161 void album_size_changed(GtkRange
*spin
)
163 int new_size
= ((int)gtk_range_get_value(spin
))*20;
164 if(new_size
!= album_size
) {
165 album_size
= new_size
;
166 size_changed(albumview_vbox
, &(albumview_vbox
->allocation
));
168 if(entry && GTK_WIDGET_IS_SENSITIVE(entry))
174 cfg_set_single_value_as_int(config
, "albumview", "zoom-level", (int)gtk_range_get_value(spin
));
176 static void albumview_init()
178 /** Get an allready exposed widgets to grab theme colors from. */
179 GtkWidget
*colw
= (GtkWidget
*)playlist3_get_category_tree_view();
180 GtkWidget
*label
= NULL
;
181 GtkWidget
*table
= NULL
;
182 GtkWidget
*event
= gtk_event_box_new();
184 albumview_vbox
= gtk_vbox_new(FALSE
, 0);
186 album_size
= 20*cfg_get_single_value_as_int_with_default(config
, "albumview", "zoom-level", 10);
187 g_signal_connect(G_OBJECT(albumview_vbox
), "size-allocate", G_CALLBACK(size_changed
), NULL
);
189 GtkWidget
*sw
= gtk_scrolled_window_new(NULL
, NULL
);
190 GtkWidget
*iv
= albumview_tree
= gtk_vbox_new(FALSE
, 6);
193 entry
= gtk_entry_new();
194 gtk_box_pack_start(GTK_BOX(albumview_vbox
), entry
, FALSE
, FALSE
, 0);
195 g_signal_connect(G_OBJECT(entry
),"changed", G_CALLBACK(update_view
), NULL
);
197 GtkWidget
*spin
= gtk_hscale_new_with_range(1, 12, 1);
198 gtk_scale_set_draw_value(GTK_SCALE(spin
), FALSE
);
199 gtk_range_set_value(GTK_RANGE(spin
), album_size
);
200 gtk_box_pack_start(GTK_BOX(albumview_vbox
), spin
, FALSE
, FALSE
, 0);
202 #if GTK_CHECK_VERSION(2,16,0)
204 gtk_scale_add_mark(GTK_RANGE(spin
), (gdouble
)i
,GTK_POS_TOP
, NULL
);
208 gtk_range_set_value(GTK_RANGE(spin
), cfg_get_single_value_as_int_with_default(config
, "albumview", "zoom-level", 10));
209 g_signal_connect(G_OBJECT(spin
), "value-changed", G_CALLBACK(album_size_changed
), NULL
);
212 gtk_box_pack_start(GTK_BOX(albumview_vbox
),sw
, TRUE
, TRUE
, 0);
213 gtk_widget_modify_bg(event
, GTK_STATE_NORMAL
,&(albumview_vbox
->style
->white
));
215 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw
), event
);
216 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
217 gtk_container_add(GTK_CONTAINER(event
), iv
);
219 gtk_widget_show_all(albumview_vbox
);
220 /* maintain my own reference to the widget, so it won't get destroyed removing
223 g_object_ref(albumview_vbox
);
227 static void albumview_selected(GtkWidget
*container
)
229 if(albumview_vbox
== NULL
) {
231 albumview_connection_changed(connection
,1,NULL
);
233 gtk_container_add(GTK_CONTAINER(container
), albumview_vbox
);
234 gtk_widget_show(albumview_vbox
);
237 static void albumview_unselected(GtkWidget
*container
)
239 gtk_container_remove(GTK_CONTAINER(container
), albumview_vbox
);
244 void albumview_plugin_init(void)
246 gchar
*path
= gmpc_plugin_get_data_path(&plugin
);
247 gchar
*url
= g_build_path(G_DIR_SEPARATOR_S
,path
, "albumview", NULL
);
248 debug_printf(DEBUG_WARNING
,"Found url: %s\n", url
);
251 gtk_icon_theme_append_search_path(gtk_icon_theme_get_default (),url
);
256 #define TIMER_SUB(start,stop,diff) diff.tv_usec = stop.tv_usec - start.tv_usec;\
257 diff.tv_sec = stop.tv_sec - start.tv_sec;\
258 if(diff.tv_usec < 0) {\
260 diff.tv_usec += G_USEC_PER_SEC; \
262 static gint
__add_sort(gpointer aa
, gpointer bb
, gpointer data
)
264 MpdData_real
*a
= *(MpdData_real
**)aa
;
265 MpdData_real
*b
= *(MpdData_real
**)bb
;
266 if(!a
|| !b
) return 0;
267 if(a
->type
== MPD_DATA_TYPE_SONG
&& b
->type
== MPD_DATA_TYPE_SONG
)
269 if(a
->song
->artist
&& b
->song
->artist
)
274 sa
= g_utf8_strdown(a
->song
->artist
, -1);
275 sb
= g_utf8_strdown(b
->song
->artist
, -1);
276 val
= g_utf8_collate(sa
,sb
);
280 val = (a == NULL)?((b==NULL)?0:-1):1;
287 MpdData
*complete_list
= NULL
;
289 int max
= 0, current
= 0;
290 static gboolean
load_list_itterate(MpdData
*data
)
292 MpdData
*data2
= NULL
;
295 gchar
*temp
= g_strdup_printf("%i of %i albums loaded", current
, max
);
296 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(pb
), current
/(double)max
);
297 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(pb
), temp
);
303 mpd_database_search_field_start(connection
, MPD_TAG_ITEM_ARTIST
);
304 mpd_database_search_add_constraint(connection
, MPD_TAG_ITEM_ALBUM
, (data
)->tag
);
305 data2
= mpd_database_search_commit(connection
);
308 mpd_Song
*song
= mpd_newSong();
309 song
->album
= g_strdup((data
)->tag
);
310 song
->artist
= g_strdup(data2
->tag
);
311 if(!mpd_data_is_last(data2
))
313 /* test if server supports album artist */
314 if(mpd_server_tag_supported(connection
, MPD_TAG_ITEM_ALBUM_ARTIST
))
316 mpd_database_search_field_start(connection
, MPD_TAG_ITEM_ALBUM_ARTIST
);
317 mpd_database_search_add_constraint(connection
, MPD_TAG_ITEM_ALBUM
, (data
)->tag
);
318 MpdData
*data3
= mpd_database_search_commit(connection
);
319 if(mpd_data_is_last(data3
)){
320 song
->albumartist
= g_strdup(data3
->tag
);
326 mpd_data_free(data3
);
333 mpd_data_free(data2
);
335 complete_list
= mpd_new_data_struct_append(complete_list
);
336 complete_list
->song
= song
;
337 complete_list
->type
= MPD_DATA_TYPE_SONG
;
342 (data
) = mpd_data_get_next((data
));
345 complete_list
= (MpdData
*)misc_sort_mpddata(mpd_data_get_first(complete_list
), (GCompareDataFunc
)__add_sort
, NULL
);
346 printf("update view\n");
347 gtk_widget_destroy(pb
);
350 gtk_widget_set_sensitive(entry
, TRUE
);
354 g_idle_add((GSourceFunc
)load_list_itterate
, data
);
358 static GtkWidget
*table
= NULL
;
359 static void load_list(void)
361 mpd_data_free(complete_list
);
362 complete_list
= NULL
;
364 pb
= gtk_progress_bar_new();
365 gtk_box_pack_start(GTK_BOX(albumview_tree
), pb
, FALSE
, FALSE
, 0);
366 gtk_widget_show_all(albumview_tree
);
367 mpd_database_search_field_start(connection
, MPD_TAG_ITEM_ALBUM
);
368 MpdData
*iter
,*data
= mpd_database_search_commit(connection
);
371 gtk_widget_set_sensitive(entry
, FALSE
);
372 for(iter
= data
; iter
; iter
= mpd_data_get_next_real(iter
, FALSE
)) max
++;
373 g_idle_add((GSourceFunc
)load_list_itterate
, data
);
375 void albumview_connection_changed(MpdObj
*mi
, int connect
,void *usedata
)
378 if(connect
&& albumview_vbox
)
382 else if(albumview_vbox
){
383 mpd_data_free(complete_list
);
384 complete_list
= NULL
;
387 static void album_add(GtkWidget
*button
, mpd_Song
*song
)
389 mpd_database_search_start(connection
,TRUE
);
391 mpd_database_search_add_constraint(connection
, MPD_TAG_ITEM_ALBUM
, song
->album
);
392 if(song
->albumartist
){
393 mpd_database_search_add_constraint(connection
, MPD_TAG_ITEM_ALBUM_ARTIST
, song
->albumartist
);
395 mpd_database_search_add_constraint(connection
, MPD_TAG_ITEM_ARTIST
, song
->artist
);
397 MpdData
*data
= mpd_database_search_commit(connection
);
398 for(;data
;data
= mpd_data_get_next(data
)){
399 mpd_playlist_queue_add(connection
, data
->song
->file
);
401 mpd_playlist_queue_commit(connection
);
403 static void album_view(GtkWidget
*button
,mpd_Song
*song
)
405 if (song
&& song
->artist
&& song
->album
) {
407 info2_fill_album_view(song
->artist
, song
->album
);
411 static void album_replace(GtkWidget
*button
, mpd_Song
*song
)
413 mpd_playlist_clear(connection
);
414 album_add(button
, song
);
415 mpd_player_play(connection
);
417 static gboolean
album_button_press(GtkWidget
*item
, GdkEventButton
*event
, mpd_Song
*song
)
419 printf("button press event %i\n", event
->button
);
420 if(event
->button
== 1 && event
->type
== GDK_BUTTON_PRESS
)
422 GtkWidget
*menu
= gtk_menu_new();
425 item
= gtk_image_menu_item_new_with_label("Album information");
426 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
),
427 gtk_image_new_from_stock(GTK_STOCK_INFO
, GTK_ICON_SIZE_MENU
));
428 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
429 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(album_view
), song
);
431 item
= gtk_image_menu_item_new_from_stock(GTK_STOCK_ADD
,NULL
);
432 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
433 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(album_add
), song
);
435 /* replace the replace widget */
436 item
= gtk_image_menu_item_new_with_label("Replace");
437 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
),
438 gtk_image_new_from_stock(GTK_STOCK_REDO
, GTK_ICON_SIZE_MENU
));
439 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
440 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(album_replace
), song
);
441 gtk_widget_show_all(menu
);
442 gtk_menu_popup(GTK_MENU(menu
), NULL
, NULL
, NULL
, NULL
, 0, event
->time
);
444 else if (event
->button
== 2){
445 album_add(NULL
, song
);
450 static guint update_timeout
= 0;
452 static gboolean
update_view_real(void)
454 MpdData
*complete_list_iter
;
455 const char *search_query
= gtk_entry_get_text(GTK_ENTRY(entry
));
457 GTimeVal start
, stop
,diff
;
461 MpdData
*data
= NULL
;
462 printf("search query: %s\n", search_query
);
464 GList
*list
= (table
)?gtk_container_get_children(GTK_CONTAINER(table
)):NULL
;
467 gtk_widget_hide(table
);
468 for(iter
= g_list_first(list
); iter
; iter
= iter
->next
){
469 GtkWidget
*widget
= iter
->data
;
470 gtk_container_remove(GTK_CONTAINER(table
), widget
);
474 g_get_current_time(&start
);
476 gtk_widget_show(albumview_tree
);
479 * Create a filtered linked list
481 for(complete_list_iter
= mpd_data_get_first(complete_list
);
483 complete_list_iter
= mpd_data_get_next_real(complete_list_iter
, FALSE
))
485 if(search_query
[0] == '\0' || strstr(complete_list_iter
->song
->artist
, search_query
)){
487 list
= g_list_append(list
, complete_list_iter
);
491 g_get_current_time(&stop
);
492 TIMER_SUB(start
, stop
, diff
);
493 printf("Removing old stuff time elapsed: %lu s %lus us\n", diff
.tv_sec
, diff
.tv_usec
);
494 int rows
= items
/supported_columns
+1;
496 * Create holding table if it does not exist
500 table
= exo_wrap_table_new(TRUE
);//gtk_table_new(rows, supported_columns, TRUE);
501 GtkWidget
*ali
= gtk_alignment_new(0.0, 0.5, 0,0);
502 gtk_container_add(GTK_CONTAINER(ali
), table
);
503 gtk_box_pack_start(GTK_BOX(albumview_tree
), ali
, FALSE
, FALSE
, 0);
505 // else gtk_table_resize(GTK_TABLE(table), rows, supported_columns);
507 /* I know how large it is going to be.. so lets set the size */
508 gtk_widget_set_size_request(table
, supported_columns
*(album_size
+20+6), rows
*(album_size
+40+6));
512 if((iter
= g_list_first(list
)))
515 complete_list_iter
= iter
->data
;
516 if(complete_list_iter
->song
&& (complete_list_iter
->song
->artist
)[0] != '\0')
518 GtkWidget
*vbox
= complete_list_iter
->userdata
;
522 GtkWidget
*label
= NULL
;
524 /* Wrap it in a vbox */
525 vbox
= gtk_vbox_new(FALSE
, 3);
526 gtk_widget_set_size_request(vbox
, album_size
+20,album_size
+40);
528 item
= gmpc_metaimage_new_size(META_ALBUM_ART
,album_size
);
529 gtk_widget_set_has_tooltip(GTK_WIDGET(item
), FALSE
);
530 gmpc_metaimage_set_squared(GMPC_METAIMAGE(item
), TRUE
);
532 gmpc_metaimage_update_cover_from_song_delayed(GMPC_METAIMAGE(item
), complete_list_iter
->song
);
534 gtk_box_pack_start(GTK_BOX(vbox
), item
, TRUE
, TRUE
, 0);
535 /* Set artist name */
536 if(complete_list_iter
->song
->albumartist
){
537 GtkWidget
*label
= gtk_label_new(complete_list_iter
->song
->albumartist
);
538 gtk_label_set_ellipsize(GTK_LABEL(label
), PANGO_ELLIPSIZE_MIDDLE
);
539 gtk_box_pack_end(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
541 GtkWidget
*label
= gtk_label_new(complete_list_iter
->song
->artist
);
542 gtk_label_set_ellipsize(GTK_LABEL(label
), PANGO_ELLIPSIZE_MIDDLE
);
543 gtk_box_pack_end(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
547 label
= gtk_label_new("");
548 temp
= g_markup_printf_escaped("<b>%s</b>", complete_list_iter
->song
->album
);
549 gtk_label_set_markup(GTK_LABEL(label
), temp
);
551 gtk_label_set_ellipsize(GTK_LABEL(label
), PANGO_ELLIPSIZE_MIDDLE
);
552 gtk_box_pack_end(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
555 /* Attach it to the song */
556 complete_list_iter
->userdata
= g_object_ref_sink(vbox
);
557 complete_list_iter
->freefunc
= gtk_widget_destroy
;
558 g_object_set_data(G_OBJECT(vbox
), "item", item
);
559 g_signal_connect(item
, "button-press-event",
560 G_CALLBACK(album_button_press
), complete_list_iter
->song
);
564 item
= g_object_get_data(G_OBJECT(vbox
), "item");
565 /* Resize if needed */
566 if(album_size
!= gmpc_metaimage_get_size(GMPC_METAIMAGE(item
))){
567 gtk_widget_set_size_request(vbox
, album_size
+20,album_size
+40);
568 gmpc_metaimage_set_size(GMPC_METAIMAGE(item
), album_size
);
569 gmpc_metaimage_reload_image(GMPC_METAIMAGE(item
));
573 a
= j
%supported_columns
;
574 b
= j
/supported_columns
;
576 gtk_table_attach(GTK_TABLE(table), vbox,
579 GTK_SHRINK, GTK_SHRINK,
583 gtk_container_add(GTK_CONTAINER(table
), vbox
);
586 }while((iter
= g_list_next(iter
)));
588 if(list
) g_list_free(list
);
591 g_get_current_time(&stop
);
592 TIMER_SUB(start
, stop
, diff
);
593 printf("Creating/Updating albums time elapsed: %lu s %lus us\n", diff
.tv_sec
, diff
.tv_usec
);
596 gtk_widget_show_all(albumview_tree
);
597 g_get_current_time(&stop
);
598 TIMER_SUB(start
, stop
, diff
);
599 printf("Showing widget time elapsed: %lu s %lus us\n", diff
.tv_sec
, diff
.tv_usec
);
604 g_source_remove(update_timeout
);
612 if(update_timeout
!= 0) {
613 g_source_remove(update_timeout
);
615 update_timeout
= g_timeout_add(500, (GSourceFunc
)update_view_real
,NULL
);