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 "exo-wrap-table.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
;
33 MpdData
*complete_list
;
38 GtkTreeRowReference
*albumview_ref
;
39 }_AlbumViewPluginPrivate
;
43 static gchar
* albumview_format_time(unsigned long seconds
);
44 static void albumview_browser_save_myself(GmpcPluginBase
*plug
);
45 static void position_changed(GtkRange
*range
, gpointer data
);
46 static void filter_list(GtkEntry
*entry
, gpointer data
);
47 void update_view(AlbumViewPlugin
*self
);
52 static int albumview_get_enabled(GmpcPluginBase
*plug
)
54 return cfg_get_single_value_as_int_with_default(config
, "albumview", "enable", TRUE
);
57 void albumview_set_enabled(GmpcPluginBase
*plug
, int enabled
)
59 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(plug
);
60 cfg_set_single_value_as_int(config
, "albumview", "enable", enabled
);
63 if(self
->priv
->albumview_ref
== NULL
)
65 albumview_add(GMPC_PLUGIN_BROWSER_IFACE(plug
), GTK_WIDGET(playlist3_get_category_tree_view()));
70 GtkTreePath
*path
= gtk_tree_row_reference_get_path(self
->priv
->albumview_ref
);
71 GtkTreeModel
*model
= gtk_tree_row_reference_get_model(self
->priv
->albumview_ref
);
74 if (gtk_tree_model_get_iter(model
, &iter
, path
)){
75 gtk_list_store_remove(GTK_LIST_STORE(model
), &iter
);
77 gtk_tree_path_free(path
);
78 gtk_tree_row_reference_free(self
->priv
->albumview_ref
);
79 self
->priv
->albumview_ref
= NULL
;
85 * Playlist browser functions
87 static void albumview_add(GmpcPluginBrowserIface
*plug
, GtkWidget
*category_tree
)
89 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(plug
);
91 GtkTreeModel
*model
= GTK_TREE_MODEL(playlist3_get_category_tree_store());
95 * don't do anything if we are disabled
97 if(!cfg_get_single_value_as_int_with_default(config
, "albumview", "enable", TRUE
)) return;
99 * Add ourslef to the list
101 pos
= cfg_get_single_value_as_int_with_default(config
, "albumview","position",2);
102 playlist3_insert_browser(&iter
, pos
);
103 gtk_list_store_set(GTK_LIST_STORE(model
), &iter
,
104 PL3_CAT_TYPE
, GMPC_PLUGIN_BASE(plug
)->id
,
105 PL3_CAT_TITLE
,"Album View",
106 PL3_CAT_ICON_ID
, "stylized-no-cover",
109 * remove odl reference if exists
111 if (self
->priv
->albumview_ref
) {
112 gtk_tree_row_reference_free(self
->priv
->albumview_ref
);
113 self
->priv
->albumview_ref
= NULL
;
116 * create reference to ourself in the list
118 path
= gtk_tree_model_get_path(GTK_TREE_MODEL(model
), &iter
);
120 self
->priv
->albumview_ref
= gtk_tree_row_reference_new(model
, path
);
121 gtk_tree_path_free(path
);
124 static void albumview_browser_save_myself(GmpcPluginBase
*plug
)
126 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(plug
);
127 if(self
->priv
->albumview_ref
)
129 GtkTreePath
*path
= gtk_tree_row_reference_get_path(self
->priv
->albumview_ref
);
132 gint
*indices
= gtk_tree_path_get_indices(path
);
133 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Saving myself to position: %i", indices
[0]);
134 cfg_set_single_value_as_int(config
, "albumview","position",indices
[0]);
135 gtk_tree_path_free(path
);
140 void size_changed(GtkWidget
*widget
, GtkAllocation
*alloc
, gpointer user_data
)
142 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(user_data
);
143 int columns
= (alloc
->width
-10)/(self
->priv
->album_size
+25);
144 int rows
= (alloc
->height
-10)/(self
->priv
->album_size
+40);
146 if(columns
!= self
->priv
->supported_columns
|| rows
!= self
->priv
->supported_rows
)
148 self
->priv
->supported_columns
= (columns
)?columns
:1;
149 self
->priv
->supported_rows
= (rows
)?rows
:1;
150 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "update columns: %i %i %i\n", alloc
->width
-20,columns
, self
->priv
->album_size
);
151 if(self
->priv
->filter_entry
&& GTK_WIDGET_IS_SENSITIVE(self
->priv
->filter_entry
))
158 void album_size_changed(GtkSpinButton
*spin
, gpointer user_data
)
160 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(user_data
);
161 int new_size
= ((int)gtk_spin_button_get_value(spin
))*25+50;
162 if(new_size
!= self
->priv
->album_size
) {
163 self
->priv
->album_size
= new_size
;
164 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Set new size: %i\n", new_size
);
165 /* Reset so it gets redrawn */
166 self
->priv
->supported_columns
= -1;
167 /* Force re-display */
168 size_changed(self
->priv
->albumview_main_box
, &(self
->priv
->albumview_main_box
->allocation
), self
);
170 cfg_set_single_value_as_int(config
, "albumview", "zoom-level", (int)gtk_spin_button_get_value(spin
));
173 static gboolean
albumview_scroll_event(GtkWidget
*event_box
, GdkEventScroll
*event
, gpointer data
)
175 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
176 if(self
->priv
->current_item
== NULL
) return FALSE
;
177 if(event
->direction
== GDK_SCROLL_UP
)
179 int value
= gtk_range_get_value(GTK_RANGE(self
->priv
->slider_scale
))-self
->priv
->supported_columns
;
180 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), value
);
182 }else if(event
->direction
== GDK_SCROLL_DOWN
) {
183 int value
= gtk_range_get_value(GTK_RANGE(self
->priv
->slider_scale
))+self
->priv
->supported_columns
;
184 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), value
);
189 static gboolean
albumview_key_press_event(GtkWidget
*event_box
, GdkEventKey
*event
, gpointer data
)
191 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
192 if(self
->priv
->current_item
== NULL
) return FALSE
;
194 if(event
->keyval
== GDK_Up
){
195 int value
= gtk_range_get_value(GTK_RANGE(self
->priv
->slider_scale
))-self
->priv
->supported_columns
;
196 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), value
);
198 }else if (event
->keyval
== GDK_Down
){
199 int value
= gtk_range_get_value(GTK_RANGE(self
->priv
->slider_scale
))+self
->priv
->supported_columns
;
200 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), value
);
202 }else if (event
->keyval
== GDK_Page_Up
) {
203 int value
= gtk_range_get_value(GTK_RANGE(self
->priv
->slider_scale
))-self
->priv
->supported_columns
*self
->priv
->supported_rows
;
204 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), value
);
207 }else if (event
->keyval
== GDK_Page_Down
) {
208 int value
= gtk_range_get_value(GTK_RANGE(self
->priv
->slider_scale
))+self
->priv
->supported_columns
*self
->priv
->supported_rows
;
209 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), value
);
216 static gboolean
albumview_button_press_event(GtkWidget
*event_box
, GdkEventButton
*event
, gpointer data
)
218 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
219 if(self
->priv
->current_item
== NULL
) return FALSE
;
220 gtk_widget_grab_focus(self
->priv
->event_bg
);
222 static gboolean
albumview_focus(GtkWidget
*wid
,GdkEventFocus
*event
, gpointer data
)
224 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
225 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "focus in");
226 gtk_widget_queue_draw(self
->priv
->event_bg
);
229 static gboolean
albumview_focus_out(GtkWidget
*wid
,GdkEventFocus
*event
, gpointer data
)
231 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
232 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "focus out");
234 gtk_widget_queue_draw(self
->priv
->event_bg
);
237 static gboolean
albumview_expose_event(GtkWidget
*widget
, GdkEventExpose
*event
, gpointer data
)
239 int width
= widget
->allocation
.width
;
240 int height
= widget
->allocation
.height
;
241 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
243 gtk_paint_flat_box(widget
->style
,
251 if(gtk_widget_is_focus(widget
))
253 gtk_paint_focus(widget
->style
, widget
->window
,
262 #if GTK_CHECK_VERSION(2,16,0)
263 static void mod_fill_clear_search_entry(GtkEntry
*entry
, GtkEntryIconPosition icon_pos
, GdkEvent
*event
, gpointer user_data
)
265 if(icon_pos
== GTK_ENTRY_ICON_SECONDARY
){
266 gtk_entry_set_text(GTK_ENTRY(entry
), "");
270 static void albumview_init(AlbumViewPlugin
*self
)
272 /** Get an allready exposed widgets to grab theme colors from. */
273 GtkWidget
*colw
= (GtkWidget
*)playlist3_get_category_tree_view();
274 GtkWidget
*label
= NULL
;
275 GtkWidget
*event
= gtk_scrolled_window_new(NULL
, NULL
);
276 GtkWidget
*hbox
= NULL
; int i
= 0;
277 self
->priv
->event_bg
= gtk_event_box_new();
278 self
->priv
->albumview_main_box
= gtk_vbox_new(FALSE
, 6);
280 self
->priv
->album_size
= 25*cfg_get_single_value_as_int_with_default(config
, "albumview", "zoom-level", 5)+50;
281 g_signal_connect(G_OBJECT(event
), "size-allocate", G_CALLBACK(size_changed
), self
);
283 GtkWidget
*iv
= self
->priv
->albumview_box
= gtk_vbox_new(FALSE
, 6);
284 self
->priv
->slider_scale
= gtk_vscale_new_with_range(0,1,1);
285 gtk_scale_set_draw_value(GTK_SCALE(self
->priv
->slider_scale
), FALSE
);
286 g_signal_connect(G_OBJECT(self
->priv
->slider_scale
), "value-changed", G_CALLBACK(position_changed
), self
);
288 self
->priv
->filter_entry
= gtk_entry_new();
290 #if GTK_CHECK_VERSION(2,16,0)
291 gtk_entry_set_icon_from_stock(GTK_ENTRY(self
->priv
->filter_entry
), GTK_ENTRY_ICON_SECONDARY
, GTK_STOCK_CLEAR
);
292 g_signal_connect(GTK_ENTRY(self
->priv
->filter_entry
), "icon-press", G_CALLBACK(mod_fill_clear_search_entry
), NULL
);
294 // gtk_box_pack_start(GTK_BOX(self->priv->albumview_main_box), self->priv->filter_entry, FALSE, FALSE, 0);
295 g_signal_connect(G_OBJECT(self
->priv
->filter_entry
),"changed", G_CALLBACK(filter_list
), self
);
297 GtkWidget
*spin
= gtk_spin_button_new_with_range(1, 10, 1);
298 // gtk_scale_set_draw_value(GTK_SCALE(spin), FALSE);
299 gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin
), self
->priv
->album_size
);
301 hbox
= gtk_hbox_new(FALSE
, 6);
302 gtk_box_pack_start(GTK_BOX(hbox
),gtk_label_new(("Filter")), FALSE
, FALSE
, 0);
303 gtk_box_pack_start(GTK_BOX(hbox
),self
->priv
->filter_entry
, TRUE
, TRUE
, 0);
305 gtk_box_pack_start(GTK_BOX(hbox
),gtk_label_new(("Zoom level")), FALSE
, FALSE
, 0);
306 gtk_widget_set_size_request(spin
, 140, -1);
307 gtk_box_pack_start(GTK_BOX(hbox
),spin
, FALSE
, FALSE
, 0);
308 gtk_box_pack_end(GTK_BOX(self
->priv
->albumview_main_box
), hbox
, FALSE
, FALSE
, 0);
310 #if GTK_CHECK_VERSION(2,16,0)
311 /* for(i=4; i<15;i++){
312 gtk_scale_add_mark(GTK_SCALE(spin), (gdouble)i,GTK_POS_TOP, NULL);
316 gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin
), cfg_get_single_value_as_int_with_default(config
, "albumview", "zoom-level", 10));
317 g_signal_connect(G_OBJECT(spin
), "value-changed", G_CALLBACK(album_size_changed
), self
);
320 hbox
= gtk_hbox_new(FALSE
, 6);
321 gtk_box_pack_start(GTK_BOX(self
->priv
->albumview_main_box
),hbox
, TRUE
, TRUE
, 0);
322 gtk_box_pack_start(GTK_BOX(hbox
),event
, TRUE
, TRUE
, 0);
323 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);
324 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(event
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
325 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(event
), GTK_SHADOW_ETCHED_IN
);
327 /* TODO draw focus */
328 // gtk_widget_modify_bg(self->priv->event_bg, GTK_STATE_NORMAL,&(self->priv->albumview_main_box->style->white));
329 gtk_widget_set_app_paintable(self
->priv
->event_bg
, TRUE
);
330 g_signal_connect(G_OBJECT(self
->priv
->event_bg
), "expose-event", G_CALLBACK(albumview_expose_event
), self
);
331 gtk_event_box_set_visible_window(GTK_EVENT_BOX(self
->priv
->event_bg
), TRUE
);
333 g_object_set(self
->priv
->event_bg
, "can-focus", TRUE
,NULL
);
334 GTK_WIDGET_SET_FLAGS(self
->priv
->event_bg
, GTK_HAS_FOCUS
);
335 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(event
), self
->priv
->event_bg
);
336 gtk_container_add(GTK_CONTAINER(self
->priv
->event_bg
), iv
);
337 gtk_widget_add_events(self
->priv
->event_bg
, GDK_SCROLL_MASK
|GDK_BUTTON_PRESS_MASK
|GDK_FOCUS_CHANGE_MASK
);
338 g_signal_connect_object(G_OBJECT(self
->priv
->event_bg
), "scroll-event", G_CALLBACK(albumview_scroll_event
), self
,0);
339 g_signal_connect_object(G_OBJECT(self
->priv
->event_bg
), "key-press-event", G_CALLBACK(albumview_key_press_event
), self
,0);
340 g_signal_connect_object(G_OBJECT(self
->priv
->event_bg
), "focus-in-event", G_CALLBACK(albumview_focus
), self
,0);
341 g_signal_connect_object(G_OBJECT(self
->priv
->event_bg
), "focus-out-event", G_CALLBACK(albumview_focus_out
), self
, 0);
342 g_signal_connect_object(G_OBJECT(self
->priv
->filter_entry
), "key-press-event", G_CALLBACK(albumview_key_press_event
), self
,0);
343 g_signal_connect_object(G_OBJECT(self
->priv
->event_bg
), "button-press-event", G_CALLBACK(albumview_button_press_event
), self
,0);
345 gtk_widget_show_all(self
->priv
->albumview_main_box
);
346 /* maintain my own reference to the widget, so it won't get destroyed removing
349 g_object_ref_sink(self
->priv
->albumview_main_box
);
353 static void albumview_selected(GmpcPluginBrowserIface
*plug
, GtkWidget
*container
)
355 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(plug
);
356 if(self
->priv
->albumview_main_box
== NULL
) {
357 albumview_init((AlbumViewPlugin
*)plug
);
358 albumview_connection_changed(gmpcconn
, connection
,1,self
);
360 gtk_container_add(GTK_CONTAINER(container
), self
->priv
->albumview_main_box
);
361 gtk_widget_show(self
->priv
->albumview_main_box
);
362 gtk_widget_grab_focus(self
->priv
->event_bg
);
365 static void albumview_unselected(GmpcPluginBrowserIface
*plug
,GtkWidget
*container
)
367 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(plug
);
368 gtk_container_remove(GTK_CONTAINER(container
), self
->priv
->albumview_main_box
);
373 void albumview_plugin_init(AlbumViewPlugin
*self
)
377 gchar *path = gmpc_plugin_get_data_path(&plugin);
378 gchar *url = g_build_path(G_DIR_SEPARATOR_S,path, "albumview", NULL);
379 debug_printf(DEBUG_WARNING,"Found url: %s\n", url);
382 gtk_icon_theme_append_search_path(gtk_icon_theme_get_default (),url);
388 #define TIMER_SUB(start,stop,diff) diff.tv_usec = stop.tv_usec - start.tv_usec;\
389 diff.tv_sec = stop.tv_sec - start.tv_sec;\
390 if(diff.tv_usec < 0) {\
392 diff.tv_usec += G_USEC_PER_SEC; \
394 static gint
__add_sort(gpointer aa
, gpointer bb
, gpointer data
)
396 MpdData_real
*a
= *(MpdData_real
**)aa
;
397 MpdData_real
*b
= *(MpdData_real
**)bb
;
398 if(!a
|| !b
) return 0;
399 if(a
->type
== MPD_DATA_TYPE_SONG
&& b
->type
== MPD_DATA_TYPE_SONG
)
401 if(a
->song
->artist
&& b
->song
->artist
)
406 sa
= g_utf8_strdown(a
->song
->artist
, -1);
407 sb
= g_utf8_strdown(b
->song
->artist
, -1);
408 val
= g_utf8_collate(sa
,sb
);
412 val = (a == NULL)?((b==NULL)?0:-1):1;
416 if (a
->song
->album
&& b
->song
->album
)
419 sa
= g_utf8_strdown(a
->song
->album
, -1);
420 sb
= g_utf8_strdown(b
->song
->album
, -1);
421 val
= g_utf8_collate(sa
,sb
);
432 static gboolean
load_list_itterate(AlbumViewPlugin
*self
)
434 MpdData
*data2
= NULL
;
435 self
->priv
->current_entry
++;
436 if(self
->priv
->max_entries
>0 && self
->priv
->current_entry
%25 == 0){
437 gchar
*temp
= g_strdup_printf("%i of %i albums loaded", self
->priv
->current_entry
, self
->priv
->max_entries
);
438 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(self
->priv
->progress_bar
), self
->priv
->current_entry
/(double)self
->priv
->max_entries
);
439 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(self
->priv
->progress_bar
), temp
);
445 mpd_database_search_field_start(connection
, MPD_TAG_ITEM_ARTIST
);
446 mpd_database_search_add_constraint(connection
, MPD_TAG_ITEM_ALBUM
, (self
->priv
->data
)->tag
);
447 data2
= mpd_database_search_commit(connection
);
450 mpd_Song
*song
= mpd_newSong();
451 song
->album
= g_strdup((self
->priv
->data
)->tag
);
452 song
->artist
= g_strdup(data2
->tag
);
453 if(!mpd_data_is_last(data2
))
455 /* test if server supports album artist */
456 if(mpd_server_tag_supported(connection
, MPD_TAG_ITEM_ALBUM_ARTIST
))
458 mpd_database_search_field_start(connection
, MPD_TAG_ITEM_ALBUM_ARTIST
);
459 mpd_database_search_add_constraint(connection
, MPD_TAG_ITEM_ALBUM
, (self
->priv
->data
)->tag
);
460 MpdData
*data3
= mpd_database_search_commit(connection
);
461 if(mpd_data_is_last(data3
)){
462 if(strlen(data3
->tag
) > 0)
464 song
->albumartist
= g_strdup(data3
->tag
);
465 if(song
->artist
) g_free(song
->artist
);
466 song
->artist
= g_strdup(data3
->tag
);
473 mpd_data_free(data3
);
480 mpd_data_free(data2
);
482 self
->priv
->complete_list
= mpd_new_data_struct_append(self
->priv
->complete_list
);
483 self
->priv
->complete_list
->song
= song
;
484 self
->priv
->complete_list
->type
= MPD_DATA_TYPE_SONG
;
489 (self
->priv
->data
) = mpd_data_get_next((self
->priv
->data
));
491 if(self
->priv
->data
== NULL
){
494 self
->priv
->complete_list
= (MpdData
*)misc_sort_mpddata(mpd_data_get_first(self
->priv
->complete_list
), (GCompareDataFunc
)__add_sort
, NULL
);
495 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
,"update view\n");
496 gtk_widget_destroy(self
->priv
->progress_bar
);
497 self
->priv
->progress_bar
= NULL
;
498 for(iter
= (MpdData_real
*)self
->priv
->complete_list
; iter
; iter
= iter
->next
) items
++;
500 gtk_widget_set_sensitive(self
->priv
->filter_entry
, TRUE
);
501 filter_list(GTK_ENTRY(self
->priv
->filter_entry
), self
);
503 gtk_widget_grab_focus(self
->priv
->event_bg
);
507 g_idle_add((GSourceFunc
)load_list_itterate
, self
);
511 static void load_list(AlbumViewPlugin
*self
)
513 if(self
->priv
->complete_list
)mpd_data_free(self
->priv
->complete_list
);
514 self
->priv
->complete_list
= NULL
;
515 if(self
->priv
->current_item
) g_list_free(self
->priv
->current_item
);
516 self
->priv
->current_item
= NULL
;
519 self
->priv
->progress_bar
= gtk_progress_bar_new();
520 gtk_box_pack_start(GTK_BOX(self
->priv
->albumview_box
), self
->priv
->progress_bar
, FALSE
, FALSE
, 0);
521 gtk_widget_show(self
->priv
->progress_bar
);
522 mpd_database_search_field_start(connection
, MPD_TAG_ITEM_ALBUM
);
523 MpdData
*iter
,*data
= mpd_database_search_commit(connection
);
524 self
->priv
->max_entries
= 0;
525 self
->priv
->current_entry
= 0;
526 gtk_widget_set_sensitive(self
->priv
->filter_entry
, FALSE
);
527 for(iter
= data
; iter
; iter
= mpd_data_get_next_real(iter
, FALSE
)) self
->priv
->max_entries
++;
528 self
->priv
->data
= data
;
529 g_idle_add((GSourceFunc
)load_list_itterate
, self
);
531 void albumview_connection_changed(GmpcConnection
*conn
, MpdObj
*mi
, int connect
,void *usedata
)
533 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(usedata
);
535 if(connect
&& self
->priv
->albumview_main_box
)
539 else if(self
->priv
->albumview_main_box
){
540 mpd_data_free(self
->priv
->complete_list
);
541 self
->priv
->complete_list
= NULL
;
542 if(self
->priv
->item_table
)
543 gtk_widget_hide(self
->priv
->item_table
);
546 static void album_add(GtkWidget
*button
, mpd_Song
*song
)
548 mpd_database_search_start(connection
,TRUE
);
550 mpd_database_search_add_constraint(connection
, MPD_TAG_ITEM_ALBUM
, song
->album
);
551 if(song
->albumartist
&& strlen(song
->albumartist
) >0){
552 mpd_database_search_add_constraint(connection
, MPD_TAG_ITEM_ALBUM_ARTIST
, song
->albumartist
);
554 mpd_database_search_add_constraint(connection
, MPD_TAG_ITEM_ARTIST
, song
->artist
);
556 MpdData
*data
= mpd_database_search_commit(connection
);
557 /* Sort it before adding */
558 data
= misc_sort_mpddata_by_album_disc_track(data
);
559 for(;data
;data
= mpd_data_get_next(data
)){
560 mpd_playlist_queue_add(connection
, data
->song
->file
);
562 mpd_playlist_queue_commit(connection
);
564 static void album_view(GtkWidget
*button
,mpd_Song
*song
)
566 if (song
&& song
->artist
&& song
->album
) {
568 info2_fill_album_view(song
->artist
, song
->album
);
572 static void album_replace(GtkWidget
*button
, mpd_Song
*song
)
574 mpd_playlist_clear(connection
);
575 album_add(button
, song
);
576 mpd_player_play(connection
);
578 static gboolean
album_button_press(GtkWidget
*image
, GtkMenu
*menu
, mpd_Song
*song
)
582 item
= gtk_image_menu_item_new_with_label("Album information");
583 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
),
584 gtk_image_new_from_stock(GTK_STOCK_INFO
, GTK_ICON_SIZE_MENU
));
585 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
586 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(album_view
), song
);
588 item
= gtk_image_menu_item_new_from_stock(GTK_STOCK_ADD
,NULL
);
589 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
590 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(album_add
), song
);
592 /* replace the replace widget */
593 item
= gtk_image_menu_item_new_with_label("Replace");
594 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
),
595 gtk_image_new_from_stock(GTK_STOCK_REDO
, GTK_ICON_SIZE_MENU
));
596 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
597 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(album_replace
), song
);
599 item
= gtk_separator_menu_item_new();
600 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
602 gtk_widget_show(item
);
606 static GtkWidget
* create_button(AlbumViewPlugin
*self
, MpdData_real
*complete_list_iter
)
610 GtkWidget
*label
= NULL
;
612 /* Wrap it in a vbox */
613 vbox
= gtk_vbox_new(FALSE
, 3);
614 gtk_widget_set_size_request(vbox
, self
->priv
->album_size
+20,self
->priv
->album_size
+40);
616 item
= gmpc_metaimage_new_size(META_ALBUM_ART
,self
->priv
->album_size
);
617 gmpc_metaimage_set_scale_up(GMPC_METAIMAGE(item
), TRUE
);
618 gtk_widget_set_has_tooltip(GTK_WIDGET(item
), FALSE
);
619 gmpc_metaimage_set_squared(GMPC_METAIMAGE(item
), TRUE
);
621 gmpc_metaimage_update_cover_from_song_delayed(GMPC_METAIMAGE(item
), complete_list_iter
->song
);
623 gtk_box_pack_start(GTK_BOX(vbox
), item
, TRUE
, TRUE
, 0);
624 /* Set artist name */
625 if(complete_list_iter
->song
->albumartist
){
626 GtkWidget
*label
= gtk_label_new(complete_list_iter
->song
->albumartist
);
627 gtk_label_set_ellipsize(GTK_LABEL(label
), PANGO_ELLIPSIZE_MIDDLE
);
628 gtk_box_pack_end(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
630 GtkWidget
*label
= gtk_label_new(complete_list_iter
->song
->artist
);
631 gtk_label_set_ellipsize(GTK_LABEL(label
), PANGO_ELLIPSIZE_MIDDLE
);
632 gtk_box_pack_end(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
636 label
= gtk_label_new("");
637 temp
= g_markup_printf_escaped("<b>%s</b>", complete_list_iter
->song
->album
);
638 gtk_label_set_markup(GTK_LABEL(label
), temp
);
640 gtk_label_set_ellipsize(GTK_LABEL(label
), PANGO_ELLIPSIZE_MIDDLE
);
641 gtk_box_pack_end(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
644 /* Attach it to the song */
645 complete_list_iter
->userdata
= g_object_ref_sink(vbox
);
646 complete_list_iter
->freefunc
= (void *)gtk_widget_destroy
;
647 g_object_set_data(G_OBJECT(vbox
), "item", item
);
648 g_signal_connect(G_OBJECT(item
), "menu_populate_client", G_CALLBACK(album_button_press
), complete_list_iter
->song
);
650 g_signal_connect(item, "button-press-event",
651 G_CALLBACK(album_button_press), complete_list_iter->song);
656 static void filter_list(GtkEntry
*entry
, gpointer data
)
658 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
659 GRegex
*regex
= NULL
;
662 const gchar
*search_query
= gtk_entry_get_text(GTK_ENTRY(self
->priv
->filter_entry
));
663 MpdData_real
*complete_list_iter
= NULL
;
664 if(search_query
[0] != '\0')
666 gchar
*str
= g_strdup(search_query
);
667 gchar
**test
= g_strsplit(g_strstrip(str
), " ", -1);
669 GString
*s
= g_string_new("((?:");
670 GError
*error
= NULL
;
672 for(i
=0;test
&& test
[i
];i
++){
673 gchar
*temp
= g_regex_escape_string(test
[i
], -1);
674 s
= g_string_append(s
, ".*");
675 s
= g_string_append(s
, temp
);
676 s
= g_string_append(s
, ".*");
677 if(test
[i
+1] != NULL
)
678 s
= g_string_append(s
, "|");
681 g_string_append_printf(s
,"){%i})",i
);
682 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
,"regex: %s\n", s
->str
);
683 regex
= g_regex_new(s
->str
, G_REGEX_CASELESS
|G_REGEX_EXTENDED
, 0,&error
);;
686 g_string_free(s
, TRUE
);
687 for(complete_list_iter
= (MpdData_real
*) mpd_data_get_first(self
->priv
->complete_list
);
689 complete_list_iter
= (MpdData_real
*)mpd_data_get_next_real((MpdData
*)complete_list_iter
, FALSE
))
691 if(g_regex_match(regex
,complete_list_iter
->song
->album
,0,NULL
)||
692 g_regex_match(regex
,complete_list_iter
->song
->artist
,0,NULL
)||
693 (complete_list_iter
->song
->albumartist
&& g_regex_match(regex
,complete_list_iter
->song
->albumartist
,0,NULL
))){
695 list
= g_list_append(list
, complete_list_iter
);
700 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_WARNING
," error creating regex: %s\n", error
->message
);
703 g_regex_unref(regex
);
705 if(self
->priv
->current_item
) g_list_free(self
->priv
->current_item
);
706 self
->priv
->current_item
= g_list_first(list
);
707 if((items
-self
->priv
->supported_rows
*self
->priv
->supported_columns
) > 0)
709 gtk_widget_set_sensitive(GTK_WIDGET(self
->priv
->slider_scale
), TRUE
);
710 gtk_range_set_range(GTK_RANGE(self
->priv
->slider_scale
), 0,
711 ((items
-self
->priv
->supported_rows
*self
->priv
->supported_columns
)>0)?(items
-self
->priv
->supported_rows
*self
->priv
->supported_columns
):1);
714 gtk_widget_set_sensitive(GTK_WIDGET(self
->priv
->slider_scale
), FALSE
);
715 gtk_range_set_range(GTK_RANGE(self
->priv
->slider_scale
), 0,1);
717 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), 0);
721 static void position_changed(GtkRange
*range
, gpointer data
)
723 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
724 gint i
=0,value
= (int)gtk_range_get_value(range
);
725 self
->priv
->current_item
= g_list_first(self
->priv
->current_item
);
726 for(i
=0;i
<value
&& self
->priv
->current_item
&& self
->priv
->current_item
->next
; self
->priv
->current_item
= self
->priv
->current_item
->next
){i
++;}
729 static gboolean
update_view_real(AlbumViewPlugin
*self
)
731 MpdData
*complete_list_iter
;
732 const char *search_query
= gtk_entry_get_text(GTK_ENTRY(self
->priv
->filter_entry
));
737 MpdData
*data
= NULL
;
738 GList
*entries
= NULL
;
739 GList
*list
= (self
->priv
->item_table
)?gtk_container_get_children(GTK_CONTAINER(self
->priv
->item_table
)):NULL
;
741 GRegex
*regex
= NULL
;
743 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
,"search query: %s\n", search_query
);
745 if(self
->priv
->item_table
)
746 gtk_widget_hide(self
->priv
->item_table
);
747 for(iter
= g_list_first(list
); iter
; iter
= iter
->next
){
748 GtkWidget
*widget
= iter
->data
;
749 gtk_container_remove(GTK_CONTAINER(self
->priv
->item_table
), widget
);
756 gtk_widget_show(self
->priv
->albumview_box
);
757 if(self
->priv
->current_item
== NULL
){
759 for(complete_list_iter
= mpd_data_get_first(self
->priv
->complete_list
);
761 complete_list_iter
= mpd_data_get_next_real(complete_list_iter
, FALSE
))
764 self
->priv
->current_item
= g_list_append(self
->priv
->current_item
, complete_list_iter
);
766 self
->priv
->current_item
= g_list_first(self
->priv
->current_item
);
767 if((items
-self
->priv
->supported_rows
*self
->priv
->supported_columns
) > 0)
769 gtk_widget_set_sensitive(GTK_WIDGET(self
->priv
->slider_scale
), TRUE
);
770 gtk_range_set_range(GTK_RANGE(self
->priv
->slider_scale
), 0,
771 ((items
-self
->priv
->supported_rows
*self
->priv
->supported_columns
)>0)?(items
-self
->priv
->supported_rows
*self
->priv
->supported_columns
):1);
774 gtk_widget_set_sensitive(GTK_WIDGET(self
->priv
->slider_scale
), FALSE
);
775 gtk_range_set_range(GTK_RANGE(self
->priv
->slider_scale
), 0,1);
777 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), 0);
780 int rows
= self
->priv
->supported_rows
;
782 * Create holding table if it does not exist
784 if(!self
->priv
->item_table
){
785 GtkWidget
*ali
= gtk_alignment_new(0.0, 0.5, 0,0);
786 self
->priv
->item_table
= exo_wrap_table_new(TRUE
);//gtk_table_new(rows, supported_columns, TRUE);
787 gtk_container_add(GTK_CONTAINER(ali
), self
->priv
->item_table
);
788 gtk_box_pack_start(GTK_BOX(self
->priv
->albumview_box
), ali
, FALSE
, FALSE
, 0);
791 /* I know how large it is going to be.. so lets set the size */
792 gtk_widget_set_size_request(self
->priv
->item_table
, self
->priv
->supported_columns
*(self
->priv
->album_size
+20)+6, (rows
)*(self
->priv
->album_size
+40));
797 if(self
->priv
->current_item
)//(iter = g_list_first(list)))
799 GList
*iter
= self
->priv
->current_item
;
803 complete_list_iter
= iter
->data
;
804 if(complete_list_iter
->song
/* && (complete_list_iter->song->artist)[0] != '\0'*/)
806 GtkWidget
*vbox
= complete_list_iter
->userdata
;
810 vbox
= create_button(self
, (MpdData_real
*)complete_list_iter
);
813 item
= g_object_get_data(G_OBJECT(vbox
), "item");
814 /* Resize if needed */
815 if(self
->priv
->album_size
!= gmpc_metaimage_get_size(GMPC_METAIMAGE(item
))){
816 gtk_widget_set_size_request(vbox
, self
->priv
->album_size
+20,self
->priv
->album_size
+40);
817 gmpc_metaimage_set_size(GMPC_METAIMAGE(item
), self
->priv
->album_size
);
818 gmpc_metaimage_reload_image(GMPC_METAIMAGE(item
));
822 entries
= g_list_prepend(entries
, vbox
);
826 }while(v_items
< (rows
*self
->priv
->supported_columns
)&& (iter
= iter
->next
));
829 if(list
) g_list_free(list
);
832 for(iter
= entries
= g_list_reverse(entries
); iter
; iter
= g_list_next(iter
)){
833 gtk_container_add(GTK_CONTAINER(self
->priv
->item_table
), iter
->data
);
835 if(entries
) g_list_free(entries
);
837 gtk_widget_show_all(self
->priv
->albumview_box
);
841 if(self
->priv
->update_timeout
)
842 g_source_remove(self
->priv
->update_timeout
);
843 self
->priv
->update_timeout
= 0;
848 void update_view(AlbumViewPlugin
*self
)
850 if(self
->priv
->update_timeout
!= 0) {
851 g_source_remove(self
->priv
->update_timeout
);
853 self
->priv
->update_timeout
= g_timeout_add(10, (GSourceFunc
)update_view_real
,self
);
859 static void albumview_plugin_class_init (AlbumViewPluginClass
*klass
);
861 static int *albumview_plugin_get_version(GmpcPluginBase
*plug
, int *length
)
863 static int version
[3] = {PLUGIN_MAJOR_VERSION
,PLUGIN_MINOR_VERSION
,PLUGIN_MICRO_VERSION
};
864 if(length
) *length
= 3;
865 return (int *)version
;
868 static const char *albumview_plugin_get_name(GmpcPluginBase
*plug
)
870 return ("Album View");
872 static GObject
*albumview_plugin_constructor(GType type
, guint n_construct_properties
, GObjectConstructParam
* construct_properties
) {
873 AlbumViewPluginClass
* klass
;
874 AlbumViewPlugin
*self
;
875 GObjectClass
* parent_class
;
876 klass
= (g_type_class_peek (albumview_plugin_get_type()));
877 parent_class
= G_OBJECT_CLASS (g_type_class_peek_parent (klass
));
878 self
= (AlbumViewPlugin
*) parent_class
->constructor (type
, n_construct_properties
, construct_properties
);
880 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Constructing plugin");
882 /* setup private structure */
883 self
->priv
= g_malloc0(sizeof(AlbumViewPluginPrivate
));
885 self
->priv
->supported_rows
= 1;
886 self
->priv
->supported_columns
= 1;
887 self
->priv
->data
= NULL
;
888 self
->priv
->album_size
= 200;
889 self
->priv
->filter_entry
= NULL
;
890 self
->priv
->slider_scale
= NULL
;
891 self
->priv
->progress_bar
= NULL
;
892 self
->priv
->max_entries
= 0;
893 self
->priv
->current_entry
= 0;
894 self
->priv
->current_item
= NULL
;
895 self
->priv
->update_timeout
= 0;
896 self
->priv
->complete_list
= NULL
;
897 self
->priv
->item_table
= NULL
;
898 self
->priv
->albumview_ref
= NULL
;
899 self
->priv
->albumview_box
= NULL
;
901 /* Watch status changed signals */
902 g_signal_connect_object(G_OBJECT(gmpcconn
), "connection-changed", G_CALLBACK(albumview_connection_changed
), self
, 0);
904 /* Setup textdomain */
905 bindtextdomain(GETTEXT_PACKAGE
, PACKAGE_LOCALE_DIR
);
906 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8");
908 GMPC_PLUGIN_BASE(self
)->translation_domain
= GETTEXT_PACKAGE
;
909 GMPC_PLUGIN_BASE(self
)->plugin_type
= GMPC_PLUGIN_NO_GUI
;
911 albumview_plugin_init(self
);
913 return G_OBJECT(self
);
915 static void albumview_plugin_finalize(GObject
*obj
) {
916 AlbumViewPlugin
*self
= (AlbumViewPlugin
*)obj
;
917 AlbumViewPluginClass
* klass
= (g_type_class_peek (play_queue_plugin_get_type()));
918 gpointer parent_class
= g_type_class_peek_parent (klass
);
920 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Destroying plugin");
923 if(self
->priv
->current_item
) g_list_free(self
->priv
->current_item
);
924 self
->priv
->current_item
= NULL
;
925 if(self
->priv
->complete_list
) mpd_data_free(self
->priv
->complete_list
);
926 self
->priv
->complete_list
= NULL
;
932 G_OBJECT_CLASS(parent_class
)->finalize(obj
);
936 static void albumview_plugin_class_init (AlbumViewPluginClass
*klass
)
938 G_OBJECT_CLASS(klass
)->finalize
= albumview_plugin_finalize
;
939 G_OBJECT_CLASS(klass
)->constructor
= albumview_plugin_constructor
;
940 /* Connect plugin functions */
941 GMPC_PLUGIN_BASE_CLASS(klass
)->get_version
= albumview_plugin_get_version
;
942 GMPC_PLUGIN_BASE_CLASS(klass
)->get_name
= albumview_plugin_get_name
;
944 GMPC_PLUGIN_BASE_CLASS(klass
)->get_enabled
= albumview_get_enabled
;
945 GMPC_PLUGIN_BASE_CLASS(klass
)->set_enabled
= albumview_set_enabled
;
947 GMPC_PLUGIN_BASE_CLASS(klass
)->save_yourself
= albumview_browser_save_myself
;
950 static void albumview_plugin_browser_iface_init(GmpcPluginBrowserIfaceIface
* iface
) {
951 iface
->browser_add
= albumview_add
;
952 iface
->browser_selected
= albumview_selected
;
953 iface
->browser_unselected
= albumview_unselected
;
956 const GType
albumview_plugin_get_type(void) {
957 static GType albumview_plugin_type_id
= 0;
958 if(albumview_plugin_type_id
== 0) {
959 static const GTypeInfo info
= {
960 .class_size
= sizeof(AlbumViewPluginClass
),
961 .class_init
= (GClassInitFunc
)albumview_plugin_class_init
,
962 .instance_size
= sizeof(AlbumViewPlugin
),
966 albumview_plugin_type_id
= g_type_register_static(GMPC_PLUGIN_TYPE_BASE
, "AlbumViewPlugin", &info
, 0);
968 /** Browser interface */
969 static const GInterfaceInfo iface_info
= { (GInterfaceInitFunc
) albumview_plugin_browser_iface_init
,
970 (GInterfaceFinalizeFunc
) NULL
, NULL
};
971 g_type_add_interface_static (albumview_plugin_type_id
, GMPC_PLUGIN_TYPE_BROWSER_IFACE
, &iface_info
);
973 return albumview_plugin_type_id
;
976 G_MODULE_EXPORT GType
plugin_get_type(void)
978 return albumview_plugin_get_type();