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
);
48 static void load_list(AlbumViewPlugin
*self
);
53 static int albumview_get_enabled(GmpcPluginBase
*plug
)
55 return cfg_get_single_value_as_int_with_default(config
, "albumview", "enable", TRUE
);
58 void albumview_set_enabled(GmpcPluginBase
*plug
, int enabled
)
60 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(plug
);
61 cfg_set_single_value_as_int(config
, "albumview", "enable", enabled
);
64 if(self
->priv
->albumview_ref
== NULL
)
66 albumview_add(GMPC_PLUGIN_BROWSER_IFACE(plug
), GTK_WIDGET(playlist3_get_category_tree_view()));
71 GtkTreePath
*path
= gtk_tree_row_reference_get_path(self
->priv
->albumview_ref
);
72 GtkTreeModel
*model
= gtk_tree_row_reference_get_model(self
->priv
->albumview_ref
);
75 if (gtk_tree_model_get_iter(model
, &iter
, path
)){
76 gtk_list_store_remove(GTK_LIST_STORE(model
), &iter
);
78 gtk_tree_path_free(path
);
79 gtk_tree_row_reference_free(self
->priv
->albumview_ref
);
80 self
->priv
->albumview_ref
= NULL
;
86 * Playlist browser functions
88 static void albumview_add(GmpcPluginBrowserIface
*plug
, GtkWidget
*category_tree
)
90 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(plug
);
92 GtkTreeModel
*model
= GTK_TREE_MODEL(playlist3_get_category_tree_store());
96 * don't do anything if we are disabled
98 if(!cfg_get_single_value_as_int_with_default(config
, "albumview", "enable", TRUE
)) return;
100 * Add ourslef to the list
102 pos
= cfg_get_single_value_as_int_with_default(config
, "albumview","position",2);
103 playlist3_insert_browser(&iter
, pos
);
104 gtk_list_store_set(GTK_LIST_STORE(model
), &iter
,
105 PL3_CAT_TYPE
, GMPC_PLUGIN_BASE(plug
)->id
,
106 PL3_CAT_TITLE
,"Album View",
107 PL3_CAT_ICON_ID
, "albumview",
110 * remove odl reference if exists
112 if (self
->priv
->albumview_ref
) {
113 gtk_tree_row_reference_free(self
->priv
->albumview_ref
);
114 self
->priv
->albumview_ref
= NULL
;
117 * create reference to ourself in the list
119 path
= gtk_tree_model_get_path(GTK_TREE_MODEL(model
), &iter
);
121 self
->priv
->albumview_ref
= gtk_tree_row_reference_new(model
, path
);
122 gtk_tree_path_free(path
);
125 static void albumview_browser_save_myself(GmpcPluginBase
*plug
)
127 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(plug
);
128 if(self
->priv
->albumview_ref
)
130 GtkTreePath
*path
= gtk_tree_row_reference_get_path(self
->priv
->albumview_ref
);
133 gint
*indices
= gtk_tree_path_get_indices(path
);
134 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Saving myself to position: %i", indices
[0]);
135 cfg_set_single_value_as_int(config
, "albumview","position",indices
[0]);
136 gtk_tree_path_free(path
);
141 void size_changed(GtkWidget
*widget
, GtkAllocation
*alloc
, gpointer user_data
)
143 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(user_data
);
144 int columns
= (alloc
->width
-10)/(self
->priv
->album_size
+25);
145 int rows
= (alloc
->height
-10)/(self
->priv
->album_size
+40);
147 if(columns
!= self
->priv
->supported_columns
|| rows
!= self
->priv
->supported_rows
)
149 self
->priv
->supported_columns
= (columns
)?columns
:1;
150 self
->priv
->supported_rows
= (rows
)?rows
:1;
151 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "update columns: %i %i %i\n", alloc
->width
-20,columns
, self
->priv
->album_size
);
152 if(self
->priv
->filter_entry
&& GTK_WIDGET_IS_SENSITIVE(self
->priv
->filter_entry
))
159 void album_size_changed(GtkSpinButton
*spin
, gpointer user_data
)
161 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(user_data
);
162 int new_size
= ((int)gtk_spin_button_get_value(spin
))*25+50;
163 if(new_size
!= self
->priv
->album_size
) {
164 self
->priv
->album_size
= new_size
;
165 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Set new size: %i\n", new_size
);
166 /* Reset so it gets redrawn */
167 self
->priv
->supported_columns
= -1;
168 /* Force re-display */
169 size_changed(self
->priv
->albumview_main_box
, &(self
->priv
->albumview_main_box
->allocation
), self
);
171 cfg_set_single_value_as_int(config
, "albumview", "zoom-level", (int)gtk_spin_button_get_value(spin
));
174 static gboolean
albumview_scroll_event(GtkWidget
*event_box
, GdkEventScroll
*event
, gpointer data
)
176 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
177 if(self
->priv
->current_item
== NULL
) return FALSE
;
178 if(event
->direction
== GDK_SCROLL_UP
)
180 int value
= gtk_range_get_value(GTK_RANGE(self
->priv
->slider_scale
))-self
->priv
->supported_columns
;
181 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), value
);
183 }else if(event
->direction
== GDK_SCROLL_DOWN
) {
184 int value
= gtk_range_get_value(GTK_RANGE(self
->priv
->slider_scale
))+self
->priv
->supported_columns
;
185 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), value
);
190 static gboolean
albumview_key_press_event(GtkWidget
*event_box
, GdkEventKey
*event
, gpointer data
)
192 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
193 if(self
->priv
->current_item
== NULL
) return FALSE
;
195 if(event
->keyval
== GDK_Up
){
196 int value
= gtk_range_get_value(GTK_RANGE(self
->priv
->slider_scale
))-self
->priv
->supported_columns
;
197 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), value
);
199 }else if (event
->keyval
== GDK_Down
){
200 int value
= gtk_range_get_value(GTK_RANGE(self
->priv
->slider_scale
))+self
->priv
->supported_columns
;
201 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), value
);
203 }else if (event
->keyval
== GDK_Page_Up
) {
204 int value
= gtk_range_get_value(GTK_RANGE(self
->priv
->slider_scale
))-self
->priv
->supported_columns
*self
->priv
->supported_rows
;
205 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), value
);
208 }else if (event
->keyval
== GDK_Page_Down
) {
209 int value
= gtk_range_get_value(GTK_RANGE(self
->priv
->slider_scale
))+self
->priv
->supported_columns
*self
->priv
->supported_rows
;
210 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), value
);
217 static gboolean
albumview_button_press_event(GtkWidget
*event_box
, GdkEventButton
*event
, gpointer data
)
219 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
220 if(self
->priv
->current_item
== NULL
) return FALSE
;
221 gtk_widget_grab_focus(self
->priv
->event_bg
);
223 static gboolean
albumview_focus(GtkWidget
*wid
,GdkEventFocus
*event
, gpointer data
)
225 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
226 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "focus in");
227 gtk_widget_queue_draw(self
->priv
->event_bg
);
230 static gboolean
albumview_focus_out(GtkWidget
*wid
,GdkEventFocus
*event
, gpointer data
)
232 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
233 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "focus out");
235 gtk_widget_queue_draw(self
->priv
->event_bg
);
238 static gboolean
albumview_expose_event(GtkWidget
*widget
, GdkEventExpose
*event
, gpointer data
)
240 int width
= widget
->allocation
.width
;
241 int height
= widget
->allocation
.height
;
242 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
244 gtk_paint_flat_box(widget
->style
,
252 if(gtk_widget_is_focus(widget
))
254 gtk_paint_focus(widget
->style
, widget
->window
,
263 #if GTK_CHECK_VERSION(2,16,0)
264 static void mod_fill_clear_search_entry(GtkEntry
*entry
, GtkEntryIconPosition icon_pos
, GdkEvent
*event
, gpointer user_data
)
266 if(icon_pos
== GTK_ENTRY_ICON_SECONDARY
){
267 gtk_entry_set_text(GTK_ENTRY(entry
), "");
271 static void albumview_init(AlbumViewPlugin
*self
)
273 /** Get an allready exposed widgets to grab theme colors from. */
274 GtkWidget
*colw
= (GtkWidget
*)playlist3_get_category_tree_view();
275 GtkWidget
*label
= NULL
;
276 GtkWidget
*event
= gtk_scrolled_window_new(NULL
, NULL
);
277 GtkWidget
*hbox
= NULL
; int i
= 0;
278 self
->priv
->event_bg
= gtk_event_box_new();
279 self
->priv
->albumview_main_box
= gtk_vbox_new(FALSE
, 6);
281 self
->priv
->album_size
= 25*cfg_get_single_value_as_int_with_default(config
, "albumview", "zoom-level", 5)+50;
282 g_signal_connect(G_OBJECT(event
), "size-allocate", G_CALLBACK(size_changed
), self
);
284 GtkWidget
*iv
= self
->priv
->albumview_box
= gtk_vbox_new(FALSE
, 6);
285 self
->priv
->slider_scale
= gtk_vscale_new_with_range(0,1,1);
286 gtk_scale_set_draw_value(GTK_SCALE(self
->priv
->slider_scale
), FALSE
);
287 g_signal_connect(G_OBJECT(self
->priv
->slider_scale
), "value-changed", G_CALLBACK(position_changed
), self
);
289 self
->priv
->filter_entry
= gtk_entry_new();
291 #if GTK_CHECK_VERSION(2,16,0)
292 gtk_entry_set_icon_from_stock(GTK_ENTRY(self
->priv
->filter_entry
), GTK_ENTRY_ICON_SECONDARY
, GTK_STOCK_CLEAR
);
293 g_signal_connect(GTK_ENTRY(self
->priv
->filter_entry
), "icon-press", G_CALLBACK(mod_fill_clear_search_entry
), NULL
);
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_spin_button_set_value(GTK_SPIN_BUTTON(spin
), self
->priv
->album_size
);
300 hbox
= gtk_hbox_new(FALSE
, 6);
301 gtk_box_pack_start(GTK_BOX(hbox
),gtk_label_new(("Filter")), FALSE
, FALSE
, 0);
302 gtk_box_pack_start(GTK_BOX(hbox
),self
->priv
->filter_entry
, TRUE
, TRUE
, 0);
304 gtk_box_pack_start(GTK_BOX(hbox
),gtk_label_new(("Zoom level")), FALSE
, FALSE
, 0);
305 gtk_widget_set_size_request(spin
, 140, -1);
306 gtk_box_pack_start(GTK_BOX(hbox
),spin
, FALSE
, FALSE
, 0);
307 gtk_box_pack_end(GTK_BOX(self
->priv
->albumview_main_box
), hbox
, FALSE
, FALSE
, 0);
309 gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin
), cfg_get_single_value_as_int_with_default(config
, "albumview", "zoom-level", 10));
310 g_signal_connect(G_OBJECT(spin
), "value-changed", G_CALLBACK(album_size_changed
), self
);
313 hbox
= gtk_hbox_new(FALSE
, 6);
314 gtk_box_pack_start(GTK_BOX(self
->priv
->albumview_main_box
),hbox
, TRUE
, TRUE
, 0);
315 gtk_box_pack_start(GTK_BOX(hbox
),event
, TRUE
, TRUE
, 0);
316 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);
317 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(event
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
318 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(event
), GTK_SHADOW_ETCHED_IN
);
320 /* TODO draw focus */
321 // gtk_widget_modify_bg(self->priv->event_bg, GTK_STATE_NORMAL,&(self->priv->albumview_main_box->style->white));
322 gtk_widget_set_app_paintable(self
->priv
->event_bg
, TRUE
);
323 g_signal_connect(G_OBJECT(self
->priv
->event_bg
), "expose-event", G_CALLBACK(albumview_expose_event
), self
);
324 gtk_event_box_set_visible_window(GTK_EVENT_BOX(self
->priv
->event_bg
), TRUE
);
326 g_object_set(self
->priv
->event_bg
, "can-focus", TRUE
,NULL
);
327 GTK_WIDGET_SET_FLAGS(self
->priv
->event_bg
, GTK_HAS_FOCUS
);
328 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(event
), self
->priv
->event_bg
);
329 gtk_container_add(GTK_CONTAINER(self
->priv
->event_bg
), iv
);
330 gtk_widget_add_events(self
->priv
->event_bg
, GDK_SCROLL_MASK
|GDK_BUTTON_PRESS_MASK
|GDK_FOCUS_CHANGE_MASK
);
331 g_signal_connect_object(G_OBJECT(self
->priv
->event_bg
), "scroll-event", G_CALLBACK(albumview_scroll_event
), self
,0);
332 g_signal_connect_object(G_OBJECT(self
->priv
->event_bg
), "key-press-event", G_CALLBACK(albumview_key_press_event
), self
,0);
333 g_signal_connect_object(G_OBJECT(self
->priv
->event_bg
), "focus-in-event", G_CALLBACK(albumview_focus
), self
,0);
334 g_signal_connect_object(G_OBJECT(self
->priv
->event_bg
), "focus-out-event", G_CALLBACK(albumview_focus_out
), self
, 0);
335 g_signal_connect_object(G_OBJECT(self
->priv
->filter_entry
), "key-press-event", G_CALLBACK(albumview_key_press_event
), self
,0);
336 g_signal_connect_object(G_OBJECT(self
->priv
->event_bg
), "button-press-event", G_CALLBACK(albumview_button_press_event
), self
,0);
338 gtk_widget_show_all(self
->priv
->albumview_main_box
);
341 /* maintain my own reference to the widget, so it won't get destroyed removing
344 g_object_ref_sink(self
->priv
->albumview_main_box
);
348 static void albumview_selected(GmpcPluginBrowserIface
*plug
, GtkWidget
*container
)
350 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(plug
);
351 if(self
->priv
->albumview_main_box
== NULL
) {
352 albumview_init((AlbumViewPlugin
*)plug
);
353 albumview_connection_changed(gmpcconn
, connection
,1,self
);
355 gtk_container_add(GTK_CONTAINER(container
), self
->priv
->albumview_main_box
);
356 gtk_widget_show(self
->priv
->albumview_main_box
);
357 gtk_widget_show(container
);
358 gtk_widget_grab_focus(self
->priv
->event_bg
);
361 static void albumview_unselected(GmpcPluginBrowserIface
*plug
,GtkWidget
*container
)
363 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(plug
);
364 gtk_container_remove(GTK_CONTAINER(container
), self
->priv
->albumview_main_box
);
367 static void status_changed(GmpcConnection
*gmpcconnn
,MpdObj
*mi
, ChangedStatusType type
, AlbumViewPlugin
*self
)
369 if((type
&MPD_CST_DATABASE
) > 0)
372 if(self
->priv
->albumview_main_box
)
377 void albumview_plugin_init(AlbumViewPlugin
*self
)
379 gtk_icon_theme_append_search_path(gtk_icon_theme_get_default (),PIXMAP_DATA_DIR
);
380 g_signal_connect_object(G_OBJECT(gmpcconn
), "status-changed", G_CALLBACK(status_changed
), self
, 0);
382 #define TIMER_SUB(start,stop,diff) diff.tv_usec = stop.tv_usec - start.tv_usec;\
383 diff.tv_sec = stop.tv_sec - start.tv_sec;\
384 if(diff.tv_usec < 0) {\
386 diff.tv_usec += G_USEC_PER_SEC; \
388 static gint
__add_sort(gpointer aa
, gpointer bb
, gpointer data
)
390 MpdData_real
*a
= *(MpdData_real
**)aa
;
391 MpdData_real
*b
= *(MpdData_real
**)bb
;
392 if(!a
|| !b
) return 0;
393 if(a
->type
== MPD_DATA_TYPE_SONG
&& b
->type
== MPD_DATA_TYPE_SONG
)
395 if(a
->song
->artist
&& b
->song
->artist
)
400 sa
= g_utf8_strdown(a
->song
->artist
, -1);
401 sb
= g_utf8_strdown(b
->song
->artist
, -1);
402 val
= g_utf8_collate(sa
,sb
);
406 val = (a == NULL)?((b==NULL)?0:-1):1;
410 if (a
->song
->album
&& b
->song
->album
)
413 sa
= g_utf8_strdown(a
->song
->album
, -1);
414 sb
= g_utf8_strdown(b
->song
->album
, -1);
415 val
= g_utf8_collate(sa
,sb
);
426 static gboolean
update_progressbar(AlbumViewPlugin
*self
)
428 gchar
*temp
= g_strdup_printf("%i of %i albums loaded", self
->priv
->current_entry
, self
->priv
->max_entries
);
429 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(self
->priv
->progress_bar
), self
->priv
->current_entry
/(double)self
->priv
->max_entries
);
430 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(self
->priv
->progress_bar
), temp
);
435 static void load_list_itterate(MpdObj
*mi
, AlbumViewPlugin
*self
)
438 MpdData
*data2
= NULL
;
439 self
->priv
->current_entry
++;
440 if(self
->priv
->max_entries
>0 && self
->priv
->current_entry
%25 == 0){
441 g_idle_add(update_progressbar
, self
);
445 mpd_database_search_field_start(mi
, MPD_TAG_ITEM_ARTIST
);
446 mpd_database_search_add_constraint(mi
, MPD_TAG_ITEM_ALBUM
, (self
->priv
->data
)->tag
);
447 data2
= mpd_database_search_commit(mi
);
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(mi
, MPD_TAG_ITEM_ALBUM_ARTIST
))
458 mpd_database_search_field_start(mi
, MPD_TAG_ITEM_ALBUM_ARTIST
);
459 mpd_database_search_add_constraint(mi
, MPD_TAG_ITEM_ALBUM
, (self
->priv
->data
)->tag
);
460 MpdData
*data3
= mpd_database_search_commit(mi
);
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
));
492 while(self
->priv
->data
!= NULL
);
493 self
->priv
->complete_list
= (MpdData
*)misc_sort_mpddata(mpd_data_get_first(self
->priv
->complete_list
), (GCompareDataFunc
)__add_sort
, NULL
);
496 void update_finished(MpdData
*data
, AlbumViewPlugin
*self
)
498 if(self
->priv
->data
== NULL
){
501 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
,"update view\n");
502 gtk_widget_destroy(self
->priv
->progress_bar
);
503 self
->priv
->progress_bar
= NULL
;
504 for(iter
= (MpdData_real
*)self
->priv
->complete_list
; iter
; iter
= iter
->next
) items
++;
506 gtk_widget_set_sensitive(self
->priv
->filter_entry
, TRUE
);
507 filter_list(GTK_ENTRY(self
->priv
->filter_entry
), self
);
509 gtk_widget_grab_focus(self
->priv
->event_bg
);
513 // g_idle_add((GSourceFunc)load_list_itterate, self);
517 static void load_list(AlbumViewPlugin
*self
)
519 if(self
->priv
->complete_list
)mpd_data_free(self
->priv
->complete_list
);
520 self
->priv
->complete_list
= NULL
;
521 if(self
->priv
->current_item
) g_list_free(self
->priv
->current_item
);
522 self
->priv
->current_item
= NULL
;
525 self
->priv
->progress_bar
= gtk_progress_bar_new();
526 gtk_box_pack_start(GTK_BOX(self
->priv
->albumview_box
), self
->priv
->progress_bar
, FALSE
, FALSE
, 0);
527 gtk_widget_show(self
->priv
->progress_bar
);
528 mpd_database_search_field_start(connection
, MPD_TAG_ITEM_ALBUM
);
529 MpdData
*iter
,*data
= mpd_database_search_commit(connection
);
530 self
->priv
->max_entries
= 0;
531 self
->priv
->current_entry
= 0;
532 gtk_widget_set_sensitive(self
->priv
->filter_entry
, FALSE
);
533 for(iter
= data
; iter
; iter
= mpd_data_get_next_real(iter
, FALSE
)) self
->priv
->max_entries
++;
534 self
->priv
->data
= data
;
535 mpd_async_request(update_finished
, self
, load_list_itterate
, self
);
537 void albumview_connection_changed(GmpcConnection
*conn
, MpdObj
*mi
, int connect
,void *usedata
)
539 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(usedata
);
541 if(connect
&& self
->priv
->albumview_main_box
)
545 else if(self
->priv
->albumview_main_box
){
546 mpd_data_free(self
->priv
->complete_list
);
547 self
->priv
->complete_list
= NULL
;
548 if(self
->priv
->item_table
)
549 gtk_widget_hide(self
->priv
->item_table
);
552 static void album_add(GtkWidget
*button
, mpd_Song
*song
)
554 mpd_database_search_start(connection
,TRUE
);
556 mpd_database_search_add_constraint(connection
, MPD_TAG_ITEM_ALBUM
, song
->album
);
557 if(song
->albumartist
&& strlen(song
->albumartist
) >0){
558 mpd_database_search_add_constraint(connection
, MPD_TAG_ITEM_ALBUM_ARTIST
, song
->albumartist
);
560 mpd_database_search_add_constraint(connection
, MPD_TAG_ITEM_ARTIST
, song
->artist
);
562 MpdData
*data
= mpd_database_search_commit(connection
);
563 /* Sort it before adding */
564 data
= misc_sort_mpddata_by_album_disc_track(data
);
565 for(;data
;data
= mpd_data_get_next(data
)){
566 mpd_playlist_queue_add(connection
, data
->song
->file
);
568 mpd_playlist_queue_commit(connection
);
570 static void album_view(GtkWidget
*button
,mpd_Song
*song
)
572 if (song
&& song
->artist
&& song
->album
) {
574 info2_fill_album_view(song
->artist
, song
->album
);
578 static void album_replace(GtkWidget
*button
, mpd_Song
*song
)
580 mpd_playlist_clear(connection
);
581 album_add(button
, song
);
582 mpd_player_play(connection
);
584 static gboolean
album_button_press(GtkWidget
*image
, GtkMenu
*menu
, mpd_Song
*song
)
588 item
= gtk_image_menu_item_new_with_label("Album information");
589 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
),
590 gtk_image_new_from_stock(GTK_STOCK_INFO
, GTK_ICON_SIZE_MENU
));
591 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
592 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(album_view
), song
);
594 item
= gtk_image_menu_item_new_from_stock(GTK_STOCK_ADD
,NULL
);
595 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
596 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(album_add
), song
);
598 /* replace the replace widget */
599 item
= gtk_image_menu_item_new_with_label("Replace");
600 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
),
601 gtk_image_new_from_stock(GTK_STOCK_REDO
, GTK_ICON_SIZE_MENU
));
602 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
603 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(album_replace
), song
);
605 item
= gtk_separator_menu_item_new();
606 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
608 gtk_widget_show(item
);
612 static GtkWidget
* create_button(AlbumViewPlugin
*self
, MpdData_real
*complete_list_iter
)
616 GtkWidget
*label
= NULL
;
618 /* Wrap it in a vbox */
619 vbox
= gtk_vbox_new(FALSE
, 3);
620 gtk_widget_set_size_request(vbox
, self
->priv
->album_size
+20,self
->priv
->album_size
+40);
622 item
= gmpc_metaimage_new_size(META_ALBUM_ART
,self
->priv
->album_size
);
623 gmpc_metaimage_set_scale_up(GMPC_METAIMAGE(item
), TRUE
);
624 gtk_widget_set_has_tooltip(GTK_WIDGET(item
), FALSE
);
625 gmpc_metaimage_set_squared(GMPC_METAIMAGE(item
), TRUE
);
627 gmpc_metaimage_update_cover_from_song_delayed(GMPC_METAIMAGE(item
), complete_list_iter
->song
);
629 gtk_box_pack_start(GTK_BOX(vbox
), item
, TRUE
, TRUE
, 0);
630 /* Set artist name */
631 if(complete_list_iter
->song
->albumartist
){
632 GtkWidget
*label
= gtk_label_new(complete_list_iter
->song
->albumartist
);
633 gtk_label_set_ellipsize(GTK_LABEL(label
), PANGO_ELLIPSIZE_MIDDLE
);
634 gtk_box_pack_end(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
636 GtkWidget
*label
= gtk_label_new(complete_list_iter
->song
->artist
);
637 gtk_label_set_ellipsize(GTK_LABEL(label
), PANGO_ELLIPSIZE_MIDDLE
);
638 gtk_box_pack_end(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
642 label
= gtk_label_new("");
643 temp
= g_markup_printf_escaped("<b>%s</b>", complete_list_iter
->song
->album
);
644 gtk_label_set_markup(GTK_LABEL(label
), temp
);
646 gtk_label_set_ellipsize(GTK_LABEL(label
), PANGO_ELLIPSIZE_MIDDLE
);
647 gtk_box_pack_end(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
650 /* Attach it to the song */
651 g_object_add_weak_pointer(vbox
,&(complete_list_iter
->userdata
));
652 complete_list_iter
->userdata
= vbox
;//g_object_ref_sink(vbox);
653 complete_list_iter
->freefunc
= (void *)gtk_widget_destroy
;
654 g_object_set_data(G_OBJECT(vbox
), "item", item
);
655 g_signal_connect(G_OBJECT(item
), "menu_populate_client", G_CALLBACK(album_button_press
), complete_list_iter
->song
);
657 g_signal_connect(item, "button-press-event",
658 G_CALLBACK(album_button_press), complete_list_iter->song);
663 static void filter_list(GtkEntry
*entry
, gpointer data
)
665 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
666 GRegex
*regex
= NULL
;
669 const gchar
*search_query
= gtk_entry_get_text(GTK_ENTRY(self
->priv
->filter_entry
));
670 MpdData_real
*complete_list_iter
= NULL
;
671 if(search_query
[0] != '\0')
673 gchar
*str
= g_strdup(search_query
);
674 gchar
**test
= g_strsplit(g_strstrip(str
), " ", -1);
676 GString
*s
= g_string_new("((?:");
677 GError
*error
= NULL
;
679 for(i
=0;test
&& test
[i
];i
++){
680 gchar
*temp
= g_regex_escape_string(test
[i
], -1);
681 s
= g_string_append(s
, ".*");
682 s
= g_string_append(s
, temp
);
683 s
= g_string_append(s
, ".*");
684 if(test
[i
+1] != NULL
)
685 s
= g_string_append(s
, "|");
688 g_string_append_printf(s
,"){%i})",i
);
689 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
,"regex: %s\n", s
->str
);
690 regex
= g_regex_new(s
->str
, G_REGEX_CASELESS
|G_REGEX_EXTENDED
, 0,&error
);;
693 g_string_free(s
, TRUE
);
694 for(complete_list_iter
= (MpdData_real
*) mpd_data_get_first(self
->priv
->complete_list
);
696 complete_list_iter
= (MpdData_real
*)mpd_data_get_next_real((MpdData
*)complete_list_iter
, FALSE
))
698 if(g_regex_match(regex
,complete_list_iter
->song
->album
,0,NULL
)||
699 g_regex_match(regex
,complete_list_iter
->song
->artist
,0,NULL
)||
700 (complete_list_iter
->song
->albumartist
&& g_regex_match(regex
,complete_list_iter
->song
->albumartist
,0,NULL
))){
702 list
= g_list_append(list
, complete_list_iter
);
707 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_WARNING
," error creating regex: %s\n", error
->message
);
710 g_regex_unref(regex
);
712 if(self
->priv
->current_item
) g_list_free(self
->priv
->current_item
);
713 self
->priv
->current_item
= g_list_first(list
);
714 if((items
-self
->priv
->supported_rows
*self
->priv
->supported_columns
) > 0)
716 gtk_widget_set_sensitive(GTK_WIDGET(self
->priv
->slider_scale
), TRUE
);
717 gtk_range_set_range(GTK_RANGE(self
->priv
->slider_scale
), 0,
718 ((items
-self
->priv
->supported_rows
*self
->priv
->supported_columns
)>0)?(items
-self
->priv
->supported_rows
*self
->priv
->supported_columns
):1);
721 gtk_widget_set_sensitive(GTK_WIDGET(self
->priv
->slider_scale
), FALSE
);
722 gtk_range_set_range(GTK_RANGE(self
->priv
->slider_scale
), 0,1);
724 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), 0);
728 static void position_changed(GtkRange
*range
, gpointer data
)
730 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
731 gint i
=0,value
= (int)gtk_range_get_value(range
);
732 self
->priv
->current_item
= g_list_first(self
->priv
->current_item
);
733 for(i
=0;i
<value
&& self
->priv
->current_item
&& self
->priv
->current_item
->next
; self
->priv
->current_item
= self
->priv
->current_item
->next
){i
++;}
736 static gboolean
update_view_real(AlbumViewPlugin
*self
)
738 MpdData
*complete_list_iter
;
739 const char *search_query
= gtk_entry_get_text(GTK_ENTRY(self
->priv
->filter_entry
));
744 MpdData
*data
= NULL
;
745 GList
*entries
= NULL
;
746 GList
*list
= (self
->priv
->item_table
)?gtk_container_get_children(GTK_CONTAINER(self
->priv
->item_table
)):NULL
;
748 GRegex
*regex
= NULL
;
750 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
,"search query: %s\n", search_query
);
752 if(self
->priv
->item_table
)
753 gtk_widget_hide(self
->priv
->item_table
);
754 for(iter
= g_list_first(list
); iter
; iter
= iter
->next
){
755 GtkWidget
*widget
= iter
->data
;
756 gtk_container_remove(GTK_CONTAINER(self
->priv
->item_table
), widget
);
763 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_append(self
->priv
->current_item
, complete_list_iter
);
773 self
->priv
->current_item
= g_list_first(self
->priv
->current_item
);
774 if((items
-self
->priv
->supported_rows
*self
->priv
->supported_columns
) > 0)
776 gtk_widget_set_sensitive(GTK_WIDGET(self
->priv
->slider_scale
), TRUE
);
777 gtk_range_set_range(GTK_RANGE(self
->priv
->slider_scale
), 0,
778 ((items
-self
->priv
->supported_rows
*self
->priv
->supported_columns
)>0)?(items
-self
->priv
->supported_rows
*self
->priv
->supported_columns
):1);
781 gtk_widget_set_sensitive(GTK_WIDGET(self
->priv
->slider_scale
), FALSE
);
782 gtk_range_set_range(GTK_RANGE(self
->priv
->slider_scale
), 0,1);
784 gtk_range_set_value(GTK_RANGE(self
->priv
->slider_scale
), 0);
787 int rows
= self
->priv
->supported_rows
;
789 * Create holding table if it does not exist
791 if(!self
->priv
->item_table
){
792 GtkWidget
*ali
= gtk_alignment_new(0.0, 0.5, 0,0);
793 self
->priv
->item_table
= exo_wrap_table_new(TRUE
);//gtk_table_new(rows, supported_columns, TRUE);
794 gtk_container_add(GTK_CONTAINER(ali
), self
->priv
->item_table
);
795 gtk_box_pack_start(GTK_BOX(self
->priv
->albumview_box
), ali
, FALSE
, FALSE
, 0);
798 /* I know how large it is going to be.. so lets set the size */
799 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));
804 if(self
->priv
->current_item
)//(iter = g_list_first(list)))
806 GList
*iter
= self
->priv
->current_item
;
810 complete_list_iter
= iter
->data
;
811 if(complete_list_iter
->song
/* && (complete_list_iter->song->artist)[0] != '\0'*/)
813 GtkWidget
*vbox
= complete_list_iter
->userdata
;
817 vbox
= create_button(self
, (MpdData_real
*)complete_list_iter
);
820 item
= g_object_get_data(G_OBJECT(vbox
), "item");
821 /* Resize if needed */
822 if(self
->priv
->album_size
!= gmpc_metaimage_get_size(GMPC_METAIMAGE(item
))){
823 gtk_widget_set_size_request(vbox
, self
->priv
->album_size
+20,self
->priv
->album_size
+40);
824 gmpc_metaimage_set_size(GMPC_METAIMAGE(item
), self
->priv
->album_size
);
825 gmpc_metaimage_reload_image(GMPC_METAIMAGE(item
));
829 entries
= g_list_prepend(entries
, vbox
);
833 }while(v_items
< (rows
*self
->priv
->supported_columns
)&& (iter
= iter
->next
));
836 if(list
) g_list_free(list
);
839 for(iter
= entries
= g_list_reverse(entries
); iter
; iter
= g_list_next(iter
)){
840 gtk_container_add(GTK_CONTAINER(self
->priv
->item_table
), iter
->data
);
842 if(entries
) g_list_free(entries
);
844 gtk_widget_show_all(self
->priv
->albumview_box
);
848 if(self
->priv
->update_timeout
)
849 g_source_remove(self
->priv
->update_timeout
);
850 self
->priv
->update_timeout
= 0;
855 void update_view(AlbumViewPlugin
*self
)
857 if(self
->priv
->update_timeout
!= 0) {
858 g_source_remove(self
->priv
->update_timeout
);
860 self
->priv
->update_timeout
= g_timeout_add(10, (GSourceFunc
)update_view_real
,self
);
866 static void albumview_plugin_class_init (AlbumViewPluginClass
*klass
);
868 static int *albumview_plugin_get_version(GmpcPluginBase
*plug
, int *length
)
870 static int version
[3] = {PLUGIN_MAJOR_VERSION
,PLUGIN_MINOR_VERSION
,PLUGIN_MICRO_VERSION
};
871 if(length
) *length
= 3;
872 return (int *)version
;
875 static const char *albumview_plugin_get_name(GmpcPluginBase
*plug
)
877 return ("Album View");
879 static GObject
*albumview_plugin_constructor(GType type
, guint n_construct_properties
, GObjectConstructParam
* construct_properties
) {
880 AlbumViewPluginClass
* klass
;
881 AlbumViewPlugin
*self
;
882 GObjectClass
* parent_class
;
883 klass
= (g_type_class_peek (albumview_plugin_get_type()));
884 parent_class
= G_OBJECT_CLASS (g_type_class_peek_parent (klass
));
885 self
= (AlbumViewPlugin
*) parent_class
->constructor (type
, n_construct_properties
, construct_properties
);
887 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Constructing plugin");
889 /* setup private structure */
890 self
->priv
= g_malloc0(sizeof(AlbumViewPluginPrivate
));
892 self
->priv
->supported_rows
= 1;
893 self
->priv
->supported_columns
= 1;
894 self
->priv
->data
= NULL
;
895 self
->priv
->album_size
= 200;
896 self
->priv
->filter_entry
= NULL
;
897 self
->priv
->slider_scale
= NULL
;
898 self
->priv
->progress_bar
= NULL
;
899 self
->priv
->max_entries
= 0;
900 self
->priv
->current_entry
= 0;
901 self
->priv
->current_item
= NULL
;
902 self
->priv
->update_timeout
= 0;
903 self
->priv
->complete_list
= NULL
;
904 self
->priv
->item_table
= NULL
;
905 self
->priv
->albumview_ref
= NULL
;
906 self
->priv
->albumview_box
= NULL
;
908 /* Watch status changed signals */
909 g_signal_connect_object(G_OBJECT(gmpcconn
), "connection-changed", G_CALLBACK(albumview_connection_changed
), self
, 0);
911 /* Setup textdomain */
912 bindtextdomain(GETTEXT_PACKAGE
, PACKAGE_LOCALE_DIR
);
913 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8");
915 GMPC_PLUGIN_BASE(self
)->translation_domain
= GETTEXT_PACKAGE
;
916 GMPC_PLUGIN_BASE(self
)->plugin_type
= GMPC_PLUGIN_NO_GUI
;
918 albumview_plugin_init(self
);
920 return G_OBJECT(self
);
922 static void albumview_plugin_finalize(GObject
*obj
) {
923 AlbumViewPlugin
*self
= (AlbumViewPlugin
*)obj
;
924 AlbumViewPluginClass
* klass
= (g_type_class_peek (play_queue_plugin_get_type()));
925 gpointer parent_class
= g_type_class_peek_parent (klass
);
927 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Destroying plugin");
930 if(self
->priv
->current_item
) g_list_free(self
->priv
->current_item
);
931 self
->priv
->current_item
= NULL
;
932 if(self
->priv
->complete_list
) mpd_data_free(self
->priv
->complete_list
);
933 self
->priv
->complete_list
= NULL
;
939 G_OBJECT_CLASS(parent_class
)->finalize(obj
);
943 static void albumview_plugin_class_init (AlbumViewPluginClass
*klass
)
945 G_OBJECT_CLASS(klass
)->finalize
= albumview_plugin_finalize
;
946 G_OBJECT_CLASS(klass
)->constructor
= albumview_plugin_constructor
;
947 /* Connect plugin functions */
948 GMPC_PLUGIN_BASE_CLASS(klass
)->get_version
= albumview_plugin_get_version
;
949 GMPC_PLUGIN_BASE_CLASS(klass
)->get_name
= albumview_plugin_get_name
;
951 GMPC_PLUGIN_BASE_CLASS(klass
)->get_enabled
= albumview_get_enabled
;
952 GMPC_PLUGIN_BASE_CLASS(klass
)->set_enabled
= albumview_set_enabled
;
954 GMPC_PLUGIN_BASE_CLASS(klass
)->save_yourself
= albumview_browser_save_myself
;
957 static void albumview_plugin_browser_iface_init(GmpcPluginBrowserIfaceIface
* iface
) {
958 iface
->browser_add
= albumview_add
;
959 iface
->browser_selected
= albumview_selected
;
960 iface
->browser_unselected
= albumview_unselected
;
963 const GType
albumview_plugin_get_type(void) {
964 static GType albumview_plugin_type_id
= 0;
965 if(albumview_plugin_type_id
== 0) {
966 static const GTypeInfo info
= {
967 .class_size
= sizeof(AlbumViewPluginClass
),
968 .class_init
= (GClassInitFunc
)albumview_plugin_class_init
,
969 .instance_size
= sizeof(AlbumViewPlugin
),
973 albumview_plugin_type_id
= g_type_register_static(GMPC_PLUGIN_TYPE_BASE
, "AlbumViewPlugin", &info
, 0);
975 /** Browser interface */
976 static const GInterfaceInfo iface_info
= { (GInterfaceInitFunc
) albumview_plugin_browser_iface_init
,
977 (GInterfaceFinalizeFunc
) NULL
, NULL
};
978 g_type_add_interface_static (albumview_plugin_type_id
, GMPC_PLUGIN_TYPE_BROWSER_IFACE
, &iface_info
);
980 return albumview_plugin_type_id
;
983 G_MODULE_EXPORT GType
plugin_get_type(void)
985 return albumview_plugin_get_type();