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 static void load_list(AlbumViewPlugin
*self
)
522 if(self
->priv
->complete_list
)mpd_data_free(self
->priv
->complete_list
);
523 self
->priv
->complete_list
= NULL
;
524 if(self
->priv
->current_item
) g_list_free(self
->priv
->current_item
);
525 self
->priv
->current_item
= NULL
;
528 self
->priv
->progress_bar
= gtk_progress_bar_new();
529 gtk_box_pack_start(GTK_BOX(self
->priv
->albumview_box
), self
->priv
->progress_bar
, FALSE
, FALSE
, 0);
530 gtk_widget_show(self
->priv
->progress_bar
);
531 mpd_database_search_field_start(connection
, MPD_TAG_ITEM_ALBUM
);
532 MpdData
*iter
,*data
= mpd_database_search_commit(connection
);
533 self
->priv
->max_entries
= 0;
534 self
->priv
->current_entry
= 0;
535 gtk_widget_set_sensitive(self
->priv
->filter_entry
, FALSE
);
536 for(iter
= data
; iter
; iter
= mpd_data_get_next_real(iter
, FALSE
)) self
->priv
->max_entries
++;
537 self
->priv
->data
= data
;
538 mpd_async_request(update_finished
, self
, load_list_itterate
, self
);
540 void albumview_connection_changed(GmpcConnection
*conn
, MpdObj
*mi
, int connect
,void *usedata
)
542 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(usedata
);
544 if(connect
&& self
->priv
->albumview_main_box
)
548 else if(self
->priv
->albumview_main_box
){
549 mpd_data_free(self
->priv
->complete_list
);
550 self
->priv
->complete_list
= NULL
;
551 if(self
->priv
->item_table
)
552 gtk_widget_hide(self
->priv
->item_table
);
555 static void album_add(GtkWidget
*button
, mpd_Song
*song
)
557 mpd_database_search_start(connection
,TRUE
);
559 mpd_database_search_add_constraint(connection
, MPD_TAG_ITEM_ALBUM
, song
->album
);
560 if(song
->albumartist
&& strlen(song
->albumartist
) >0){
561 mpd_database_search_add_constraint(connection
, MPD_TAG_ITEM_ALBUM_ARTIST
, song
->albumartist
);
563 mpd_database_search_add_constraint(connection
, MPD_TAG_ITEM_ARTIST
, song
->artist
);
565 MpdData
*data
= mpd_database_search_commit(connection
);
566 /* Sort it before adding */
567 data
= misc_sort_mpddata_by_album_disc_track(data
);
568 for(;data
;data
= mpd_data_get_next(data
)){
569 mpd_playlist_queue_add(connection
, data
->song
->file
);
571 mpd_playlist_queue_commit(connection
);
573 static void album_view(GtkWidget
*button
,mpd_Song
*song
)
575 if (song
&& song
->artist
&& song
->album
) {
577 info2_fill_album_view(song
->artist
, song
->album
);
581 static void album_replace(GtkWidget
*button
, mpd_Song
*song
)
583 mpd_playlist_clear(connection
);
584 album_add(button
, song
);
585 mpd_player_play(connection
);
587 static gboolean
album_button_press(GtkWidget
*image
, GtkMenu
*menu
, mpd_Song
*song
)
591 item
= gtk_image_menu_item_new_with_label("Album information");
592 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
),
593 gtk_image_new_from_stock(GTK_STOCK_INFO
, GTK_ICON_SIZE_MENU
));
594 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
595 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(album_view
), song
);
597 item
= gtk_image_menu_item_new_from_stock(GTK_STOCK_ADD
,NULL
);
598 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
599 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(album_add
), song
);
601 /* replace the replace widget */
602 item
= gtk_image_menu_item_new_with_label("Replace");
603 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
),
604 gtk_image_new_from_stock(GTK_STOCK_REDO
, GTK_ICON_SIZE_MENU
));
605 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
606 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(album_replace
), song
);
608 item
= gtk_separator_menu_item_new();
609 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
611 gtk_widget_show(item
);
615 static GtkWidget
* create_button(AlbumViewPlugin
*self
, MpdData_real
*complete_list_iter
)
619 GtkWidget
*label
= NULL
;
621 /* Wrap it in a vbox */
622 vbox
= gtk_vbox_new(FALSE
, 3);
623 gtk_widget_set_size_request(vbox
, self
->priv
->album_size
+20,self
->priv
->album_size
+40);
625 item
= (GtkWidget
*)gmpc_metaimage_new_size(META_ALBUM_ART
,self
->priv
->album_size
);
626 gmpc_metaimage_set_scale_up(GMPC_METAIMAGE(item
), TRUE
);
627 gtk_widget_set_has_tooltip(GTK_WIDGET(item
), FALSE
);
628 gmpc_metaimage_set_squared(GMPC_METAIMAGE(item
), TRUE
);
630 gmpc_metaimage_update_cover_from_song_delayed(GMPC_METAIMAGE(item
), complete_list_iter
->song
);
632 gtk_box_pack_start(GTK_BOX(vbox
), item
, TRUE
, TRUE
, 0);
633 /* Set artist name */
634 if(complete_list_iter
->song
->albumartist
){
635 GtkWidget
*label
= gtk_label_new(complete_list_iter
->song
->albumartist
);
636 gtk_label_set_ellipsize(GTK_LABEL(label
), PANGO_ELLIPSIZE_MIDDLE
);
637 gtk_box_pack_end(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
639 GtkWidget
*label
= gtk_label_new(complete_list_iter
->song
->artist
);
640 gtk_label_set_ellipsize(GTK_LABEL(label
), PANGO_ELLIPSIZE_MIDDLE
);
641 gtk_box_pack_end(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
645 label
= gtk_label_new("");
646 temp
= g_markup_printf_escaped("<b>%s</b>", complete_list_iter
->song
->album
);
647 gtk_label_set_markup(GTK_LABEL(label
), temp
);
649 gtk_label_set_ellipsize(GTK_LABEL(label
), PANGO_ELLIPSIZE_MIDDLE
);
650 gtk_box_pack_end(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
653 /* Attach it to the song */
654 g_object_add_weak_pointer(G_OBJECT(vbox
),&(complete_list_iter
->userdata
));
655 complete_list_iter
->userdata
= vbox
;//g_object_ref_sink(vbox);
656 complete_list_iter
->freefunc
= (void *)gtk_widget_destroy
;
657 g_object_set_data(G_OBJECT(vbox
), "item", item
);
658 g_signal_connect(G_OBJECT(item
), "menu_populate_client", G_CALLBACK(album_button_press
), complete_list_iter
->song
);
660 g_signal_connect(item, "button-press-event",
661 G_CALLBACK(album_button_press), complete_list_iter->song);
666 static void filter_list(GtkEntry
*entry
, gpointer data
)
668 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
669 GRegex
*regex
= NULL
;
672 const gchar
*search_query
= gtk_entry_get_text(GTK_ENTRY(self
->priv
->filter_entry
));
673 MpdData_real
*complete_list_iter
= NULL
;
674 if(search_query
[0] != '\0')
676 gchar
*str
= g_strdup(search_query
);
677 gchar
**test
= g_strsplit(g_strstrip(str
), " ", -1);
679 GString
*s
= g_string_new("((?:");
680 GError
*error
= NULL
;
682 for(i
=0;test
&& test
[i
];i
++){
683 gchar
*temp
= g_regex_escape_string(test
[i
], -1);
684 s
= g_string_append(s
, ".*");
685 s
= g_string_append(s
, temp
);
686 s
= g_string_append(s
, ".*");
687 if(test
[i
+1] != NULL
)
688 s
= g_string_append(s
, "|");
691 g_string_append_printf(s
,"){%i})",i
);
692 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
,"regex: %s\n", s
->str
);
693 regex
= g_regex_new(s
->str
, G_REGEX_CASELESS
|G_REGEX_EXTENDED
, 0,&error
);;
696 g_string_free(s
, TRUE
);
697 for(complete_list_iter
= (MpdData_real
*) mpd_data_get_first(self
->priv
->complete_list
);
699 complete_list_iter
= (MpdData_real
*)mpd_data_get_next_real((MpdData
*)complete_list_iter
, FALSE
))
701 if(g_regex_match(regex
,complete_list_iter
->song
->album
,0,NULL
)||
702 g_regex_match(regex
,complete_list_iter
->song
->artist
,0,NULL
)||
703 (complete_list_iter
->song
->albumartist
&& g_regex_match(regex
,complete_list_iter
->song
->albumartist
,0,NULL
))){
705 list
= g_list_append(list
, complete_list_iter
);
710 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_WARNING
," error creating regex: %s\n", error
->message
);
713 g_regex_unref(regex
);
715 if(self
->priv
->current_item
) g_list_free(self
->priv
->current_item
);
716 self
->priv
->current_item
= g_list_first(list
);
717 self
->priv
->require_scale_update
= TRUE
;
718 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), 0);
723 static void position_changed(GtkRange
*range
, gpointer data
)
725 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
726 gint i
=0,value
= (int)gtk_range_get_value(range
)*self
->priv
->supported_columns
;
727 self
->priv
->current_item
= g_list_first(self
->priv
->current_item
);
728 for(i
=0;i
<value
&& self
->priv
->current_item
&& self
->priv
->current_item
->next
; self
->priv
->current_item
= self
->priv
->current_item
->next
){i
++;}
731 static gboolean
update_view_real(AlbumViewPlugin
*self
)
733 MpdData
*complete_list_iter
;
734 const char *search_query
= gtk_entry_get_text(GTK_ENTRY(self
->priv
->filter_entry
));
739 MpdData
*data
= NULL
;
740 GList
*entries
= NULL
;
741 GList
*list
= (self
->priv
->item_table
)?gtk_container_get_children(GTK_CONTAINER(self
->priv
->item_table
)):NULL
;
743 GRegex
*regex
= NULL
;
745 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
,"search query: %s\n", search_query
);
747 if(self
->priv
->item_table
)
748 gtk_widget_hide(self
->priv
->item_table
);
749 for(iter
= g_list_first(list
); iter
; iter
= iter
->next
){
750 GtkWidget
*widget
= iter
->data
;
751 gtk_container_remove(GTK_CONTAINER(self
->priv
->item_table
), widget
);
758 gtk_widget_show(self
->priv
->albumview_box
);
760 if(self
->priv
->current_item
== NULL
){
762 for(complete_list_iter
= mpd_data_get_first(self
->priv
->complete_list
);
764 complete_list_iter
= mpd_data_get_next_real(complete_list_iter
, FALSE
))
767 self
->priv
->current_item
= g_list_prepend(self
->priv
->current_item
, complete_list_iter
);
769 self
->priv
->current_item
= g_list_reverse(self
->priv
->current_item
);
770 // self->priv->current_item = g_list_first(self->priv->current_item);
771 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), 0);
772 self
->priv
->require_scale_update
= TRUE
;
774 /* TODO: Only update this when something changed! */
775 if(self
->priv
->require_scale_update
)
777 int items
= g_list_length(g_list_first(self
->priv
->current_item
));
778 if(ceil(items
/(double)self
->priv
->supported_columns
)-self
->priv
->supported_rows
> 0)
780 gtk_widget_set_sensitive(GTK_WIDGET(self
->priv
->slider_scale
), TRUE
);
781 gtk_range_set_range(GTK_RANGE(self
->priv
->slider_scale
), 0,
782 ceil(items
/(double)self
->priv
->supported_columns
)-self
->priv
->supported_rows
);
785 gtk_widget_set_sensitive(GTK_WIDGET(self
->priv
->slider_scale
), FALSE
);
786 gtk_range_set_range(GTK_RANGE(self
->priv
->slider_scale
), 0,1);
788 self
->priv
->require_scale_update
= FALSE
;
792 * Create holding table if it does not exist
794 if(!self
->priv
->item_table
){
795 self
->priv
->item_table
= (GtkWidget
*)gmpc_widgets_qtable_new();
796 gmpc_widgets_qtable_set_item_width(GMPC_WIDGETS_QTABLE(self
->priv
->item_table
),
797 self
->priv
->album_size
+25);
798 gmpc_widgets_qtable_set_item_height(GMPC_WIDGETS_QTABLE(self
->priv
->item_table
),
799 self
->priv
->album_size
+40);
800 gtk_box_pack_start(GTK_BOX(self
->priv
->albumview_box
), self
->priv
->item_table
, TRUE
, TRUE
, 0);
807 if(self
->priv
->current_item
)
809 int rows
= self
->priv
->supported_rows
;
810 GList
*iter
= self
->priv
->current_item
;
814 complete_list_iter
= iter
->data
;
815 if(complete_list_iter
->song
)
817 GtkWidget
*vbox
= complete_list_iter
->userdata
;
821 vbox
= create_button(self
, (MpdData_real
*)complete_list_iter
);
824 item
= g_object_get_data(G_OBJECT(vbox
), "item");
825 /* Resize if needed */
826 if(self
->priv
->album_size
!= gmpc_metaimage_get_size(GMPC_METAIMAGE(item
))){
827 gtk_widget_set_size_request(vbox
, self
->priv
->album_size
+20,self
->priv
->album_size
+40);
828 gmpc_metaimage_set_size(GMPC_METAIMAGE(item
), self
->priv
->album_size
);
829 gmpc_metaimage_reload_image(GMPC_METAIMAGE(item
));
833 entries
= g_list_prepend(entries
, vbox
);
837 }while(v_items
< (rows
*self
->priv
->supported_columns
)&& (iter
= iter
->next
));
840 if(list
) g_list_free(list
);
843 for(iter
= entries
= g_list_reverse(entries
); iter
; iter
= g_list_next(iter
)){
844 gtk_container_add(GTK_CONTAINER(self
->priv
->item_table
), iter
->data
);
846 if(entries
) g_list_free(entries
);
848 gtk_widget_show_all(self
->priv
->albumview_box
);
852 if(self
->priv
->update_timeout
)
853 g_source_remove(self
->priv
->update_timeout
);
854 self
->priv
->update_timeout
= 0;
859 void update_view(AlbumViewPlugin
*self
)
861 if(self
->priv
->update_timeout
!= 0) {
862 g_source_remove(self
->priv
->update_timeout
);
864 self
->priv
->update_timeout
= g_timeout_add(10, (GSourceFunc
)update_view_real
,self
);
870 static void albumview_plugin_class_init (AlbumViewPluginClass
*klass
);
872 static int *albumview_plugin_get_version(GmpcPluginBase
*plug
, int *length
)
874 static int version
[3] = {PLUGIN_MAJOR_VERSION
,PLUGIN_MINOR_VERSION
,PLUGIN_MICRO_VERSION
};
875 if(length
) *length
= 3;
876 return (int *)version
;
879 static const char *albumview_plugin_get_name(GmpcPluginBase
*plug
)
881 return ("Album View");
883 static GObject
*albumview_plugin_constructor(GType type
, guint n_construct_properties
, GObjectConstructParam
* construct_properties
) {
884 AlbumViewPluginClass
* klass
;
885 AlbumViewPlugin
*self
;
886 GObjectClass
* parent_class
;
887 klass
= (g_type_class_peek (albumview_plugin_get_type()));
888 parent_class
= G_OBJECT_CLASS (g_type_class_peek_parent (klass
));
889 self
= (AlbumViewPlugin
*) parent_class
->constructor (type
, n_construct_properties
, construct_properties
);
891 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Constructing plugin");
893 /* setup private structure */
894 self
->priv
= g_malloc0(sizeof(AlbumViewPluginPrivate
));
896 self
->priv
->supported_rows
= 1;
897 self
->priv
->supported_columns
= 1;
898 self
->priv
->data
= NULL
;
899 self
->priv
->album_size
= 200;
900 self
->priv
->filter_entry
= NULL
;
901 self
->priv
->slider_scale
= NULL
;
902 self
->priv
->progress_bar
= NULL
;
903 self
->priv
->max_entries
= 0;
904 self
->priv
->current_entry
= 0;
905 self
->priv
->current_item
= NULL
;
906 self
->priv
->update_timeout
= 0;
907 self
->priv
->complete_list
= NULL
;
908 self
->priv
->item_table
= NULL
;
909 self
->priv
->albumview_ref
= NULL
;
910 self
->priv
->albumview_box
= NULL
;
911 self
->priv
->require_scale_update
= FALSE
;
913 /* Watch status changed signals */
914 g_signal_connect_object(G_OBJECT(gmpcconn
), "connection-changed", G_CALLBACK(albumview_connection_changed
), self
, 0);
916 /* Setup textdomain */
917 bindtextdomain(GETTEXT_PACKAGE
, PACKAGE_LOCALE_DIR
);
918 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8");
920 GMPC_PLUGIN_BASE(self
)->translation_domain
= GETTEXT_PACKAGE
;
921 GMPC_PLUGIN_BASE(self
)->plugin_type
= GMPC_PLUGIN_NO_GUI
;
923 albumview_plugin_init(self
);
925 return G_OBJECT(self
);
927 static void albumview_plugin_finalize(GObject
*obj
) {
928 AlbumViewPlugin
*self
= (AlbumViewPlugin
*)obj
;
929 AlbumViewPluginClass
* klass
= (g_type_class_peek (albumview_plugin_get_type()));
930 gpointer parent_class
= g_type_class_peek_parent (klass
);
932 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Destroying plugin");
935 if(self
->priv
->current_item
) g_list_free(self
->priv
->current_item
);
936 self
->priv
->current_item
= NULL
;
937 if(self
->priv
->complete_list
) mpd_data_free(self
->priv
->complete_list
);
938 self
->priv
->complete_list
= NULL
;
944 G_OBJECT_CLASS(parent_class
)->finalize(obj
);
948 static void albumview_plugin_class_init (AlbumViewPluginClass
*klass
)
950 G_OBJECT_CLASS(klass
)->finalize
= albumview_plugin_finalize
;
951 G_OBJECT_CLASS(klass
)->constructor
= albumview_plugin_constructor
;
952 /* Connect plugin functions */
953 GMPC_PLUGIN_BASE_CLASS(klass
)->get_version
= albumview_plugin_get_version
;
954 GMPC_PLUGIN_BASE_CLASS(klass
)->get_name
= albumview_plugin_get_name
;
956 GMPC_PLUGIN_BASE_CLASS(klass
)->get_enabled
= albumview_get_enabled
;
957 GMPC_PLUGIN_BASE_CLASS(klass
)->set_enabled
= albumview_set_enabled
;
959 GMPC_PLUGIN_BASE_CLASS(klass
)->save_yourself
= albumview_browser_save_myself
;
962 static void albumview_plugin_browser_iface_init(GmpcPluginBrowserIfaceIface
* iface
) {
963 iface
->browser_add
= albumview_add
;
964 iface
->browser_selected
= albumview_selected
;
965 iface
->browser_unselected
= albumview_unselected
;
968 const GType
albumview_plugin_get_type(void) {
969 static GType albumview_plugin_type_id
= 0;
970 if(albumview_plugin_type_id
== 0) {
971 static const GTypeInfo info
= {
972 .class_size
= sizeof(AlbumViewPluginClass
),
973 .class_init
= (GClassInitFunc
)albumview_plugin_class_init
,
974 .instance_size
= sizeof(AlbumViewPlugin
),
978 albumview_plugin_type_id
= g_type_register_static(GMPC_PLUGIN_TYPE_BASE
, "AlbumViewPlugin", &info
, 0);
980 /** Browser interface */
981 static const GInterfaceInfo iface_info
= { (GInterfaceInitFunc
) albumview_plugin_browser_iface_init
,
982 (GInterfaceFinalizeFunc
) NULL
, NULL
};
983 g_type_add_interface_static (albumview_plugin_type_id
, GMPC_PLUGIN_TYPE_BROWSER_IFACE
, &iface_info
);
985 return albumview_plugin_type_id
;
988 G_MODULE_EXPORT GType
plugin_get_type(void)
990 return albumview_plugin_get_type();