Add clear entry button
[gmpc-albumview.git] / src / plugin.c
blobbe11de1108c0c179c5bc0506c19f80726eeb12ea
1 #include <config.h>
2 #include <glib.h>
3 #include <glib/gi18n-lib.h>
4 #include <gtk/gtk.h>
5 #include <gdk/gdkkeysyms.h>
6 #include <gmpc/plugin.h>
7 #include <gmpc/playlist3-messages.h>
8 #include <gmpc/gmpc-metaimage.h>
9 #include <gmpc/misc.h>
10 #include <libmpd/libmpd-internal.h>
11 #include <math.h>
12 #include "exo-wrap-table.h"
13 #include "plugin.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;
21 int supported_rows;
22 int album_size;
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;
29 GtkWidget *event_bg;
30 int max_entries;
31 int current_entry;
33 MpdData *complete_list;
34 guint update_timeout;
35 /* temp */
36 MpdData *data;
37 GList *current_item;
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 /**
49 * Get/Set enable
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);
61 if(enabled)
63 if(self->priv->albumview_ref == NULL)
65 albumview_add(GMPC_PLUGIN_BROWSER_IFACE(plug), GTK_WIDGET(playlist3_get_category_tree_view()));
68 else
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);
72 if (path){
73 GtkTreeIter iter;
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;
84 /**
85 * Playlist browser functions
87 static void albumview_add(GmpcPluginBrowserIface *plug, GtkWidget *category_tree)
89 AlbumViewPlugin *self = ALBUM_VIEW_PLUGIN(plug);
90 GtkTreePath *path;
91 GtkTreeModel *model = GTK_TREE_MODEL(playlist3_get_category_tree_store());
92 GtkTreeIter iter;
93 gint pos;
94 /**
95 * don't do anything if we are disabled
97 if(!cfg_get_single_value_as_int_with_default(config, "albumview", "enable", TRUE)) return;
98 /**
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",
107 -1);
108 /**
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);
119 if (path) {
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);
130 if(path)
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))
153 update_view(self);
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);
181 return TRUE;
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);
185 return TRUE;
187 return FALSE;
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);
197 return TRUE;
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);
201 return TRUE;
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);
205 return TRUE;
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);
210 return TRUE;
214 return FALSE;
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);
227 return TRUE;
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);
235 return TRUE;
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,
244 widget->window,
245 GTK_STATE_NORMAL,
246 GTK_SHADOW_NONE,
247 NULL,
248 widget,
249 "entry_bg",
250 0,0,width,height);
251 if(gtk_widget_is_focus(widget))
253 gtk_paint_focus(widget->style, widget->window,
254 GTK_STATE_NORMAL,
255 NULL,
256 widget,
257 "entry_bg",
258 0,0,width,height);
260 return FALSE;
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), "");
269 #endif
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);
293 #endif
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);
314 #endif
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);
326 /* setup bg */
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
347 * from view
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)
375 /* TODO: fix this */
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);
384 g_free(path);
385 g_free(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) {\
391 diff.tv_sec -= 1; \
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)
403 int val;
405 gchar *sa,*sb;
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);
409 g_free(sa);
410 g_free(sb);
411 }/* else {
412 val = (a == NULL)?((b==NULL)?0:-1):1;
414 if(val != 0)
415 return val;
416 if (a->song->album && b->song->album)
418 gchar *sa,*sb;
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);
422 g_free(sa);
423 g_free(sb);
426 return val;
429 return -1;
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);
440 g_free(temp);
443 if(self->priv->data)
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);
448 if(data2)
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);
469 else{
470 mpd_freeSong(song);
471 song = NULL;
473 mpd_data_free(data3);
475 else {
476 mpd_freeSong(song);
477 song = NULL;
480 mpd_data_free(data2);
481 if(song){
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;
485 song = NULL;
489 (self->priv->data) = mpd_data_get_next((self->priv->data));
491 if(self->priv->data == NULL){
492 int items = 0;
493 MpdData_real *iter;
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);
504 return FALSE;
507 g_idle_add((GSourceFunc)load_list_itterate, self);
508 return FALSE;
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)
537 load_list(self);
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);
553 }else{
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) {
567 info2_activate();
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)
580 GtkWidget *item;
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);
604 return TRUE;
606 static GtkWidget * create_button(AlbumViewPlugin *self, MpdData_real *complete_list_iter)
608 GtkWidget *item;
609 GtkWidget *vbox;
610 GtkWidget *label = NULL;
611 gchar *temp = 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);
629 }else{
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);
635 /* Set album name */
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);
639 g_free(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);
653 return vbox;
656 static void filter_list(GtkEntry *entry, gpointer data)
658 AlbumViewPlugin *self = ALBUM_VIEW_PLUGIN(data);
659 GRegex *regex = NULL;
660 int items = 0;
661 GList *list = 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);
668 int i=0;
669 GString *s = g_string_new("((?:");
670 GError *error = NULL;
671 g_free(str);
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, "|");
679 g_free(temp);
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);;
684 if(regex)
686 g_string_free(s, TRUE);
687 for(complete_list_iter = (MpdData_real *) mpd_data_get_first(self->priv->complete_list);
688 complete_list_iter;
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))){
694 items++;
695 list = g_list_append(list, complete_list_iter);
699 if(error) {
700 g_log(AV_LOG_DOMAIN, G_LOG_LEVEL_WARNING," error creating regex: %s\n", error->message);
701 g_error_free(error);
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);
713 else{
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);
718 update_view(self);
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++;}
727 update_view(self);
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));
733 int i=0;
734 int j=0;
735 gchar *artist= NULL;
736 int items =0;
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;
740 GList *iter;
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);
751 g_list_free(list);
752 list = NULL;
756 gtk_widget_show(self->priv->albumview_box);
757 if(self->priv->current_item == NULL){
758 int items =0;
759 for(complete_list_iter = mpd_data_get_first(self->priv->complete_list);
760 complete_list_iter;
761 complete_list_iter = mpd_data_get_next_real(complete_list_iter, FALSE))
763 items++;
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);
773 else{
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));
794 * Add albums
797 if(self->priv->current_item)//(iter = g_list_first(list)))
799 GList *iter = self->priv->current_item;
800 int v_items = 0;
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;
807 GtkWidget *item;
808 int a,b;
809 if(vbox == NULL){
810 vbox = create_button(self, (MpdData_real *)complete_list_iter);
812 else{
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);
823 j++;
825 v_items++;
826 }while(v_items < (rows*self->priv->supported_columns)&& (iter = iter->next));
828 /* remove list */
829 if(list) g_list_free(list);
830 list = NULL;
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);
839 * Remove the timeout
841 if(self->priv->update_timeout)
842 g_source_remove(self->priv->update_timeout);
843 self->priv->update_timeout = 0;
844 return FALSE;
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);
857 * Gobject plugin
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));
884 /* set defaults */
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");
922 if(self->priv){
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;
928 g_free(self->priv);
929 self->priv = NULL;
931 if(parent_class)
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),
963 .n_preallocs = 0
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();