gmpc-albumview version 11.8.16
[gmpc-albumview.git] / src / plugin.c
blobd328d429bbbfc8c0e8becc0b07fb58590dcb3abe
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 <gmpc/gmpc-extras.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 gboolean require_scale_update;
31 int max_entries;
32 int current_entry;
34 MpdData *complete_list;
35 guint update_timeout;
36 /* temp */
37 MpdData *data;
38 GList *current_item;
39 GtkTreeRowReference *albumview_ref;
40 }_AlbumViewPluginPrivate;
44 static gchar * albumview_format_time(unsigned long seconds);
45 static void albumview_browser_save_myself(GmpcPluginBase *plug);
46 static void position_changed(GtkRange *range, gpointer data);
47 static void filter_list(GtkEntry *entry, gpointer data);
48 void update_view(AlbumViewPlugin *self);
49 static void load_list(AlbumViewPlugin *self);
50 /**
51 * Get/Set enable
54 static int albumview_get_enabled(GmpcPluginBase *plug)
56 return cfg_get_single_value_as_int_with_default(config, "albumview", "enable", TRUE);
59 void albumview_set_enabled(GmpcPluginBase *plug, int enabled)
61 AlbumViewPlugin *self = ALBUM_VIEW_PLUGIN(plug);
62 cfg_set_single_value_as_int(config, "albumview", "enable", enabled);
63 if(enabled)
65 if(self->priv->albumview_ref == NULL)
67 albumview_add(GMPC_PLUGIN_BROWSER_IFACE(plug), GTK_WIDGET(playlist3_get_category_tree_view()));
70 else
72 GtkTreePath *path = gtk_tree_row_reference_get_path(self->priv->albumview_ref);
73 GtkTreeModel *model = gtk_tree_row_reference_get_model(self->priv->albumview_ref);
74 if (path){
75 GtkTreeIter iter;
76 if (gtk_tree_model_get_iter(model, &iter, path)){
77 gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
79 gtk_tree_path_free(path);
80 gtk_tree_row_reference_free(self->priv->albumview_ref);
81 self->priv->albumview_ref = NULL;
86 /**
87 * Playlist browser functions
89 static void albumview_add(GmpcPluginBrowserIface *plug, GtkWidget *category_tree)
91 AlbumViewPlugin *self = ALBUM_VIEW_PLUGIN(plug);
92 GtkTreePath *path;
93 GtkTreeModel *model = GTK_TREE_MODEL(playlist3_get_category_tree_store());
94 GtkTreeIter iter;
95 gint pos;
96 /**
97 * don't do anything if we are disabled
99 if(!cfg_get_single_value_as_int_with_default(config, "albumview", "enable", TRUE)) return;
101 * Add ourslef to the list
103 pos = cfg_get_single_value_as_int_with_default(config, "albumview","position",2);
104 playlist3_insert_browser(&iter, pos);
105 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
106 PL3_CAT_TYPE, GMPC_PLUGIN_BASE(plug)->id,
107 PL3_CAT_TITLE,"Album View",
108 PL3_CAT_ICON_ID, "albumview",
109 -1);
111 * remove odl reference if exists
113 if (self->priv->albumview_ref) {
114 gtk_tree_row_reference_free(self->priv->albumview_ref);
115 self->priv->albumview_ref = NULL;
118 * create reference to ourself in the list
120 path = gtk_tree_model_get_path(GTK_TREE_MODEL(model), &iter);
121 if (path) {
122 self->priv->albumview_ref = gtk_tree_row_reference_new(model, path);
123 gtk_tree_path_free(path);
126 static void albumview_browser_save_myself(GmpcPluginBase *plug)
128 AlbumViewPlugin *self = ALBUM_VIEW_PLUGIN(plug);
129 if(self->priv->albumview_ref)
131 GtkTreePath *path = gtk_tree_row_reference_get_path(self->priv->albumview_ref);
132 if(path)
134 gint *indices = gtk_tree_path_get_indices(path);
135 g_log(AV_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Saving myself to position: %i", indices[0]);
136 cfg_set_single_value_as_int(config, "albumview","position",indices[0]);
137 gtk_tree_path_free(path);
142 void size_changed(GtkWidget *widget, GtkAllocation *alloc, gpointer user_data)
144 AlbumViewPlugin *self = ALBUM_VIEW_PLUGIN(user_data);
145 int columns = (alloc->width-10)/(self->priv->album_size +25);
146 int rows = (alloc->height-10)/(self->priv->album_size +40);
148 if(columns != self->priv->supported_columns || rows != self->priv->supported_rows)
150 self->priv->supported_columns = (columns)?columns:1;
151 self->priv->supported_rows = (rows)?rows:1;
152 printf("supported rows: %i\n", self->priv->supported_rows);
153 g_log(AV_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "update columns: %i %i %i\n",
154 alloc->width-20,columns, self->priv->album_size);
155 self->priv->require_scale_update = TRUE;
156 if(self->priv->filter_entry && GTK_WIDGET_IS_SENSITIVE(self->priv->filter_entry))
158 update_view(self);
164 static gboolean albumview_scroll_event(GtkWidget *event_box, GdkEventScroll *event, gpointer data)
166 AlbumViewPlugin *self = ALBUM_VIEW_PLUGIN(data);
167 if(self->priv->current_item == NULL) return FALSE;
168 if(event->direction == GDK_SCROLL_UP)
170 int value = gtk_range_get_value(GTK_RANGE(self->priv->slider_scale))-1;
171 gtk_range_set_value(GTK_RANGE(self->priv->slider_scale), value);
172 return TRUE;
173 }else if(event->direction == GDK_SCROLL_DOWN) {
174 int value = gtk_range_get_value(GTK_RANGE(self->priv->slider_scale))+1;
175 gtk_range_set_value(GTK_RANGE(self->priv->slider_scale), value);
176 return TRUE;
178 return FALSE;
180 static gboolean albumview_key_press_event(GtkWidget *event_box, GdkEventKey *event, gpointer data)
182 AlbumViewPlugin *self = ALBUM_VIEW_PLUGIN(data);
183 if(self->priv->current_item == NULL) return FALSE;
185 if(event->keyval == GDK_Up){
186 int value = gtk_range_get_value(GTK_RANGE(self->priv->slider_scale))-1;
187 gtk_range_set_value(GTK_RANGE(self->priv->slider_scale), value);
188 return TRUE;
189 }else if (event->keyval == GDK_Down){
190 int value = gtk_range_get_value(GTK_RANGE(self->priv->slider_scale))+1;
191 gtk_range_set_value(GTK_RANGE(self->priv->slider_scale), value);
192 return TRUE;
193 }else if (event->keyval == GDK_Page_Up) {
194 int value = gtk_range_get_value(GTK_RANGE(self->priv->slider_scale))-5;
195 gtk_range_set_value(GTK_RANGE(self->priv->slider_scale), value);
196 return TRUE;
198 }else if (event->keyval == GDK_Page_Down) {
199 int value = gtk_range_get_value(GTK_RANGE(self->priv->slider_scale))+5;
200 gtk_range_set_value(GTK_RANGE(self->priv->slider_scale), value);
201 return TRUE;
205 return FALSE;
207 static gboolean albumview_button_press_event(GtkWidget *event_box, GdkEventButton *event, gpointer data)
209 AlbumViewPlugin *self = ALBUM_VIEW_PLUGIN(data);
210 if(self->priv->current_item == NULL) return FALSE;
211 gtk_widget_grab_focus(self->priv->event_bg);
213 static gboolean albumview_focus(GtkWidget *wid,GdkEventFocus *event, gpointer data)
215 AlbumViewPlugin *self = ALBUM_VIEW_PLUGIN(data);
216 g_log(AV_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "focus in");
217 gtk_widget_queue_draw(self->priv->event_bg);
218 return TRUE;
220 static gboolean albumview_focus_out(GtkWidget *wid,GdkEventFocus *event, gpointer data)
222 AlbumViewPlugin *self = ALBUM_VIEW_PLUGIN(data);
223 g_log(AV_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "focus out");
225 gtk_widget_queue_draw(self->priv->event_bg);
226 return TRUE;
228 static gboolean albumview_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
230 int width = widget->allocation.width;
231 int height = widget->allocation.height;
232 AlbumViewPlugin *self = ALBUM_VIEW_PLUGIN(data);
234 gtk_paint_flat_box(widget->style,
235 widget->window,
236 GTK_STATE_NORMAL,
237 GTK_SHADOW_NONE,
238 NULL,
239 widget,
240 "entry_bg",
241 0,0,width,height);
242 if(gtk_widget_is_focus(widget))
244 gtk_paint_focus(widget->style, widget->window,
245 GTK_STATE_NORMAL,
246 NULL,
247 widget,
248 "entry_bg",
249 0,0,width,height);
251 return FALSE;
253 #if GTK_CHECK_VERSION(2,16,0)
254 static void mod_fill_clear_search_entry(GtkEntry *entry, GtkEntryIconPosition icon_pos, GdkEvent *event, gpointer user_data)
256 if(icon_pos == GTK_ENTRY_ICON_SECONDARY){
257 gtk_entry_set_text(GTK_ENTRY(entry), "");
260 #endif
261 static void albumview_init(AlbumViewPlugin *self)
263 /** Get an allready exposed widgets to grab theme colors from. */
264 GtkWidget *colw = (GtkWidget *)playlist3_get_category_tree_view();
265 GtkWidget *label = NULL;
266 GtkWidget *event = gtk_scrolled_window_new(NULL, NULL);
267 GtkWidget *hbox = NULL; int i = 0;
268 self->priv->event_bg = gtk_event_box_new();
269 self->priv->albumview_main_box = gtk_vbox_new(FALSE, 6);
271 g_signal_connect(G_OBJECT(event), "size-allocate", G_CALLBACK(size_changed), self);
273 GtkWidget *iv = self->priv->albumview_box = gtk_vbox_new(FALSE, 6);
274 self->priv->slider_scale = gtk_vscale_new_with_range(0,1,1);
275 gtk_scale_set_draw_value(GTK_SCALE(self->priv->slider_scale), FALSE);
276 g_signal_connect(G_OBJECT(self->priv->slider_scale), "value-changed", G_CALLBACK(position_changed), self);
278 self->priv->filter_entry = gtk_entry_new();
280 #if GTK_CHECK_VERSION(2,16,0)
281 gtk_entry_set_icon_from_stock(GTK_ENTRY(self->priv->filter_entry), GTK_ENTRY_ICON_SECONDARY, GTK_STOCK_CLEAR);
282 g_signal_connect(GTK_ENTRY(self->priv->filter_entry), "icon-press", G_CALLBACK(mod_fill_clear_search_entry), NULL);
283 #endif
284 g_signal_connect(G_OBJECT(self->priv->filter_entry),"changed", G_CALLBACK(filter_list), self);
287 hbox = gtk_hbox_new(FALSE, 6);
288 gtk_box_pack_start(GTK_BOX(hbox),gtk_label_new(("Filter")), FALSE, FALSE, 0);
289 gtk_box_pack_start(GTK_BOX(hbox),self->priv->filter_entry, TRUE, TRUE, 0);
291 gtk_box_pack_end(GTK_BOX(self->priv->albumview_main_box), hbox, FALSE, FALSE, 0);
293 hbox = gtk_hbox_new(FALSE, 6);
294 gtk_box_pack_start(GTK_BOX(self->priv->albumview_main_box),hbox, TRUE, TRUE, 0);
295 gtk_box_pack_start(GTK_BOX(hbox),event, TRUE, TRUE, 0);
296 gtk_box_pack_start(GTK_BOX(hbox),self->priv->slider_scale, FALSE, FALSE, 0);//gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw), event);
297 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(event), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
298 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(event), GTK_SHADOW_ETCHED_IN);
299 /* setup bg */
300 /* TODO draw focus */
301 // gtk_widget_modify_bg(self->priv->event_bg, GTK_STATE_NORMAL,&(self->priv->albumview_main_box->style->white));
302 gtk_widget_set_app_paintable(self->priv->event_bg, TRUE);
303 g_signal_connect(G_OBJECT(self->priv->event_bg), "expose-event", G_CALLBACK(albumview_expose_event), self);
304 gtk_event_box_set_visible_window(GTK_EVENT_BOX(self->priv->event_bg), TRUE);
306 g_object_set(self->priv->event_bg, "can-focus", TRUE,NULL);
307 GTK_WIDGET_SET_FLAGS(self->priv->event_bg, GTK_HAS_FOCUS);
308 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(event), self->priv->event_bg);
309 gtk_container_add(GTK_CONTAINER(self->priv->event_bg), iv);
310 gtk_widget_add_events(self->priv->event_bg, GDK_SCROLL_MASK|GDK_BUTTON_PRESS_MASK|GDK_FOCUS_CHANGE_MASK);
311 g_signal_connect_object(G_OBJECT(self->priv->event_bg), "scroll-event", G_CALLBACK(albumview_scroll_event), self,0);
312 g_signal_connect_object(G_OBJECT(self->priv->event_bg), "key-press-event", G_CALLBACK(albumview_key_press_event), self,0);
313 g_signal_connect_object(G_OBJECT(self->priv->event_bg), "focus-in-event", G_CALLBACK(albumview_focus), self,0);
314 g_signal_connect_object(G_OBJECT(self->priv->event_bg), "focus-out-event", G_CALLBACK(albumview_focus_out), self, 0);
315 g_signal_connect_object(G_OBJECT(self->priv->filter_entry), "key-press-event", G_CALLBACK(albumview_key_press_event), self,0);
316 g_signal_connect_object(G_OBJECT(self->priv->event_bg), "button-press-event", G_CALLBACK(albumview_button_press_event), self,0);
318 gtk_widget_show_all(self->priv->albumview_main_box);
321 /* maintain my own reference to the widget, so it won't get destroyed removing
322 * from view
324 g_object_ref_sink(self->priv->albumview_main_box);
328 static void albumview_selected(GmpcPluginBrowserIface *plug, GtkWidget *container)
330 AlbumViewPlugin *self = ALBUM_VIEW_PLUGIN(plug);
331 if(self->priv->albumview_main_box== NULL) {
332 albumview_init((AlbumViewPlugin *)plug);
333 albumview_connection_changed(gmpcconn, connection,1,self);
335 gtk_container_add(GTK_CONTAINER(container), self->priv->albumview_main_box);
336 gtk_widget_show(self->priv->albumview_main_box);
337 gtk_widget_show(container);
338 gtk_widget_grab_focus(self->priv->event_bg);
341 static void albumview_unselected(GmpcPluginBrowserIface *plug,GtkWidget *container)
343 AlbumViewPlugin *self = ALBUM_VIEW_PLUGIN(plug);
344 gtk_container_remove(GTK_CONTAINER(container), self->priv->albumview_main_box);
347 static void status_changed(GmpcConnection *gmpcconnn,MpdObj *mi, ChangedStatusType type, AlbumViewPlugin *self)
349 if((type&MPD_CST_DATABASE) > 0)
352 if(self->priv->albumview_main_box)
353 load_list(self);
357 void albumview_plugin_init(AlbumViewPlugin *self)
359 gchar *path;
360 const gchar * const *paths = g_get_system_data_dirs();
361 int i;
363 /* First try the compile time path */
364 path = g_build_filename(PIXMAP_DATA_DIR, NULL);
365 if(path){
366 if(!g_file_test(path, G_FILE_TEST_EXISTS|G_FILE_TEST_IS_DIR))
368 g_free(path);
369 path = NULL;
373 for(i=0; path == NULL && paths && paths[i]; i++) {
374 path = g_build_filename(paths[i], "gmpc-albumview", "icons", NULL);
375 if(!g_file_test(path, G_FILE_TEST_EXISTS|G_FILE_TEST_IS_DIR))
377 g_free(path);
378 path = NULL;
381 if(path) {
382 gtk_icon_theme_append_search_path(gtk_icon_theme_get_default (),path);
384 g_signal_connect_object(G_OBJECT(gmpcconn), "status-changed", G_CALLBACK(status_changed), self, 0);
386 g_free(path);
387 path = NULL;
389 #define TIMER_SUB(start,stop,diff) diff.tv_usec = stop.tv_usec - start.tv_usec;\
390 diff.tv_sec = stop.tv_sec - start.tv_sec;\
391 if(diff.tv_usec < 0) {\
392 diff.tv_sec -= 1; \
393 diff.tv_usec += G_USEC_PER_SEC; \
395 static gint __add_sort(gpointer aa, gpointer bb, gpointer data)
397 MpdData_real *a = *(MpdData_real **)aa;
398 MpdData_real *b = *(MpdData_real **)bb;
399 if(!a || !b) return 0;
400 if(a->type == MPD_DATA_TYPE_SONG && b->type == MPD_DATA_TYPE_SONG)
402 if(a->song->artist && b->song->artist)
404 int val;
406 gchar *sa,*sb;
407 sa = g_utf8_strdown(a->song->artist, -1);
408 sb = g_utf8_strdown(b->song->artist, -1);
409 val = g_utf8_collate(sa,sb);
410 g_free(sa);
411 g_free(sb);
412 }/* else {
413 val = (a == NULL)?((b==NULL)?0:-1):1;
415 if(val != 0)
416 return val;
417 if (a->song->album && b->song->album)
419 gchar *sa,*sb;
420 sa = g_utf8_strdown(a->song->album, -1);
421 sb = g_utf8_strdown(b->song->album, -1);
422 val = g_utf8_collate(sa,sb);
423 g_free(sa);
424 g_free(sb);
427 return val;
430 return -1;
433 static gboolean update_progressbar(AlbumViewPlugin *self)
435 gchar *temp = g_strdup_printf("%i of %i albums loaded", self->priv->current_entry, self->priv->max_entries);
436 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(self->priv->progress_bar), self->priv->current_entry/(double)self->priv->max_entries);
437 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(self->priv->progress_bar), temp);
438 g_free(temp);
439 return FALSE;
442 static void load_list_itterate(MpdObj *mi, AlbumViewPlugin *self)
445 MpdData *data2 = NULL;
446 self->priv->current_entry++;
447 if(self->priv->max_entries>0 && self->priv->current_entry%25 == 0){
448 g_idle_add(update_progressbar, self);
450 if(self->priv->data)
452 mpd_database_search_field_start(mi, MPD_TAG_ITEM_ARTIST);
453 mpd_database_search_add_constraint(mi, MPD_TAG_ITEM_ALBUM, (self->priv->data)->tag);
454 data2 = mpd_database_search_commit(mi);
455 if(data2)
457 mpd_Song *song = mpd_newSong();
458 song->album = g_strdup((self->priv->data)->tag);
459 song->artist = g_strdup(data2->tag);
460 if(!mpd_data_is_last(data2))
462 /* test if server supports album artist */
463 if(mpd_server_tag_supported(mi, MPD_TAG_ITEM_ALBUM_ARTIST))
465 mpd_database_search_field_start(mi, MPD_TAG_ITEM_ALBUM_ARTIST);
466 mpd_database_search_add_constraint(mi, MPD_TAG_ITEM_ALBUM, (self->priv->data)->tag);
467 MpdData *data3 = mpd_database_search_commit(mi);
468 if(mpd_data_is_last(data3)){
469 if(strlen(data3->tag) > 0)
471 song->albumartist = g_strdup(data3->tag);
472 if(song->artist) g_free(song->artist);
473 song->artist = g_strdup(data3->tag);
476 else{
477 mpd_freeSong(song);
478 song = NULL;
480 mpd_data_free(data3);
482 else {
483 mpd_freeSong(song);
484 song = NULL;
487 mpd_data_free(data2);
488 if(song){
489 self->priv->complete_list = mpd_new_data_struct_append(self->priv->complete_list);
490 self->priv->complete_list->song = song;
491 self->priv->complete_list->type = MPD_DATA_TYPE_SONG;
492 song = NULL;
496 (self->priv->data) = mpd_data_get_next((self->priv->data));
499 while(self->priv->data != NULL);
500 self->priv->complete_list = (MpdData *)misc_sort_mpddata(mpd_data_get_first(self->priv->complete_list), (GCompareDataFunc)__add_sort, NULL);
503 void update_finished(MpdData *data, AlbumViewPlugin *self)
505 if(self->priv->data == NULL){
506 int items = 0;
507 MpdData_real *iter;
508 g_log(AV_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,"update view\n");
509 gtk_widget_destroy(self->priv->progress_bar);
510 self->priv->progress_bar = NULL;
511 for(iter = (MpdData_real*)self->priv->complete_list; iter; iter = iter->next) items++;
513 gtk_widget_set_sensitive(self->priv->filter_entry, TRUE);
514 filter_list(GTK_ENTRY(self->priv->filter_entry), self);
516 gtk_widget_grab_focus(self->priv->event_bg);
520 static void load_list(AlbumViewPlugin *self)
522 if(self->priv->complete_list)mpd_data_free(self->priv->complete_list);
523 self->priv->complete_list = NULL;
524 if(self->priv->current_item) g_list_free(self->priv->current_item);
525 self->priv->current_item = NULL;
528 self->priv->progress_bar = gtk_progress_bar_new();
529 gtk_box_pack_start(GTK_BOX(self->priv->albumview_box), self->priv->progress_bar, FALSE, FALSE, 0);
530 gtk_widget_show(self->priv->progress_bar);
531 mpd_database_search_field_start(connection, MPD_TAG_ITEM_ALBUM);
532 MpdData *iter,*data = mpd_database_search_commit(connection);
533 self->priv->max_entries= 0;
534 self->priv->current_entry = 0;
535 gtk_widget_set_sensitive(self->priv->filter_entry, FALSE);
536 for(iter = data; iter; iter = mpd_data_get_next_real(iter, FALSE)) self->priv->max_entries++;
537 self->priv->data= data;
538 mpd_async_request(update_finished, self, load_list_itterate, self);
540 void albumview_connection_changed(GmpcConnection *conn, MpdObj *mi, int connect,void *usedata)
542 AlbumViewPlugin *self = ALBUM_VIEW_PLUGIN(usedata);
544 if(connect && self->priv->albumview_main_box)
546 load_list(self);
548 else if(self->priv->albumview_main_box){
549 mpd_data_free(self->priv->complete_list);
550 self->priv->complete_list = NULL;
551 if(self->priv->item_table)
552 gtk_widget_hide(self->priv->item_table);
555 static void album_add(GtkWidget *button, mpd_Song *song)
557 mpd_database_search_start(connection,TRUE);
559 mpd_database_search_add_constraint(connection, MPD_TAG_ITEM_ALBUM, song->album);
560 if(song->albumartist && strlen(song->albumartist) >0){
561 mpd_database_search_add_constraint(connection, MPD_TAG_ITEM_ALBUM_ARTIST, song->albumartist);
562 }else{
563 mpd_database_search_add_constraint(connection, MPD_TAG_ITEM_ARTIST, song->artist);
565 MpdData *data = mpd_database_search_commit(connection);
566 /* Sort it before adding */
567 data = misc_sort_mpddata_by_album_disc_track(data);
568 for(;data;data = mpd_data_get_next(data)){
569 mpd_playlist_queue_add(connection, data->song->file);
571 mpd_playlist_queue_commit(connection);
573 static void album_view(GtkWidget *button ,mpd_Song *song)
575 if (song && song->artist && song->album) {
576 info2_activate();
577 info2_fill_album_view(song->artist, song->album);
581 static void album_replace(GtkWidget *button, mpd_Song *song)
583 mpd_playlist_clear(connection);
584 album_add(button, song);
585 mpd_player_play(connection);
587 static gboolean album_button_press(GtkWidget *image, GtkMenu *menu, mpd_Song *song)
589 GtkWidget *item;
591 item = gtk_image_menu_item_new_with_label("Album information");
592 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
593 gtk_image_new_from_stock(GTK_STOCK_INFO, GTK_ICON_SIZE_MENU));
594 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
595 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(album_view), song);
597 item = gtk_image_menu_item_new_from_stock(GTK_STOCK_ADD,NULL);
598 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
599 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(album_add), song);
601 /* replace the replace widget */
602 item = gtk_image_menu_item_new_with_label("Replace");
603 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item),
604 gtk_image_new_from_stock(GTK_STOCK_REDO, GTK_ICON_SIZE_MENU));
605 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
606 g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(album_replace), song);
608 item = gtk_separator_menu_item_new();
609 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
611 gtk_widget_show(item);
613 return TRUE;
615 static GtkWidget * create_button(AlbumViewPlugin *self, MpdData_real *complete_list_iter)
617 GtkWidget *item;
618 GtkWidget *vbox;
619 GtkWidget *label = NULL;
620 gchar *temp = NULL;
621 /* Wrap it in a vbox */
622 vbox = gtk_vbox_new(FALSE, 3);
623 gtk_widget_set_size_request(vbox, self->priv->album_size+20,self->priv->album_size+40);
625 item = (GtkWidget *)gmpc_metaimage_new_size(META_ALBUM_ART,self->priv->album_size);
626 gmpc_metaimage_set_scale_up(GMPC_METAIMAGE(item), TRUE);
627 gtk_widget_set_has_tooltip(GTK_WIDGET(item), FALSE);
628 gmpc_metaimage_set_squared(GMPC_METAIMAGE(item), TRUE);
630 gmpc_metaimage_update_cover_from_song_delayed(GMPC_METAIMAGE(item), complete_list_iter->song);
632 gtk_box_pack_start(GTK_BOX(vbox), item, TRUE, TRUE, 0);
633 /* Set artist name */
634 if(complete_list_iter->song->albumartist){
635 GtkWidget *label = gtk_label_new(complete_list_iter->song->albumartist);
636 gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_MIDDLE);
637 gtk_box_pack_end(GTK_BOX(vbox), label, FALSE, FALSE, 0);
638 }else{
639 GtkWidget *label = gtk_label_new(complete_list_iter->song->artist);
640 gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_MIDDLE);
641 gtk_box_pack_end(GTK_BOX(vbox), label, FALSE, FALSE, 0);
644 /* Set album name */
645 label = gtk_label_new("");
646 temp = g_markup_printf_escaped("<b>%s</b>", complete_list_iter->song->album);
647 gtk_label_set_markup(GTK_LABEL(label), temp);
648 g_free(temp);
649 gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_MIDDLE);
650 gtk_box_pack_end(GTK_BOX(vbox), label, FALSE, FALSE, 0);
653 /* Attach it to the song */
654 g_object_add_weak_pointer(G_OBJECT(vbox),&(complete_list_iter->userdata));
655 complete_list_iter->userdata = vbox;//g_object_ref_sink(vbox);
656 complete_list_iter->freefunc = (void *)gtk_widget_destroy;
657 g_object_set_data(G_OBJECT(vbox), "item", item);
658 g_signal_connect(G_OBJECT(item), "menu_populate_client", G_CALLBACK(album_button_press), complete_list_iter->song);
660 g_signal_connect(item, "button-press-event",
661 G_CALLBACK(album_button_press), complete_list_iter->song);
663 return vbox;
666 static void filter_list(GtkEntry *entry, gpointer data)
668 AlbumViewPlugin *self = ALBUM_VIEW_PLUGIN(data);
669 GRegex *regex = NULL;
670 int items = 0;
671 GList *list = NULL;
672 const gchar *search_query = gtk_entry_get_text(GTK_ENTRY(self->priv->filter_entry));
673 MpdData_real *complete_list_iter = NULL;
674 if(search_query[0] != '\0')
676 gchar *str = g_strdup(search_query);
677 gchar **test = g_strsplit(g_strstrip(str), " ", -1);
678 int i=0;
679 GString *s = g_string_new("((?:");
680 GError *error = NULL;
681 g_free(str);
682 for(i=0;test && test[i];i++){
683 gchar *temp = g_regex_escape_string(test[i], -1);
684 s = g_string_append(s, ".*");
685 s= g_string_append(s, temp);
686 s = g_string_append(s, ".*");
687 if(test[i+1] != NULL)
688 s = g_string_append(s, "|");
689 g_free(temp);
691 g_string_append_printf(s,"){%i})",i);
692 g_log(AV_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,"regex: %s\n", s->str);
693 regex = g_regex_new(s->str, G_REGEX_CASELESS|G_REGEX_EXTENDED, 0,&error);;
694 if(regex)
696 g_string_free(s, TRUE);
697 for(complete_list_iter = (MpdData_real *) mpd_data_get_first(self->priv->complete_list);
698 complete_list_iter;
699 complete_list_iter = (MpdData_real *)mpd_data_get_next_real((MpdData *)complete_list_iter, FALSE))
701 if(g_regex_match(regex,complete_list_iter->song->album,0,NULL)||
702 g_regex_match(regex,complete_list_iter->song->artist,0,NULL)||
703 (complete_list_iter->song->albumartist && g_regex_match(regex,complete_list_iter->song->albumartist,0,NULL))){
704 items++;
705 list = g_list_append(list, complete_list_iter);
709 if(error) {
710 g_log(AV_LOG_DOMAIN, G_LOG_LEVEL_WARNING," error creating regex: %s\n", error->message);
711 g_error_free(error);
713 g_regex_unref(regex);
715 if(self->priv->current_item) g_list_free(self->priv->current_item);
716 self->priv->current_item = g_list_first(list);
717 self->priv->require_scale_update = TRUE;
718 gtk_range_set_value(GTK_RANGE(self->priv->slider_scale), 0);
720 update_view(self);
723 static void position_changed(GtkRange *range, gpointer data)
725 AlbumViewPlugin *self = ALBUM_VIEW_PLUGIN(data);
726 gint i=0,value = (int)gtk_range_get_value(range)*self->priv->supported_columns;
727 self->priv->current_item = g_list_first(self->priv->current_item);
728 for(i=0;i<value && self->priv->current_item && self->priv->current_item->next; self->priv->current_item = self->priv->current_item->next){i++;}
729 update_view(self);
731 static gboolean update_view_real(AlbumViewPlugin *self)
733 MpdData *complete_list_iter;
734 const char *search_query = gtk_entry_get_text(GTK_ENTRY(self->priv->filter_entry));
735 int i=0;
736 int j=0;
737 gchar *artist= NULL;
738 int items =0;
739 MpdData *data = NULL;
740 GList *entries = NULL;
741 GList *list = (self->priv->item_table)?gtk_container_get_children(GTK_CONTAINER(self->priv->item_table)):NULL;
742 GList *iter;
743 GRegex *regex = NULL;
745 g_log(AV_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,"search query: %s\n", search_query);
747 if(self->priv->item_table)
748 gtk_widget_hide(self->priv->item_table);
749 for(iter = g_list_first(list); iter; iter = iter->next){
750 GtkWidget *widget = iter->data;
751 gtk_container_remove(GTK_CONTAINER(self->priv->item_table), widget);
753 g_list_free(list);
754 list = NULL;
758 gtk_widget_show(self->priv->albumview_box);
760 if(self->priv->current_item == NULL){
761 int items =0;
762 for(complete_list_iter = mpd_data_get_first(self->priv->complete_list);
763 complete_list_iter;
764 complete_list_iter = mpd_data_get_next_real(complete_list_iter, FALSE))
766 items++;
767 self->priv->current_item = g_list_prepend(self->priv->current_item, complete_list_iter);
769 self->priv->current_item = g_list_reverse(self->priv->current_item);
770 // self->priv->current_item = g_list_first(self->priv->current_item);
771 gtk_range_set_value(GTK_RANGE(self->priv->slider_scale), 0);
772 self->priv->require_scale_update = TRUE;
774 /* TODO: Only update this when something changed! */
775 if(self->priv->require_scale_update)
777 int items = g_list_length(g_list_first(self->priv->current_item));
778 if(ceil(items/(double)self->priv->supported_columns)-self->priv->supported_rows > 0)
780 gtk_widget_set_sensitive(GTK_WIDGET(self->priv->slider_scale), TRUE);
781 gtk_range_set_range(GTK_RANGE(self->priv->slider_scale), 0,
782 ceil(items/(double)self->priv->supported_columns)-self->priv->supported_rows);
784 else{
785 gtk_widget_set_sensitive(GTK_WIDGET(self->priv->slider_scale), FALSE);
786 gtk_range_set_range(GTK_RANGE(self->priv->slider_scale), 0,1);
788 self->priv->require_scale_update = FALSE;
792 * Create holding table if it does not exist
794 if(!self->priv->item_table){
795 self->priv->item_table = (GtkWidget *)gmpc_widgets_qtable_new();
796 gmpc_widgets_qtable_set_item_width(GMPC_WIDGETS_QTABLE(self->priv->item_table),
797 self->priv->album_size+25);
798 gmpc_widgets_qtable_set_item_height(GMPC_WIDGETS_QTABLE(self->priv->item_table),
799 self->priv->album_size+40);
800 gtk_box_pack_start(GTK_BOX(self->priv->albumview_box), self->priv->item_table, TRUE, TRUE, 0);
804 * Add albums
807 if(self->priv->current_item)
809 int rows = self->priv->supported_rows;
810 GList *iter = self->priv->current_item;
811 int v_items = 0;
814 complete_list_iter = iter->data;
815 if(complete_list_iter->song)
817 GtkWidget *vbox = complete_list_iter->userdata;
818 GtkWidget *item;
819 int a,b;
820 if(vbox == NULL){
821 vbox = create_button(self, (MpdData_real *)complete_list_iter);
823 else{
824 item = g_object_get_data(G_OBJECT(vbox), "item");
825 /* Resize if needed */
826 if(self->priv->album_size != gmpc_metaimage_get_size(GMPC_METAIMAGE(item))){
827 gtk_widget_set_size_request(vbox, self->priv->album_size+20,self->priv->album_size+40);
828 gmpc_metaimage_set_size(GMPC_METAIMAGE(item), self->priv->album_size);
829 gmpc_metaimage_reload_image(GMPC_METAIMAGE(item));
833 entries = g_list_prepend(entries, vbox);
834 j++;
836 v_items++;
837 }while(v_items < (rows*self->priv->supported_columns)&& (iter = iter->next));
839 /* remove list */
840 if(list) g_list_free(list);
841 list = NULL;
843 for(iter = entries = g_list_reverse(entries); iter; iter = g_list_next(iter)){
844 gtk_container_add(GTK_CONTAINER(self->priv->item_table), iter->data);
846 if(entries) g_list_free(entries);
848 gtk_widget_show_all(self->priv->albumview_box);
850 * Remove the timeout
852 if(self->priv->update_timeout)
853 g_source_remove(self->priv->update_timeout);
854 self->priv->update_timeout = 0;
855 return FALSE;
859 void update_view(AlbumViewPlugin *self)
861 if(self->priv->update_timeout != 0) {
862 g_source_remove(self->priv->update_timeout);
864 self->priv->update_timeout = g_timeout_add(10, (GSourceFunc)update_view_real,self);
868 * Gobject plugin
870 static void albumview_plugin_class_init (AlbumViewPluginClass *klass);
872 static int *albumview_plugin_get_version(GmpcPluginBase *plug, int *length)
874 static int version[3] = {PLUGIN_MAJOR_VERSION,PLUGIN_MINOR_VERSION,PLUGIN_MICRO_VERSION};
875 if(length) *length = 3;
876 return (int *)version;
879 static const char *albumview_plugin_get_name(GmpcPluginBase *plug)
881 return ("Album View");
883 static GObject *albumview_plugin_constructor(GType type, guint n_construct_properties, GObjectConstructParam * construct_properties) {
884 AlbumViewPluginClass * klass;
885 AlbumViewPlugin *self;
886 GObjectClass * parent_class;
887 klass = (g_type_class_peek (albumview_plugin_get_type()));
888 parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
889 self = (AlbumViewPlugin *) parent_class->constructor (type, n_construct_properties, construct_properties);
891 g_log(AV_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Constructing plugin");
893 /* setup private structure */
894 self->priv = g_malloc0(sizeof(AlbumViewPluginPrivate));
895 /* set defaults */
896 self->priv->supported_rows = 1;
897 self->priv->supported_columns = 1;
898 self->priv->data = NULL;
899 self->priv->album_size = 200;
900 self->priv->filter_entry = NULL;
901 self->priv->slider_scale = NULL;
902 self->priv->progress_bar = NULL;
903 self->priv->max_entries = 0;
904 self->priv->current_entry = 0;
905 self->priv->current_item = NULL;
906 self->priv->update_timeout = 0;
907 self->priv->complete_list = NULL;
908 self->priv->item_table = NULL;
909 self->priv->albumview_ref = NULL;
910 self->priv->albumview_box = NULL;
911 self->priv->require_scale_update = FALSE;
913 /* Watch status changed signals */
914 g_signal_connect_object(G_OBJECT(gmpcconn), "connection-changed", G_CALLBACK(albumview_connection_changed), self, 0);
916 /* Setup textdomain */
917 bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
918 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
920 GMPC_PLUGIN_BASE(self)->translation_domain = GETTEXT_PACKAGE;
921 GMPC_PLUGIN_BASE(self)->plugin_type = GMPC_PLUGIN_NO_GUI;
923 albumview_plugin_init(self);
925 return G_OBJECT(self);
927 static void albumview_plugin_finalize(GObject *obj) {
928 AlbumViewPlugin *self = (AlbumViewPlugin *)obj;
929 AlbumViewPluginClass * klass = (g_type_class_peek (albumview_plugin_get_type()));
930 gpointer parent_class = g_type_class_peek_parent (klass);
932 g_log(AV_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Destroying plugin");
934 if(self->priv){
935 if(self->priv->current_item) g_list_free(self->priv->current_item);
936 self->priv->current_item = NULL;
937 if(self->priv->complete_list) mpd_data_free(self->priv->complete_list);
938 self->priv->complete_list = NULL;
940 g_free(self->priv);
941 self->priv = NULL;
943 if(parent_class)
944 G_OBJECT_CLASS(parent_class)->finalize(obj);
948 static void albumview_plugin_class_init (AlbumViewPluginClass *klass)
950 G_OBJECT_CLASS(klass)->finalize = albumview_plugin_finalize;
951 G_OBJECT_CLASS(klass)->constructor = albumview_plugin_constructor;
952 /* Connect plugin functions */
953 GMPC_PLUGIN_BASE_CLASS(klass)->get_version = albumview_plugin_get_version;
954 GMPC_PLUGIN_BASE_CLASS(klass)->get_name = albumview_plugin_get_name;
956 GMPC_PLUGIN_BASE_CLASS(klass)->get_enabled = albumview_get_enabled;
957 GMPC_PLUGIN_BASE_CLASS(klass)->set_enabled = albumview_set_enabled;
959 GMPC_PLUGIN_BASE_CLASS(klass)->save_yourself = albumview_browser_save_myself;
962 static void albumview_plugin_browser_iface_init(GmpcPluginBrowserIfaceIface * iface) {
963 iface->browser_add = albumview_add;
964 iface->browser_selected = albumview_selected;
965 iface->browser_unselected = albumview_unselected;
968 const GType albumview_plugin_get_type(void) {
969 static GType albumview_plugin_type_id = 0;
970 if(albumview_plugin_type_id == 0) {
971 static const GTypeInfo info = {
972 .class_size = sizeof(AlbumViewPluginClass),
973 .class_init = (GClassInitFunc)albumview_plugin_class_init,
974 .instance_size = sizeof(AlbumViewPlugin),
975 .n_preallocs = 0
978 albumview_plugin_type_id = g_type_register_static(GMPC_PLUGIN_TYPE_BASE, "AlbumViewPlugin", &info, 0);
980 /** Browser interface */
981 static const GInterfaceInfo iface_info = { (GInterfaceInitFunc) albumview_plugin_browser_iface_init,
982 (GInterfaceFinalizeFunc) NULL, NULL};
983 g_type_add_interface_static (albumview_plugin_type_id, GMPC_PLUGIN_TYPE_BROWSER_IFACE, &iface_info);
985 return albumview_plugin_type_id;
988 G_MODULE_EXPORT GType plugin_get_type(void)
990 return albumview_plugin_get_type();