3 #include <glib/gi18n-lib.h>
5 #include <gdk/gdkkeysyms.h>
6 #include <gmpc/plugin.h>
7 #include <gmpc/playlist3-messages.h>
8 #include <gmpc/gmpc-metaimage.h>
10 #include <libmpd/libmpd-internal.h>
12 #include <gmpc/gmpc-extras.h>
15 const GType
albumview_plugin_get_type(void);
16 #define ALBUM_VIEW_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), albumview_plugin_get_type(), AlbumViewPlugin))
17 #define AV_LOG_DOMAIN "AlbumViewPlugin"
19 typedef struct _AlbumViewPluginPrivate
{
20 int supported_columns
;
23 GtkWidget
*filter_entry
;
24 GtkWidget
*slider_scale
;
25 GtkWidget
*progress_bar
;
26 GtkWidget
*item_table
;
27 GtkWidget
*albumview_box
;
28 GtkWidget
*albumview_main_box
;
30 gboolean require_scale_update
;
34 MpdData
*complete_list
;
39 GtkTreeRowReference
*albumview_ref
;
40 }_AlbumViewPluginPrivate
;
44 static gchar
* albumview_format_time(unsigned long seconds
);
45 static void albumview_browser_save_myself(GmpcPluginBase
*plug
);
46 static void position_changed(GtkRange
*range
, gpointer data
);
47 static void filter_list(GtkEntry
*entry
, gpointer data
);
48 void update_view(AlbumViewPlugin
*self
);
49 static void load_list(AlbumViewPlugin
*self
);
54 static int albumview_get_enabled(GmpcPluginBase
*plug
)
56 return cfg_get_single_value_as_int_with_default(config
, "albumview", "enable", TRUE
);
59 void albumview_set_enabled(GmpcPluginBase
*plug
, int enabled
)
61 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(plug
);
62 cfg_set_single_value_as_int(config
, "albumview", "enable", enabled
);
65 if(self
->priv
->albumview_ref
== NULL
)
67 albumview_add(GMPC_PLUGIN_BROWSER_IFACE(plug
), GTK_WIDGET(playlist3_get_category_tree_view()));
72 GtkTreePath
*path
= gtk_tree_row_reference_get_path(self
->priv
->albumview_ref
);
73 GtkTreeModel
*model
= gtk_tree_row_reference_get_model(self
->priv
->albumview_ref
);
76 if (gtk_tree_model_get_iter(model
, &iter
, path
)){
77 gtk_list_store_remove(GTK_LIST_STORE(model
), &iter
);
79 gtk_tree_path_free(path
);
80 gtk_tree_row_reference_free(self
->priv
->albumview_ref
);
81 self
->priv
->albumview_ref
= NULL
;
87 * Playlist browser functions
89 static void albumview_add(GmpcPluginBrowserIface
*plug
, GtkWidget
*category_tree
)
91 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(plug
);
93 GtkTreeModel
*model
= GTK_TREE_MODEL(playlist3_get_category_tree_store());
97 * don't do anything if we are disabled
99 if(!cfg_get_single_value_as_int_with_default(config
, "albumview", "enable", TRUE
)) return;
101 * Add ourslef to the list
103 pos
= cfg_get_single_value_as_int_with_default(config
, "albumview","position",2);
104 playlist3_insert_browser(&iter
, pos
);
105 gtk_list_store_set(GTK_LIST_STORE(model
), &iter
,
106 PL3_CAT_TYPE
, GMPC_PLUGIN_BASE(plug
)->id
,
107 PL3_CAT_TITLE
,"Album View",
108 PL3_CAT_ICON_ID
, "albumview",
111 * remove odl reference if exists
113 if (self
->priv
->albumview_ref
) {
114 gtk_tree_row_reference_free(self
->priv
->albumview_ref
);
115 self
->priv
->albumview_ref
= NULL
;
118 * create reference to ourself in the list
120 path
= gtk_tree_model_get_path(GTK_TREE_MODEL(model
), &iter
);
122 self
->priv
->albumview_ref
= gtk_tree_row_reference_new(model
, path
);
123 gtk_tree_path_free(path
);
126 static void albumview_browser_save_myself(GmpcPluginBase
*plug
)
128 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(plug
);
129 if(self
->priv
->albumview_ref
)
131 GtkTreePath
*path
= gtk_tree_row_reference_get_path(self
->priv
->albumview_ref
);
134 gint
*indices
= gtk_tree_path_get_indices(path
);
135 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Saving myself to position: %i", indices
[0]);
136 cfg_set_single_value_as_int(config
, "albumview","position",indices
[0]);
137 gtk_tree_path_free(path
);
142 void size_changed(GtkWidget
*widget
, GtkAllocation
*alloc
, gpointer user_data
)
144 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(user_data
);
145 int columns
= (alloc
->width
-10)/(self
->priv
->album_size
+25);
146 int rows
= (alloc
->height
-10)/(self
->priv
->album_size
+40);
148 if(columns
!= self
->priv
->supported_columns
|| rows
!= self
->priv
->supported_rows
)
150 self
->priv
->supported_columns
= (columns
)?columns
:1;
151 self
->priv
->supported_rows
= (rows
)?rows
:1;
152 printf("supported rows: %i\n", self
->priv
->supported_rows
);
153 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "update columns: %i %i %i\n",
154 alloc
->width
-20,columns
, self
->priv
->album_size
);
155 self
->priv
->require_scale_update
= TRUE
;
156 if(self
->priv
->filter_entry
&& GTK_WIDGET_IS_SENSITIVE(self
->priv
->filter_entry
))
164 static gboolean
albumview_scroll_event(GtkWidget
*event_box
, GdkEventScroll
*event
, gpointer data
)
166 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
167 if(self
->priv
->current_item
== NULL
) return FALSE
;
168 if(event
->direction
== GDK_SCROLL_UP
)
170 int value
= gtk_range_get_value(GTK_RANGE(self
->priv
->slider_scale
))-1;
171 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), value
);
173 }else if(event
->direction
== GDK_SCROLL_DOWN
) {
174 int value
= gtk_range_get_value(GTK_RANGE(self
->priv
->slider_scale
))+1;
175 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), value
);
180 static gboolean
albumview_key_press_event(GtkWidget
*event_box
, GdkEventKey
*event
, gpointer data
)
182 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
183 if(self
->priv
->current_item
== NULL
) return FALSE
;
185 if(event
->keyval
== GDK_Up
){
186 int value
= gtk_range_get_value(GTK_RANGE(self
->priv
->slider_scale
))-1;
187 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), value
);
189 }else if (event
->keyval
== GDK_Down
){
190 int value
= gtk_range_get_value(GTK_RANGE(self
->priv
->slider_scale
))+1;
191 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), value
);
193 }else if (event
->keyval
== GDK_Page_Up
) {
194 int value
= gtk_range_get_value(GTK_RANGE(self
->priv
->slider_scale
))-5;
195 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), value
);
198 }else if (event
->keyval
== GDK_Page_Down
) {
199 int value
= gtk_range_get_value(GTK_RANGE(self
->priv
->slider_scale
))+5;
200 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), value
);
207 static gboolean
albumview_button_press_event(GtkWidget
*event_box
, GdkEventButton
*event
, gpointer data
)
209 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
210 if(self
->priv
->current_item
== NULL
) return FALSE
;
211 gtk_widget_grab_focus(self
->priv
->event_bg
);
213 static gboolean
albumview_focus(GtkWidget
*wid
,GdkEventFocus
*event
, gpointer data
)
215 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
216 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "focus in");
217 gtk_widget_queue_draw(self
->priv
->event_bg
);
220 static gboolean
albumview_focus_out(GtkWidget
*wid
,GdkEventFocus
*event
, gpointer data
)
222 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
223 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "focus out");
225 gtk_widget_queue_draw(self
->priv
->event_bg
);
228 static gboolean
albumview_expose_event(GtkWidget
*widget
, GdkEventExpose
*event
, gpointer data
)
230 int width
= widget
->allocation
.width
;
231 int height
= widget
->allocation
.height
;
232 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
234 gtk_paint_flat_box(widget
->style
,
242 if(gtk_widget_is_focus(widget
))
244 gtk_paint_focus(widget
->style
, widget
->window
,
253 #if GTK_CHECK_VERSION(2,16,0)
254 static void mod_fill_clear_search_entry(GtkEntry
*entry
, GtkEntryIconPosition icon_pos
, GdkEvent
*event
, gpointer user_data
)
256 if(icon_pos
== GTK_ENTRY_ICON_SECONDARY
){
257 gtk_entry_set_text(GTK_ENTRY(entry
), "");
261 static void albumview_init(AlbumViewPlugin
*self
)
263 /** Get an allready exposed widgets to grab theme colors from. */
264 GtkWidget
*colw
= (GtkWidget
*)playlist3_get_category_tree_view();
265 GtkWidget
*label
= NULL
;
266 GtkWidget
*event
= gtk_scrolled_window_new(NULL
, NULL
);
267 GtkWidget
*hbox
= NULL
; int i
= 0;
268 self
->priv
->event_bg
= gtk_event_box_new();
269 self
->priv
->albumview_main_box
= gtk_vbox_new(FALSE
, 6);
271 g_signal_connect(G_OBJECT(event
), "size-allocate", G_CALLBACK(size_changed
), self
);
273 GtkWidget
*iv
= self
->priv
->albumview_box
= gtk_vbox_new(FALSE
, 6);
274 self
->priv
->slider_scale
= gtk_vscale_new_with_range(0,1,1);
275 gtk_scale_set_draw_value(GTK_SCALE(self
->priv
->slider_scale
), FALSE
);
276 g_signal_connect(G_OBJECT(self
->priv
->slider_scale
), "value-changed", G_CALLBACK(position_changed
), self
);
278 self
->priv
->filter_entry
= gtk_entry_new();
280 #if GTK_CHECK_VERSION(2,16,0)
281 gtk_entry_set_icon_from_stock(GTK_ENTRY(self
->priv
->filter_entry
), GTK_ENTRY_ICON_SECONDARY
, GTK_STOCK_CLEAR
);
282 g_signal_connect(GTK_ENTRY(self
->priv
->filter_entry
), "icon-press", G_CALLBACK(mod_fill_clear_search_entry
), NULL
);
284 g_signal_connect(G_OBJECT(self
->priv
->filter_entry
),"changed", G_CALLBACK(filter_list
), self
);
287 hbox
= gtk_hbox_new(FALSE
, 6);
288 gtk_box_pack_start(GTK_BOX(hbox
),gtk_label_new(("Filter")), FALSE
, FALSE
, 0);
289 gtk_box_pack_start(GTK_BOX(hbox
),self
->priv
->filter_entry
, TRUE
, TRUE
, 0);
291 gtk_box_pack_end(GTK_BOX(self
->priv
->albumview_main_box
), hbox
, FALSE
, FALSE
, 0);
293 hbox
= gtk_hbox_new(FALSE
, 6);
294 gtk_box_pack_start(GTK_BOX(self
->priv
->albumview_main_box
),hbox
, TRUE
, TRUE
, 0);
295 gtk_box_pack_start(GTK_BOX(hbox
),event
, TRUE
, TRUE
, 0);
296 gtk_box_pack_start(GTK_BOX(hbox
),self
->priv
->slider_scale
, FALSE
, FALSE
, 0);//gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw), event);
297 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(event
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
298 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(event
), GTK_SHADOW_ETCHED_IN
);
300 /* TODO draw focus */
301 // gtk_widget_modify_bg(self->priv->event_bg, GTK_STATE_NORMAL,&(self->priv->albumview_main_box->style->white));
302 gtk_widget_set_app_paintable(self
->priv
->event_bg
, TRUE
);
303 g_signal_connect(G_OBJECT(self
->priv
->event_bg
), "expose-event", G_CALLBACK(albumview_expose_event
), self
);
304 gtk_event_box_set_visible_window(GTK_EVENT_BOX(self
->priv
->event_bg
), TRUE
);
306 g_object_set(self
->priv
->event_bg
, "can-focus", TRUE
,NULL
);
307 GTK_WIDGET_SET_FLAGS(self
->priv
->event_bg
, GTK_HAS_FOCUS
);
308 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(event
), self
->priv
->event_bg
);
309 gtk_container_add(GTK_CONTAINER(self
->priv
->event_bg
), iv
);
310 gtk_widget_add_events(self
->priv
->event_bg
, GDK_SCROLL_MASK
|GDK_BUTTON_PRESS_MASK
|GDK_FOCUS_CHANGE_MASK
);
311 g_signal_connect_object(G_OBJECT(self
->priv
->event_bg
), "scroll-event", G_CALLBACK(albumview_scroll_event
), self
,0);
312 g_signal_connect_object(G_OBJECT(self
->priv
->event_bg
), "key-press-event", G_CALLBACK(albumview_key_press_event
), self
,0);
313 g_signal_connect_object(G_OBJECT(self
->priv
->event_bg
), "focus-in-event", G_CALLBACK(albumview_focus
), self
,0);
314 g_signal_connect_object(G_OBJECT(self
->priv
->event_bg
), "focus-out-event", G_CALLBACK(albumview_focus_out
), self
, 0);
315 g_signal_connect_object(G_OBJECT(self
->priv
->filter_entry
), "key-press-event", G_CALLBACK(albumview_key_press_event
), self
,0);
316 g_signal_connect_object(G_OBJECT(self
->priv
->event_bg
), "button-press-event", G_CALLBACK(albumview_button_press_event
), self
,0);
318 gtk_widget_show_all(self
->priv
->albumview_main_box
);
321 /* maintain my own reference to the widget, so it won't get destroyed removing
324 g_object_ref_sink(self
->priv
->albumview_main_box
);
328 static void albumview_selected(GmpcPluginBrowserIface
*plug
, GtkWidget
*container
)
330 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(plug
);
331 if(self
->priv
->albumview_main_box
== NULL
) {
332 albumview_init((AlbumViewPlugin
*)plug
);
333 albumview_connection_changed(gmpcconn
, connection
,1,self
);
335 gtk_container_add(GTK_CONTAINER(container
), self
->priv
->albumview_main_box
);
336 gtk_widget_show(self
->priv
->albumview_main_box
);
337 gtk_widget_show(container
);
338 gtk_widget_grab_focus(self
->priv
->event_bg
);
341 static void albumview_unselected(GmpcPluginBrowserIface
*plug
,GtkWidget
*container
)
343 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(plug
);
344 gtk_container_remove(GTK_CONTAINER(container
), self
->priv
->albumview_main_box
);
347 static void status_changed(GmpcConnection
*gmpcconnn
,MpdObj
*mi
, ChangedStatusType type
, AlbumViewPlugin
*self
)
349 if((type
&MPD_CST_DATABASE
) > 0)
352 if(self
->priv
->albumview_main_box
)
357 void albumview_plugin_init(AlbumViewPlugin
*self
)
360 const gchar
* const *paths
= g_get_system_data_dirs();
363 /* First try the compile time path */
364 path
= g_build_filename(PIXMAP_DATA_DIR
, NULL
);
366 if(!g_file_test(path
, G_FILE_TEST_EXISTS
|G_FILE_TEST_IS_DIR
))
373 for(i
=0; path
== NULL
&& paths
&& paths
[i
]; i
++) {
374 path
= g_build_filename(paths
[i
], "gmpc-albumview", "icons", NULL
);
375 if(!g_file_test(path
, G_FILE_TEST_EXISTS
|G_FILE_TEST_IS_DIR
))
382 gtk_icon_theme_append_search_path(gtk_icon_theme_get_default (),path
);
384 g_signal_connect_object(G_OBJECT(gmpcconn
), "status-changed", G_CALLBACK(status_changed
), self
, 0);
389 #define TIMER_SUB(start,stop,diff) diff.tv_usec = stop.tv_usec - start.tv_usec;\
390 diff.tv_sec = stop.tv_sec - start.tv_sec;\
391 if(diff.tv_usec < 0) {\
393 diff.tv_usec += G_USEC_PER_SEC; \
395 static gint
__add_sort(gpointer aa
, gpointer bb
, gpointer data
)
397 MpdData_real
*a
= *(MpdData_real
**)aa
;
398 MpdData_real
*b
= *(MpdData_real
**)bb
;
399 if(!a
|| !b
) return 0;
400 if(a
->type
== MPD_DATA_TYPE_SONG
&& b
->type
== MPD_DATA_TYPE_SONG
)
402 if(a
->song
->artist
&& b
->song
->artist
)
407 sa
= g_utf8_strdown(a
->song
->artist
, -1);
408 sb
= g_utf8_strdown(b
->song
->artist
, -1);
409 val
= g_utf8_collate(sa
,sb
);
413 val = (a == NULL)?((b==NULL)?0:-1):1;
417 if (a
->song
->album
&& b
->song
->album
)
420 sa
= g_utf8_strdown(a
->song
->album
, -1);
421 sb
= g_utf8_strdown(b
->song
->album
, -1);
422 val
= g_utf8_collate(sa
,sb
);
433 static gboolean
update_progressbar(AlbumViewPlugin
*self
)
435 gchar
*temp
= g_strdup_printf("%i of %i albums loaded", self
->priv
->current_entry
, self
->priv
->max_entries
);
436 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(self
->priv
->progress_bar
), self
->priv
->current_entry
/(double)self
->priv
->max_entries
);
437 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(self
->priv
->progress_bar
), temp
);
442 static void load_list_itterate(MpdObj
*mi
, AlbumViewPlugin
*self
)
445 MpdData
*data2
= NULL
;
446 self
->priv
->current_entry
++;
447 if(self
->priv
->max_entries
>0 && self
->priv
->current_entry
%25 == 0){
448 g_idle_add(update_progressbar
, self
);
452 mpd_database_search_field_start(mi
, MPD_TAG_ITEM_ARTIST
);
453 mpd_database_search_add_constraint(mi
, MPD_TAG_ITEM_ALBUM
, (self
->priv
->data
)->tag
);
454 data2
= mpd_database_search_commit(mi
);
457 mpd_Song
*song
= mpd_newSong();
458 song
->album
= g_strdup((self
->priv
->data
)->tag
);
459 song
->artist
= g_strdup(data2
->tag
);
460 if(!mpd_data_is_last(data2
))
462 /* test if server supports album artist */
463 if(mpd_server_tag_supported(mi
, MPD_TAG_ITEM_ALBUM_ARTIST
))
465 mpd_database_search_field_start(mi
, MPD_TAG_ITEM_ALBUM_ARTIST
);
466 mpd_database_search_add_constraint(mi
, MPD_TAG_ITEM_ALBUM
, (self
->priv
->data
)->tag
);
467 MpdData
*data3
= mpd_database_search_commit(mi
);
468 if(mpd_data_is_last(data3
)){
469 if(strlen(data3
->tag
) > 0)
471 song
->albumartist
= g_strdup(data3
->tag
);
472 if(song
->artist
) g_free(song
->artist
);
473 song
->artist
= g_strdup(data3
->tag
);
480 mpd_data_free(data3
);
487 mpd_data_free(data2
);
489 self
->priv
->complete_list
= mpd_new_data_struct_append(self
->priv
->complete_list
);
490 self
->priv
->complete_list
->song
= song
;
491 self
->priv
->complete_list
->type
= MPD_DATA_TYPE_SONG
;
496 (self
->priv
->data
) = mpd_data_get_next((self
->priv
->data
));
499 while(self
->priv
->data
!= NULL
);
500 self
->priv
->complete_list
= (MpdData
*)misc_sort_mpddata(mpd_data_get_first(self
->priv
->complete_list
), (GCompareDataFunc
)__add_sort
, NULL
);
503 void update_finished(MpdData
*data
, AlbumViewPlugin
*self
)
505 if(self
->priv
->data
== NULL
){
508 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
,"update view\n");
509 gtk_widget_destroy(self
->priv
->progress_bar
);
510 self
->priv
->progress_bar
= NULL
;
511 for(iter
= (MpdData_real
*)self
->priv
->complete_list
; iter
; iter
= iter
->next
) items
++;
513 gtk_widget_set_sensitive(self
->priv
->filter_entry
, TRUE
);
514 filter_list(GTK_ENTRY(self
->priv
->filter_entry
), self
);
516 gtk_widget_grab_focus(self
->priv
->event_bg
);
520 // g_idle_add((GSourceFunc)load_list_itterate, self);
524 static void load_list(AlbumViewPlugin
*self
)
526 if(self
->priv
->complete_list
)mpd_data_free(self
->priv
->complete_list
);
527 self
->priv
->complete_list
= NULL
;
528 if(self
->priv
->current_item
) g_list_free(self
->priv
->current_item
);
529 self
->priv
->current_item
= NULL
;
532 self
->priv
->progress_bar
= gtk_progress_bar_new();
533 gtk_box_pack_start(GTK_BOX(self
->priv
->albumview_box
), self
->priv
->progress_bar
, FALSE
, FALSE
, 0);
534 gtk_widget_show(self
->priv
->progress_bar
);
535 mpd_database_search_field_start(connection
, MPD_TAG_ITEM_ALBUM
);
536 MpdData
*iter
,*data
= mpd_database_search_commit(connection
);
537 self
->priv
->max_entries
= 0;
538 self
->priv
->current_entry
= 0;
539 gtk_widget_set_sensitive(self
->priv
->filter_entry
, FALSE
);
540 for(iter
= data
; iter
; iter
= mpd_data_get_next_real(iter
, FALSE
)) self
->priv
->max_entries
++;
541 self
->priv
->data
= data
;
542 mpd_async_request(update_finished
, self
, load_list_itterate
, self
);
544 void albumview_connection_changed(GmpcConnection
*conn
, MpdObj
*mi
, int connect
,void *usedata
)
546 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(usedata
);
548 if(connect
&& self
->priv
->albumview_main_box
)
552 else if(self
->priv
->albumview_main_box
){
553 mpd_data_free(self
->priv
->complete_list
);
554 self
->priv
->complete_list
= NULL
;
555 if(self
->priv
->item_table
)
556 gtk_widget_hide(self
->priv
->item_table
);
559 static void album_add(GtkWidget
*button
, mpd_Song
*song
)
561 mpd_database_search_start(connection
,TRUE
);
563 mpd_database_search_add_constraint(connection
, MPD_TAG_ITEM_ALBUM
, song
->album
);
564 if(song
->albumartist
&& strlen(song
->albumartist
) >0){
565 mpd_database_search_add_constraint(connection
, MPD_TAG_ITEM_ALBUM_ARTIST
, song
->albumartist
);
567 mpd_database_search_add_constraint(connection
, MPD_TAG_ITEM_ARTIST
, song
->artist
);
569 MpdData
*data
= mpd_database_search_commit(connection
);
570 /* Sort it before adding */
571 data
= misc_sort_mpddata_by_album_disc_track(data
);
572 for(;data
;data
= mpd_data_get_next(data
)){
573 mpd_playlist_queue_add(connection
, data
->song
->file
);
575 mpd_playlist_queue_commit(connection
);
577 static void album_view(GtkWidget
*button
,mpd_Song
*song
)
579 if (song
&& song
->artist
&& song
->album
) {
581 info2_fill_album_view(song
->artist
, song
->album
);
585 static void album_replace(GtkWidget
*button
, mpd_Song
*song
)
587 mpd_playlist_clear(connection
);
588 album_add(button
, song
);
589 mpd_player_play(connection
);
591 static gboolean
album_button_press(GtkWidget
*image
, GtkMenu
*menu
, mpd_Song
*song
)
595 item
= gtk_image_menu_item_new_with_label("Album information");
596 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
),
597 gtk_image_new_from_stock(GTK_STOCK_INFO
, GTK_ICON_SIZE_MENU
));
598 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
599 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(album_view
), song
);
601 item
= gtk_image_menu_item_new_from_stock(GTK_STOCK_ADD
,NULL
);
602 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
603 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(album_add
), song
);
605 /* replace the replace widget */
606 item
= gtk_image_menu_item_new_with_label("Replace");
607 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
),
608 gtk_image_new_from_stock(GTK_STOCK_REDO
, GTK_ICON_SIZE_MENU
));
609 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
610 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(album_replace
), song
);
612 item
= gtk_separator_menu_item_new();
613 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
615 gtk_widget_show(item
);
619 static GtkWidget
* create_button(AlbumViewPlugin
*self
, MpdData_real
*complete_list_iter
)
623 GtkWidget
*label
= NULL
;
625 /* Wrap it in a vbox */
626 vbox
= gtk_vbox_new(FALSE
, 3);
627 gtk_widget_set_size_request(vbox
, self
->priv
->album_size
+20,self
->priv
->album_size
+40);
629 item
= (GtkWidget
*)gmpc_metaimage_new_size(META_ALBUM_ART
,self
->priv
->album_size
);
630 gmpc_metaimage_set_scale_up(GMPC_METAIMAGE(item
), TRUE
);
631 gtk_widget_set_has_tooltip(GTK_WIDGET(item
), FALSE
);
632 gmpc_metaimage_set_squared(GMPC_METAIMAGE(item
), TRUE
);
634 gmpc_metaimage_update_cover_from_song_delayed(GMPC_METAIMAGE(item
), complete_list_iter
->song
);
636 gtk_box_pack_start(GTK_BOX(vbox
), item
, TRUE
, TRUE
, 0);
637 /* Set artist name */
638 if(complete_list_iter
->song
->albumartist
){
639 GtkWidget
*label
= gtk_label_new(complete_list_iter
->song
->albumartist
);
640 gtk_label_set_ellipsize(GTK_LABEL(label
), PANGO_ELLIPSIZE_MIDDLE
);
641 gtk_box_pack_end(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
643 GtkWidget
*label
= gtk_label_new(complete_list_iter
->song
->artist
);
644 gtk_label_set_ellipsize(GTK_LABEL(label
), PANGO_ELLIPSIZE_MIDDLE
);
645 gtk_box_pack_end(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
649 label
= gtk_label_new("");
650 temp
= g_markup_printf_escaped("<b>%s</b>", complete_list_iter
->song
->album
);
651 gtk_label_set_markup(GTK_LABEL(label
), temp
);
653 gtk_label_set_ellipsize(GTK_LABEL(label
), PANGO_ELLIPSIZE_MIDDLE
);
654 gtk_box_pack_end(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
657 /* Attach it to the song */
658 g_object_add_weak_pointer(G_OBJECT(vbox
),&(complete_list_iter
->userdata
));
659 complete_list_iter
->userdata
= vbox
;//g_object_ref_sink(vbox);
660 complete_list_iter
->freefunc
= (void *)gtk_widget_destroy
;
661 g_object_set_data(G_OBJECT(vbox
), "item", item
);
662 g_signal_connect(G_OBJECT(item
), "menu_populate_client", G_CALLBACK(album_button_press
), complete_list_iter
->song
);
664 g_signal_connect(item, "button-press-event",
665 G_CALLBACK(album_button_press), complete_list_iter->song);
670 static void filter_list(GtkEntry
*entry
, gpointer data
)
672 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
673 GRegex
*regex
= NULL
;
676 const gchar
*search_query
= gtk_entry_get_text(GTK_ENTRY(self
->priv
->filter_entry
));
677 MpdData_real
*complete_list_iter
= NULL
;
678 if(search_query
[0] != '\0')
680 gchar
*str
= g_strdup(search_query
);
681 gchar
**test
= g_strsplit(g_strstrip(str
), " ", -1);
683 GString
*s
= g_string_new("((?:");
684 GError
*error
= NULL
;
686 for(i
=0;test
&& test
[i
];i
++){
687 gchar
*temp
= g_regex_escape_string(test
[i
], -1);
688 s
= g_string_append(s
, ".*");
689 s
= g_string_append(s
, temp
);
690 s
= g_string_append(s
, ".*");
691 if(test
[i
+1] != NULL
)
692 s
= g_string_append(s
, "|");
695 g_string_append_printf(s
,"){%i})",i
);
696 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
,"regex: %s\n", s
->str
);
697 regex
= g_regex_new(s
->str
, G_REGEX_CASELESS
|G_REGEX_EXTENDED
, 0,&error
);;
700 g_string_free(s
, TRUE
);
701 for(complete_list_iter
= (MpdData_real
*) mpd_data_get_first(self
->priv
->complete_list
);
703 complete_list_iter
= (MpdData_real
*)mpd_data_get_next_real((MpdData
*)complete_list_iter
, FALSE
))
705 if(g_regex_match(regex
,complete_list_iter
->song
->album
,0,NULL
)||
706 g_regex_match(regex
,complete_list_iter
->song
->artist
,0,NULL
)||
707 (complete_list_iter
->song
->albumartist
&& g_regex_match(regex
,complete_list_iter
->song
->albumartist
,0,NULL
))){
709 list
= g_list_append(list
, complete_list_iter
);
714 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_WARNING
," error creating regex: %s\n", error
->message
);
717 g_regex_unref(regex
);
719 if(self
->priv
->current_item
) g_list_free(self
->priv
->current_item
);
720 self
->priv
->current_item
= g_list_first(list
);
721 self
->priv
->require_scale_update
= TRUE
;
722 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), 0);
727 static void position_changed(GtkRange
*range
, gpointer data
)
729 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
730 gint i
=0,value
= (int)gtk_range_get_value(range
)*self
->priv
->supported_columns
;
731 self
->priv
->current_item
= g_list_first(self
->priv
->current_item
);
732 for(i
=0;i
<value
&& self
->priv
->current_item
&& self
->priv
->current_item
->next
; self
->priv
->current_item
= self
->priv
->current_item
->next
){i
++;}
735 static gboolean
update_view_real(AlbumViewPlugin
*self
)
737 MpdData
*complete_list_iter
;
738 const char *search_query
= gtk_entry_get_text(GTK_ENTRY(self
->priv
->filter_entry
));
743 MpdData
*data
= NULL
;
744 GList
*entries
= NULL
;
745 GList
*list
= (self
->priv
->item_table
)?gtk_container_get_children(GTK_CONTAINER(self
->priv
->item_table
)):NULL
;
747 GRegex
*regex
= NULL
;
749 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
,"search query: %s\n", search_query
);
751 if(self
->priv
->item_table
)
752 gtk_widget_hide(self
->priv
->item_table
);
753 for(iter
= g_list_first(list
); iter
; iter
= iter
->next
){
754 GtkWidget
*widget
= iter
->data
;
755 gtk_container_remove(GTK_CONTAINER(self
->priv
->item_table
), widget
);
762 gtk_widget_show(self
->priv
->albumview_box
);
764 if(self
->priv
->current_item
== NULL
){
766 for(complete_list_iter
= mpd_data_get_first(self
->priv
->complete_list
);
768 complete_list_iter
= mpd_data_get_next_real(complete_list_iter
, FALSE
))
771 self
->priv
->current_item
= g_list_prepend(self
->priv
->current_item
, complete_list_iter
);
773 self
->priv
->current_item
= g_list_reverse(self
->priv
->current_item
);
774 // self->priv->current_item = g_list_first(self->priv->current_item);
775 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), 0);
776 self
->priv
->require_scale_update
= TRUE
;
778 /* TODO: Only update this when something changed! */
779 if(self
->priv
->require_scale_update
)
781 int items
= g_list_length(g_list_first(self
->priv
->current_item
));
782 if(ceil(items
/(double)self
->priv
->supported_columns
)-self
->priv
->supported_rows
> 0)
784 gtk_widget_set_sensitive(GTK_WIDGET(self
->priv
->slider_scale
), TRUE
);
785 gtk_range_set_range(GTK_RANGE(self
->priv
->slider_scale
), 0,
786 ceil(items
/(double)self
->priv
->supported_columns
)-self
->priv
->supported_rows
);
789 gtk_widget_set_sensitive(GTK_WIDGET(self
->priv
->slider_scale
), FALSE
);
790 gtk_range_set_range(GTK_RANGE(self
->priv
->slider_scale
), 0,1);
792 self
->priv
->require_scale_update
= FALSE
;
796 * Create holding table if it does not exist
798 if(!self
->priv
->item_table
){
799 self
->priv
->item_table
= (GtkWidget
*)gmpc_widgets_qtable_new();
800 gmpc_widgets_qtable_set_item_width(GMPC_WIDGETS_QTABLE(self
->priv
->item_table
),
801 self
->priv
->album_size
+25);
802 gmpc_widgets_qtable_set_item_height(GMPC_WIDGETS_QTABLE(self
->priv
->item_table
),
803 self
->priv
->album_size
+40);
804 gtk_box_pack_start(GTK_BOX(self
->priv
->albumview_box
), self
->priv
->item_table
, TRUE
, TRUE
, 0);
811 if(self
->priv
->current_item
)
813 int rows
= self
->priv
->supported_rows
;
814 GList
*iter
= self
->priv
->current_item
;
818 complete_list_iter
= iter
->data
;
819 if(complete_list_iter
->song
)
821 GtkWidget
*vbox
= complete_list_iter
->userdata
;
825 vbox
= create_button(self
, (MpdData_real
*)complete_list_iter
);
828 item
= g_object_get_data(G_OBJECT(vbox
), "item");
829 /* Resize if needed */
830 if(self
->priv
->album_size
!= gmpc_metaimage_get_size(GMPC_METAIMAGE(item
))){
831 gtk_widget_set_size_request(vbox
, self
->priv
->album_size
+20,self
->priv
->album_size
+40);
832 gmpc_metaimage_set_size(GMPC_METAIMAGE(item
), self
->priv
->album_size
);
833 gmpc_metaimage_reload_image(GMPC_METAIMAGE(item
));
837 entries
= g_list_prepend(entries
, vbox
);
841 }while(v_items
< (rows
*self
->priv
->supported_columns
)&& (iter
= iter
->next
));
844 if(list
) g_list_free(list
);
847 for(iter
= entries
= g_list_reverse(entries
); iter
; iter
= g_list_next(iter
)){
848 gtk_container_add(GTK_CONTAINER(self
->priv
->item_table
), iter
->data
);
850 if(entries
) g_list_free(entries
);
852 gtk_widget_show_all(self
->priv
->albumview_box
);
856 if(self
->priv
->update_timeout
)
857 g_source_remove(self
->priv
->update_timeout
);
858 self
->priv
->update_timeout
= 0;
863 void update_view(AlbumViewPlugin
*self
)
865 if(self
->priv
->update_timeout
!= 0) {
866 g_source_remove(self
->priv
->update_timeout
);
868 self
->priv
->update_timeout
= g_timeout_add(10, (GSourceFunc
)update_view_real
,self
);
874 static void albumview_plugin_class_init (AlbumViewPluginClass
*klass
);
876 static int *albumview_plugin_get_version(GmpcPluginBase
*plug
, int *length
)
878 static int version
[3] = {PLUGIN_MAJOR_VERSION
,PLUGIN_MINOR_VERSION
,PLUGIN_MICRO_VERSION
};
879 if(length
) *length
= 3;
880 return (int *)version
;
883 static const char *albumview_plugin_get_name(GmpcPluginBase
*plug
)
885 return ("Album View");
887 static GObject
*albumview_plugin_constructor(GType type
, guint n_construct_properties
, GObjectConstructParam
* construct_properties
) {
888 AlbumViewPluginClass
* klass
;
889 AlbumViewPlugin
*self
;
890 GObjectClass
* parent_class
;
891 klass
= (g_type_class_peek (albumview_plugin_get_type()));
892 parent_class
= G_OBJECT_CLASS (g_type_class_peek_parent (klass
));
893 self
= (AlbumViewPlugin
*) parent_class
->constructor (type
, n_construct_properties
, construct_properties
);
895 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Constructing plugin");
897 /* setup private structure */
898 self
->priv
= g_malloc0(sizeof(AlbumViewPluginPrivate
));
900 self
->priv
->supported_rows
= 1;
901 self
->priv
->supported_columns
= 1;
902 self
->priv
->data
= NULL
;
903 self
->priv
->album_size
= 200;
904 self
->priv
->filter_entry
= NULL
;
905 self
->priv
->slider_scale
= NULL
;
906 self
->priv
->progress_bar
= NULL
;
907 self
->priv
->max_entries
= 0;
908 self
->priv
->current_entry
= 0;
909 self
->priv
->current_item
= NULL
;
910 self
->priv
->update_timeout
= 0;
911 self
->priv
->complete_list
= NULL
;
912 self
->priv
->item_table
= NULL
;
913 self
->priv
->albumview_ref
= NULL
;
914 self
->priv
->albumview_box
= NULL
;
915 self
->priv
->require_scale_update
= FALSE
;
917 /* Watch status changed signals */
918 g_signal_connect_object(G_OBJECT(gmpcconn
), "connection-changed", G_CALLBACK(albumview_connection_changed
), self
, 0);
920 /* Setup textdomain */
921 bindtextdomain(GETTEXT_PACKAGE
, PACKAGE_LOCALE_DIR
);
922 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8");
924 GMPC_PLUGIN_BASE(self
)->translation_domain
= GETTEXT_PACKAGE
;
925 GMPC_PLUGIN_BASE(self
)->plugin_type
= GMPC_PLUGIN_NO_GUI
;
927 albumview_plugin_init(self
);
929 return G_OBJECT(self
);
931 static void albumview_plugin_finalize(GObject
*obj
) {
932 AlbumViewPlugin
*self
= (AlbumViewPlugin
*)obj
;
933 AlbumViewPluginClass
* klass
= (g_type_class_peek (play_queue_plugin_get_type()));
934 gpointer parent_class
= g_type_class_peek_parent (klass
);
936 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Destroying plugin");
939 if(self
->priv
->current_item
) g_list_free(self
->priv
->current_item
);
940 self
->priv
->current_item
= NULL
;
941 if(self
->priv
->complete_list
) mpd_data_free(self
->priv
->complete_list
);
942 self
->priv
->complete_list
= NULL
;
948 G_OBJECT_CLASS(parent_class
)->finalize(obj
);
952 static void albumview_plugin_class_init (AlbumViewPluginClass
*klass
)
954 G_OBJECT_CLASS(klass
)->finalize
= albumview_plugin_finalize
;
955 G_OBJECT_CLASS(klass
)->constructor
= albumview_plugin_constructor
;
956 /* Connect plugin functions */
957 GMPC_PLUGIN_BASE_CLASS(klass
)->get_version
= albumview_plugin_get_version
;
958 GMPC_PLUGIN_BASE_CLASS(klass
)->get_name
= albumview_plugin_get_name
;
960 GMPC_PLUGIN_BASE_CLASS(klass
)->get_enabled
= albumview_get_enabled
;
961 GMPC_PLUGIN_BASE_CLASS(klass
)->set_enabled
= albumview_set_enabled
;
963 GMPC_PLUGIN_BASE_CLASS(klass
)->save_yourself
= albumview_browser_save_myself
;
966 static void albumview_plugin_browser_iface_init(GmpcPluginBrowserIfaceIface
* iface
) {
967 iface
->browser_add
= albumview_add
;
968 iface
->browser_selected
= albumview_selected
;
969 iface
->browser_unselected
= albumview_unselected
;
972 const GType
albumview_plugin_get_type(void) {
973 static GType albumview_plugin_type_id
= 0;
974 if(albumview_plugin_type_id
== 0) {
975 static const GTypeInfo info
= {
976 .class_size
= sizeof(AlbumViewPluginClass
),
977 .class_init
= (GClassInitFunc
)albumview_plugin_class_init
,
978 .instance_size
= sizeof(AlbumViewPlugin
),
982 albumview_plugin_type_id
= g_type_register_static(GMPC_PLUGIN_TYPE_BASE
, "AlbumViewPlugin", &info
, 0);
984 /** Browser interface */
985 static const GInterfaceInfo iface_info
= { (GInterfaceInitFunc
) albumview_plugin_browser_iface_init
,
986 (GInterfaceFinalizeFunc
) NULL
, NULL
};
987 g_type_add_interface_static (albumview_plugin_type_id
, GMPC_PLUGIN_TYPE_BROWSER_IFACE
, &iface_info
);
989 return albumview_plugin_type_id
;
992 G_MODULE_EXPORT GType
plugin_get_type(void)
994 return albumview_plugin_get_type();