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 #define ALBUM_SIZE 200
16 static GtkWidget
* create_button(AlbumViewPlugin
*self
, MpdData_real
*complete_list_iter
);
18 const GType
albumview_plugin_get_type(void);
19 #define ALBUM_VIEW_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), albumview_plugin_get_type(), AlbumViewPlugin))
20 #define AV_LOG_DOMAIN "AlbumViewPlugin"
22 typedef struct _AlbumViewPluginPrivate
{
23 int supported_columns
;
25 GtkWidget
*filter_entry
;
26 GtkWidget
*progress_bar
;
27 GtkWidget
*item_table
;
28 GtkWidget
*albumview_box
;
29 GtkWidget
*albumview_main_box
;
34 MpdData
*complete_list
;
39 GtkTreeRowReference
*albumview_ref
;
40 }_AlbumViewPluginPrivate
;
44 static gchar
* albumview_format_time(unsigned long seconds
);
45 static void albumview_browser_save_myself(GmpcPluginBase
*plug
);
46 static void filter_list(GtkEntry
*entry
, gpointer data
);
47 void update_view(AlbumViewPlugin
*self
);
48 static void load_list(AlbumViewPlugin
*self
);
50 void size_changed(GtkWidget
*widget
, GtkAllocation
*alloc
, gpointer user_data
)
52 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(user_data
);
53 int columns
= (alloc
->width
-10)/(ALBUM_SIZE
);
54 int rows
= (alloc
->height
-10)/(ALBUM_SIZE
+40);
56 if(columns
!= self
->priv
->supported_columns
|| rows
!= self
->priv
->supported_rows
)
58 self
->priv
->supported_columns
= (columns
)?columns
:1;
59 self
->priv
->supported_rows
= (rows
)?rows
:1;
60 if(self
->priv
->filter_entry
&& GTK_WIDGET_IS_SENSITIVE(self
->priv
->filter_entry
))
71 static int albumview_get_enabled(GmpcPluginBase
*plug
)
73 return cfg_get_single_value_as_int_with_default(config
, "albumview", "enable", TRUE
);
76 void albumview_set_enabled(GmpcPluginBase
*plug
, int enabled
)
78 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(plug
);
79 cfg_set_single_value_as_int(config
, "albumview", "enable", enabled
);
82 if(self
->priv
->albumview_ref
== NULL
)
84 albumview_add(GMPC_PLUGIN_BROWSER_IFACE(plug
), GTK_WIDGET(playlist3_get_category_tree_view()));
89 GtkTreePath
*path
= gtk_tree_row_reference_get_path(self
->priv
->albumview_ref
);
90 GtkTreeModel
*model
= gtk_tree_row_reference_get_model(self
->priv
->albumview_ref
);
93 if (gtk_tree_model_get_iter(model
, &iter
, path
)){
94 gtk_list_store_remove(GTK_LIST_STORE(model
), &iter
);
96 gtk_tree_path_free(path
);
97 gtk_tree_row_reference_free(self
->priv
->albumview_ref
);
98 self
->priv
->albumview_ref
= NULL
;
104 * Playlist browser functions
106 static void albumview_add(GmpcPluginBrowserIface
*plug
, GtkWidget
*category_tree
)
108 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(plug
);
110 GtkTreeModel
*model
= GTK_TREE_MODEL(playlist3_get_category_tree_store());
114 * don't do anything if we are disabled
116 if(!cfg_get_single_value_as_int_with_default(config
, "albumview", "enable", TRUE
)) return;
118 * Add ourslef to the list
120 pos
= cfg_get_single_value_as_int_with_default(config
, "albumview","position",2);
121 playlist3_insert_browser(&iter
, pos
);
122 gtk_list_store_set(GTK_LIST_STORE(model
), &iter
,
123 PL3_CAT_TYPE
, GMPC_PLUGIN_BASE(plug
)->id
,
124 PL3_CAT_TITLE
,"Album View",
125 PL3_CAT_ICON_ID
, "albumview",
128 * remove odl reference if exists
130 if (self
->priv
->albumview_ref
) {
131 gtk_tree_row_reference_free(self
->priv
->albumview_ref
);
132 self
->priv
->albumview_ref
= NULL
;
135 * create reference to ourself in the list
137 path
= gtk_tree_model_get_path(GTK_TREE_MODEL(model
), &iter
);
139 self
->priv
->albumview_ref
= gtk_tree_row_reference_new(model
, path
);
140 gtk_tree_path_free(path
);
143 static void albumview_browser_save_myself(GmpcPluginBase
*plug
)
145 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(plug
);
146 if(self
->priv
->albumview_ref
)
148 GtkTreePath
*path
= gtk_tree_row_reference_get_path(self
->priv
->albumview_ref
);
151 gint
*indices
= gtk_tree_path_get_indices(path
);
152 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Saving myself to position: %i", indices
[0]);
153 cfg_set_single_value_as_int(config
, "albumview","position",indices
[0]);
154 gtk_tree_path_free(path
);
160 static gboolean
albumview_button_press_event(GtkWidget
*event_box
, GdkEventButton
*event
, gpointer data
)
162 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
163 if(self
->priv
->current_item
== NULL
) return FALSE
;
164 gtk_widget_grab_focus(self
->priv
->event_bg
);
166 static gboolean
albumview_focus(GtkWidget
*wid
,GdkEventFocus
*event
, gpointer data
)
168 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
169 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "focus in");
170 gtk_widget_queue_draw(self
->priv
->event_bg
);
173 static gboolean
albumview_focus_out(GtkWidget
*wid
,GdkEventFocus
*event
, gpointer data
)
175 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
176 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "focus out");
178 gtk_widget_queue_draw(self
->priv
->event_bg
);
181 static gboolean
albumview_expose_event(GtkWidget
*widget
, GdkEventExpose
*event
, gpointer data
)
183 int width
= widget
->allocation
.width
;
184 int height
= widget
->allocation
.height
;
185 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
187 gtk_paint_flat_box(widget
->style
,
195 if(gtk_widget_is_focus(widget
))
197 gtk_paint_focus(widget
->style
, widget
->window
,
206 #if GTK_CHECK_VERSION(2,16,0)
207 static void mod_fill_clear_search_entry(GtkEntry
*entry
, GtkEntryIconPosition icon_pos
, GdkEvent
*event
, gpointer user_data
)
209 if(icon_pos
== GTK_ENTRY_ICON_SECONDARY
){
210 gtk_entry_set_text(GTK_ENTRY(entry
), "");
214 static void albumview_init(AlbumViewPlugin
*self
)
216 /** Get an allready exposed widgets to grab theme colors from. */
217 GtkWidget
*colw
= (GtkWidget
*)playlist3_get_category_tree_view();
218 GtkWidget
*label
= NULL
;
219 GtkWidget
*event
= gtk_scrolled_window_new(NULL
, NULL
);
220 GtkWidget
*hbox
= NULL
; int i
= 0;
221 self
->priv
->event_bg
= gtk_event_box_new();
222 self
->priv
->albumview_main_box
= gtk_vbox_new(FALSE
, 6);
224 GtkWidget
*iv
= self
->priv
->albumview_box
= gtk_vbox_new(FALSE
, 6);
225 g_signal_connect(G_OBJECT(event
), "size-allocate", G_CALLBACK(size_changed
), self
);
227 self
->priv
->filter_entry
= gtk_entry_new();
229 #if GTK_CHECK_VERSION(2,16,0)
230 gtk_entry_set_icon_from_stock(GTK_ENTRY(self
->priv
->filter_entry
), GTK_ENTRY_ICON_SECONDARY
, GTK_STOCK_CLEAR
);
231 g_signal_connect(GTK_ENTRY(self
->priv
->filter_entry
), "icon-press", G_CALLBACK(mod_fill_clear_search_entry
), NULL
);
233 g_signal_connect(G_OBJECT(self
->priv
->filter_entry
),"changed", G_CALLBACK(filter_list
), self
);
236 hbox
= gtk_hbox_new(FALSE
, 6);
237 gtk_box_pack_start(GTK_BOX(hbox
),gtk_label_new(("Filter")), FALSE
, FALSE
, 0);
238 gtk_box_pack_start(GTK_BOX(hbox
),self
->priv
->filter_entry
, TRUE
, TRUE
, 0);
240 gtk_box_pack_end(GTK_BOX(self
->priv
->albumview_main_box
), hbox
, FALSE
, FALSE
, 0);
242 hbox
= gtk_hbox_new(FALSE
, 6);
243 gtk_box_pack_start(GTK_BOX(self
->priv
->albumview_main_box
),hbox
, TRUE
, TRUE
, 0);
244 gtk_box_pack_start(GTK_BOX(hbox
),event
, TRUE
, TRUE
, 0);
245 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(event
), GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
246 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(event
), GTK_SHADOW_ETCHED_IN
);
248 /* TODO draw focus */
249 // gtk_widget_modify_bg(self->priv->event_bg, GTK_STATE_NORMAL,&(self->priv->albumview_main_box->style->white));
250 gtk_widget_set_app_paintable(self
->priv
->event_bg
, TRUE
);
251 g_signal_connect(G_OBJECT(self
->priv
->event_bg
), "expose-event", G_CALLBACK(albumview_expose_event
), self
);
252 gtk_event_box_set_visible_window(GTK_EVENT_BOX(self
->priv
->event_bg
), TRUE
);
254 g_object_set(self
->priv
->event_bg
, "can-focus", TRUE
,NULL
);
255 GTK_WIDGET_SET_FLAGS(self
->priv
->event_bg
, GTK_HAS_FOCUS
);
256 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(event
), self
->priv
->event_bg
);
257 gtk_container_add(GTK_CONTAINER(self
->priv
->event_bg
), iv
);
258 gtk_widget_add_events(self
->priv
->event_bg
, GDK_SCROLL_MASK
|GDK_BUTTON_PRESS_MASK
|GDK_FOCUS_CHANGE_MASK
);
259 g_signal_connect_object(G_OBJECT(self
->priv
->event_bg
), "focus-in-event", G_CALLBACK(albumview_focus
), self
,0);
260 g_signal_connect_object(G_OBJECT(self
->priv
->event_bg
), "focus-out-event", G_CALLBACK(albumview_focus_out
), self
, 0);
261 g_signal_connect_object(G_OBJECT(self
->priv
->event_bg
), "button-press-event", G_CALLBACK(albumview_button_press_event
), self
,0);
263 gtk_widget_show_all(self
->priv
->albumview_main_box
);
266 /* maintain my own reference to the widget, so it won't get destroyed removing
269 g_object_ref_sink(self
->priv
->albumview_main_box
);
273 static void albumview_selected(GmpcPluginBrowserIface
*plug
, GtkWidget
*container
)
275 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(plug
);
276 if(self
->priv
->albumview_main_box
== NULL
) {
277 albumview_init((AlbumViewPlugin
*)plug
);
278 albumview_connection_changed(gmpcconn
, connection
,1,self
);
280 gtk_container_add(GTK_CONTAINER(container
), self
->priv
->albumview_main_box
);
281 gtk_widget_show(self
->priv
->albumview_main_box
);
282 gtk_widget_show(container
);
283 gtk_widget_grab_focus(self
->priv
->event_bg
);
286 static void albumview_unselected(GmpcPluginBrowserIface
*plug
,GtkWidget
*container
)
288 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(plug
);
289 gtk_container_remove(GTK_CONTAINER(container
), self
->priv
->albumview_main_box
);
292 static void status_changed(GmpcConnection
*gmpcconnn
,MpdObj
*mi
, ChangedStatusType type
, AlbumViewPlugin
*self
)
294 if((type
&MPD_CST_DATABASE
) > 0)
297 if(self
->priv
->albumview_main_box
)
302 void albumview_plugin_init(AlbumViewPlugin
*self
)
305 const gchar
* const *paths
= g_get_system_data_dirs();
308 /* First try the compile time path */
309 path
= g_build_filename(PIXMAP_DATA_DIR
, NULL
);
311 if(!g_file_test(path
, G_FILE_TEST_EXISTS
|G_FILE_TEST_IS_DIR
))
318 for(i
=0; path
== NULL
&& paths
&& paths
[i
]; i
++) {
319 path
= g_build_filename(paths
[i
], "gmpc-albumview", "icons", NULL
);
320 if(!g_file_test(path
, G_FILE_TEST_EXISTS
|G_FILE_TEST_IS_DIR
))
327 gtk_icon_theme_append_search_path(gtk_icon_theme_get_default (),path
);
329 g_signal_connect_object(G_OBJECT(gmpcconn
), "status-changed", G_CALLBACK(status_changed
), self
, 0);
334 #define TIMER_SUB(start,stop,diff) diff.tv_usec = stop.tv_usec - start.tv_usec;\
335 diff.tv_sec = stop.tv_sec - start.tv_sec;\
336 if(diff.tv_usec < 0) {\
338 diff.tv_usec += G_USEC_PER_SEC; \
340 static gint
__add_sort(gpointer aa
, gpointer bb
, gpointer data
)
342 MpdData_real
*a
= *(MpdData_real
**)aa
;
343 MpdData_real
*b
= *(MpdData_real
**)bb
;
344 if(!a
|| !b
) return 0;
345 if(a
->type
== MPD_DATA_TYPE_SONG
&& b
->type
== MPD_DATA_TYPE_SONG
)
347 if(a
->song
->artist
&& b
->song
->artist
)
352 sa
= g_utf8_strdown(a
->song
->artist
, -1);
353 sb
= g_utf8_strdown(b
->song
->artist
, -1);
354 val
= g_utf8_collate(sa
,sb
);
358 val = (a == NULL)?((b==NULL)?0:-1):1;
362 if (a
->song
->album
&& b
->song
->album
)
365 sa
= g_utf8_strdown(a
->song
->album
, -1);
366 sb
= g_utf8_strdown(b
->song
->album
, -1);
367 val
= g_utf8_collate(sa
,sb
);
378 static gboolean
update_progressbar(AlbumViewPlugin
*self
)
380 gchar
*temp
= g_strdup_printf("%i of %i albums loaded", self
->priv
->current_entry
, self
->priv
->max_entries
);
381 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(self
->priv
->progress_bar
), self
->priv
->current_entry
/(double)self
->priv
->max_entries
);
382 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(self
->priv
->progress_bar
), temp
);
387 static void load_list_itterate(MpdObj
*mi
, AlbumViewPlugin
*self
)
390 MpdData
*data2
= NULL
;
391 self
->priv
->current_entry
++;
392 if(self
->priv
->max_entries
>0 && self
->priv
->current_entry
%25 == 0){
393 g_idle_add((GSourceFunc
)update_progressbar
, self
);
397 mpd_database_search_field_start(mi
, MPD_TAG_ITEM_ARTIST
);
398 mpd_database_search_add_constraint(mi
, MPD_TAG_ITEM_ALBUM
, (self
->priv
->data
)->tag
);
399 data2
= mpd_database_search_commit(mi
);
402 mpd_Song
*song
= mpd_newSong();
403 song
->album
= g_strdup((self
->priv
->data
)->tag
);
404 song
->artist
= g_strdup(data2
->tag
);
405 if(!mpd_data_is_last(data2
))
407 /* test if server supports album artist */
408 if(mpd_server_tag_supported(mi
, MPD_TAG_ITEM_ALBUM_ARTIST
))
410 mpd_database_search_field_start(mi
, MPD_TAG_ITEM_ALBUM_ARTIST
);
411 mpd_database_search_add_constraint(mi
, MPD_TAG_ITEM_ALBUM
, (self
->priv
->data
)->tag
);
412 MpdData
*data3
= mpd_database_search_commit(mi
);
413 if(mpd_data_is_last(data3
)){
414 if(strlen(data3
->tag
) > 0)
416 song
->albumartist
= g_strdup(data3
->tag
);
417 if(song
->artist
) g_free(song
->artist
);
418 song
->artist
= g_strdup(data3
->tag
);
425 mpd_data_free(data3
);
432 mpd_data_free(data2
);
434 self
->priv
->complete_list
= mpd_new_data_struct_append(self
->priv
->complete_list
);
435 self
->priv
->complete_list
->song
= song
;
436 self
->priv
->complete_list
->type
= MPD_DATA_TYPE_SONG
;
437 self
->priv
->complete_list
->userdata
= create_button(self
, (MpdData_real
*)self
->priv
->complete_list
);
438 self
->priv
->complete_list
->freefunc
= (void *)gtk_widget_destroy
;
443 (self
->priv
->data
) = mpd_data_get_next((self
->priv
->data
));
446 while(self
->priv
->data
!= NULL
);
447 self
->priv
->complete_list
= (MpdData
*)misc_sort_mpddata(mpd_data_get_first(self
->priv
->complete_list
), (GCompareDataFunc
)__add_sort
, NULL
);
450 void update_finished(MpdData
*data
, AlbumViewPlugin
*self
)
452 if(self
->priv
->data
== NULL
){
455 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
,"update view\n");
456 gtk_widget_destroy(self
->priv
->progress_bar
);
457 self
->priv
->progress_bar
= NULL
;
458 for(iter
= (MpdData_real
*)self
->priv
->complete_list
; iter
; iter
= iter
->next
) items
++;
460 gtk_widget_set_sensitive(self
->priv
->filter_entry
, TRUE
);
461 filter_list(GTK_ENTRY(self
->priv
->filter_entry
), self
);
463 gtk_widget_grab_focus(self
->priv
->event_bg
);
467 // g_idle_add((GSourceFunc)load_list_itterate, self);
471 static void load_list(AlbumViewPlugin
*self
)
473 if(self
->priv
->complete_list
)mpd_data_free(self
->priv
->complete_list
);
474 self
->priv
->complete_list
= NULL
;
475 if(self
->priv
->current_item
) g_list_free(self
->priv
->current_item
);
476 self
->priv
->current_item
= NULL
;
479 self
->priv
->progress_bar
= gtk_progress_bar_new();
480 gtk_box_pack_start(GTK_BOX(self
->priv
->albumview_box
), self
->priv
->progress_bar
, FALSE
, FALSE
, 0);
481 gtk_widget_show(self
->priv
->progress_bar
);
482 mpd_database_search_field_start(connection
, MPD_TAG_ITEM_ALBUM
);
483 MpdData
*iter
,*data
= mpd_database_search_commit(connection
);
484 self
->priv
->max_entries
= 0;
485 self
->priv
->current_entry
= 0;
486 gtk_widget_set_sensitive(self
->priv
->filter_entry
, FALSE
);
487 for(iter
= data
; iter
; iter
= mpd_data_get_next_real(iter
, FALSE
)) self
->priv
->max_entries
++;
488 self
->priv
->data
= data
;
489 mpd_async_request(update_finished
, self
, load_list_itterate
, self
);
491 void albumview_connection_changed(GmpcConnection
*conn
, MpdObj
*mi
, int connect
,void *usedata
)
493 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(usedata
);
495 if(connect
&& self
->priv
->albumview_main_box
)
499 else if(self
->priv
->albumview_main_box
){
500 mpd_data_free(self
->priv
->complete_list
);
501 self
->priv
->complete_list
= NULL
;
502 if(self
->priv
->item_table
)
503 gtk_widget_hide(self
->priv
->item_table
);
506 static void album_add(GtkWidget
*button
, mpd_Song
*song
)
508 mpd_database_search_start(connection
,TRUE
);
510 mpd_database_search_add_constraint(connection
, MPD_TAG_ITEM_ALBUM
, song
->album
);
511 if(song
->albumartist
&& strlen(song
->albumartist
) >0){
512 mpd_database_search_add_constraint(connection
, MPD_TAG_ITEM_ALBUM_ARTIST
, song
->albumartist
);
514 mpd_database_search_add_constraint(connection
, MPD_TAG_ITEM_ARTIST
, song
->artist
);
516 MpdData
*data
= mpd_database_search_commit(connection
);
517 /* Sort it before adding */
518 data
= misc_sort_mpddata_by_album_disc_track(data
);
519 for(;data
;data
= mpd_data_get_next(data
)){
520 mpd_playlist_queue_add(connection
, data
->song
->file
);
522 mpd_playlist_queue_commit(connection
);
524 static void album_view(GtkWidget
*button
,mpd_Song
*song
)
526 if (song
&& song
->artist
&& song
->album
) {
528 info2_fill_album_view(song
->artist
, song
->album
);
532 static void album_replace(GtkWidget
*button
, mpd_Song
*song
)
534 mpd_playlist_clear(connection
);
535 album_add(button
, song
);
536 mpd_player_play(connection
);
538 static gboolean
album_button_press(GtkWidget
*image
, GtkMenu
*menu
, mpd_Song
*song
)
542 item
= gtk_image_menu_item_new_with_label("Album information");
543 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
),
544 gtk_image_new_from_stock(GTK_STOCK_INFO
, GTK_ICON_SIZE_MENU
));
545 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
546 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(album_view
), song
);
548 item
= gtk_image_menu_item_new_from_stock(GTK_STOCK_ADD
,NULL
);
549 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
550 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(album_add
), song
);
552 /* replace the replace widget */
553 item
= gtk_image_menu_item_new_with_label("Replace");
554 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
),
555 gtk_image_new_from_stock(GTK_STOCK_REDO
, GTK_ICON_SIZE_MENU
));
556 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
557 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(album_replace
), song
);
559 item
= gtk_separator_menu_item_new();
560 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
562 gtk_widget_show(item
);
566 static GtkWidget
* create_button(AlbumViewPlugin
*self
, MpdData_real
*complete_list_iter
)
570 GtkWidget
*label
= NULL
;
572 /* Wrap it in a vbox */
573 vbox
= gtk_vbox_new(FALSE
, 3);
574 gtk_widget_set_size_request(vbox
, ALBUM_SIZE
,ALBUM_SIZE
+20);
576 item
= gmpc_metaimage_new_size(META_ALBUM_ART
,ALBUM_SIZE
-20);
577 gmpc_metaimage_set_scale_up(GMPC_METAIMAGE(item
), TRUE
);
578 gtk_widget_set_has_tooltip(GTK_WIDGET(item
), FALSE
);
579 gmpc_metaimage_set_squared(GMPC_METAIMAGE(item
), TRUE
);
581 gmpc_metaimage_update_cover_from_song_delayed(GMPC_METAIMAGE(item
), complete_list_iter
->song
);
583 gtk_box_pack_start(GTK_BOX(vbox
), item
, TRUE
, TRUE
, 0);
584 /* Set artist name */
585 if(complete_list_iter
->song
->albumartist
){
586 GtkWidget
*label
= gtk_label_new(complete_list_iter
->song
->albumartist
);
587 gtk_label_set_ellipsize(GTK_LABEL(label
), PANGO_ELLIPSIZE_MIDDLE
);
588 gtk_box_pack_end(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
590 GtkWidget
*label
= gtk_label_new(complete_list_iter
->song
->artist
);
591 gtk_label_set_ellipsize(GTK_LABEL(label
), PANGO_ELLIPSIZE_MIDDLE
);
592 gtk_box_pack_end(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
596 label
= gtk_label_new("");
597 temp
= g_markup_printf_escaped("<b>%s</b>", complete_list_iter
->song
->album
);
598 gtk_label_set_markup(GTK_LABEL(label
), temp
);
600 gtk_label_set_ellipsize(GTK_LABEL(label
), PANGO_ELLIPSIZE_MIDDLE
);
601 gtk_box_pack_end(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
604 /* Attach it to the song */
605 g_object_add_weak_pointer(G_OBJECT(vbox
),&(complete_list_iter
->userdata
));
606 complete_list_iter
->userdata
= vbox
;//g_object_ref_sink(vbox);
607 complete_list_iter
->freefunc
= (void *)gtk_widget_destroy
;
608 g_object_set_data(G_OBJECT(vbox
), "item", item
);
609 g_signal_connect(G_OBJECT(item
), "menu_populate_client", G_CALLBACK(album_button_press
), complete_list_iter
->song
);
611 g_signal_connect(item, "button-press-event",
612 G_CALLBACK(album_button_press), complete_list_iter->song);
614 return g_object_ref_sink(vbox
);
617 static void filter_list(GtkEntry
*entry
, gpointer data
)
619 AlbumViewPlugin
*self
= ALBUM_VIEW_PLUGIN(data
);
620 GRegex
*regex
= NULL
;
623 const gchar
*search_query
= gtk_entry_get_text(GTK_ENTRY(self
->priv
->filter_entry
));
624 MpdData_real
*complete_list_iter
= NULL
;
625 if(search_query
[0] != '\0')
627 gchar
*str
= g_strdup(search_query
);
628 gchar
**test
= g_strsplit(g_strstrip(str
), " ", -1);
630 GString
*s
= g_string_new("((?:");
631 GError
*error
= NULL
;
633 for(i
=0;test
&& test
[i
];i
++){
634 gchar
*temp
= g_regex_escape_string(test
[i
], -1);
635 s
= g_string_append(s
, ".*");
636 s
= g_string_append(s
, temp
);
637 s
= g_string_append(s
, ".*");
638 if(test
[i
+1] != NULL
)
639 s
= g_string_append(s
, "|");
642 g_string_append_printf(s
,"){%i})",i
);
643 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
,"regex: %s\n", s
->str
);
644 regex
= g_regex_new(s
->str
, G_REGEX_CASELESS
|G_REGEX_EXTENDED
, 0,&error
);;
647 g_string_free(s
, TRUE
);
648 for(complete_list_iter
= (MpdData_real
*) mpd_data_get_first(self
->priv
->complete_list
);
650 complete_list_iter
= (MpdData_real
*)mpd_data_get_next_real((MpdData
*)complete_list_iter
, FALSE
))
652 if(g_regex_match(regex
,complete_list_iter
->song
->album
,0,NULL
)||
653 g_regex_match(regex
,complete_list_iter
->song
->artist
,0,NULL
)||
654 (complete_list_iter
->song
->albumartist
&& g_regex_match(regex
,complete_list_iter
->song
->albumartist
,0,NULL
))){
656 list
= g_list_append(list
, complete_list_iter
);
661 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_WARNING
," error creating regex: %s\n", error
->message
);
664 g_regex_unref(regex
);
666 if(self
->priv
->current_item
) g_list_free(self
->priv
->current_item
);
667 self
->priv
->current_item
= g_list_first(list
);
671 static gboolean
update_view_real(AlbumViewPlugin
*self
)
673 MpdData
*complete_list_iter
;
674 const char *search_query
= gtk_entry_get_text(GTK_ENTRY(self
->priv
->filter_entry
));
679 MpdData
*data
= NULL
;
680 GList
*entries
= NULL
;
681 GList
*list
= (self
->priv
->item_table
)?gtk_container_get_children(GTK_CONTAINER(self
->priv
->item_table
)):NULL
;
683 GRegex
*regex
= NULL
;
685 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
,"search query: %s\n", search_query
);
687 if(self
->priv
->item_table
)
688 gtk_widget_hide(self
->priv
->item_table
);
689 for(iter
= g_list_first(list
); iter
; iter
= iter
->next
){
690 GtkWidget
*widget
= iter
->data
;
691 gtk_container_remove(GTK_CONTAINER(self
->priv
->item_table
), widget
);
698 gtk_widget_show(self
->priv
->albumview_box
);
699 if(self
->priv
->current_item
== NULL
){
701 for(complete_list_iter
= mpd_data_get_first(self
->priv
->complete_list
);
703 complete_list_iter
= mpd_data_get_next_real(complete_list_iter
, FALSE
))
706 self
->priv
->current_item
= g_list_append(self
->priv
->current_item
, complete_list_iter
);
708 self
->priv
->current_item
= g_list_first(self
->priv
->current_item
);
711 int rows
= self
->priv
->supported_rows
;
713 * Create holding table if it does not exist
715 if(!self
->priv
->item_table
){
716 GtkWidget
*ali
= gtk_alignment_new(0.0, 0.5, 0,0);
717 self
->priv
->item_table
= exo_wrap_table_new(TRUE
);//gtk_table_new(rows, supported_columns, TRUE);
718 gtk_container_add(GTK_CONTAINER(ali
), self
->priv
->item_table
);
719 gtk_box_pack_start(GTK_BOX(self
->priv
->albumview_box
), ali
, FALSE
, FALSE
, 0);
722 /* I know how large it is going to be.. so lets set the size */
723 gtk_widget_set_size_request(self
->priv
->item_table
, self
->priv
->supported_columns
*(ALBUM_SIZE
)+6,-1);// (rows)*(self->priv->album_size+40));
728 if(self
->priv
->current_item
)//(iter = g_list_first(list)))
730 GList
*iter
= g_list_first(self
->priv
->current_item
);
734 complete_list_iter
= iter
->data
;
735 if(complete_list_iter
->song
/* && (complete_list_iter->song->artist)[0] != '\0'*/)
737 GtkWidget
*vbox
= complete_list_iter
->userdata
;
741 complete_list_iter
->userdata
= vbox
= create_button(self
, (MpdData_real
*)complete_list_iter
);
744 item
= g_object_get_data(G_OBJECT(vbox
), "item");
746 entries
= g_list_prepend(entries
, vbox
);
750 }while(iter
= iter
->next
);//v_items < (rows*self->priv->supported_columns)&& (iter = iter->next));
753 if(list
) g_list_free(list
);
756 for(iter
= entries
= g_list_reverse(entries
); iter
; iter
= g_list_next(iter
)){
757 gtk_container_add(GTK_CONTAINER(self
->priv
->item_table
), iter
->data
);
759 if(entries
) g_list_free(entries
);
761 gtk_widget_show_all(self
->priv
->albumview_box
);
765 if(self
->priv
->update_timeout
)
766 g_source_remove(self
->priv
->update_timeout
);
767 self
->priv
->update_timeout
= 0;
772 void update_view(AlbumViewPlugin
*self
)
774 if(self
->priv
->update_timeout
!= 0) {
775 g_source_remove(self
->priv
->update_timeout
);
777 self
->priv
->update_timeout
= g_timeout_add(10, (GSourceFunc
)update_view_real
,self
);
783 static void albumview_plugin_class_init (AlbumViewPluginClass
*klass
);
785 static int *albumview_plugin_get_version(GmpcPluginBase
*plug
, int *length
)
787 static int version
[3] = {PLUGIN_MAJOR_VERSION
,PLUGIN_MINOR_VERSION
,PLUGIN_MICRO_VERSION
};
788 if(length
) *length
= 3;
789 return (int *)version
;
792 static const char *albumview_plugin_get_name(GmpcPluginBase
*plug
)
794 return ("Album View");
796 static GObject
*albumview_plugin_constructor(GType type
, guint n_construct_properties
, GObjectConstructParam
* construct_properties
) {
797 AlbumViewPluginClass
* klass
;
798 AlbumViewPlugin
*self
;
799 GObjectClass
* parent_class
;
800 klass
= (g_type_class_peek (albumview_plugin_get_type()));
801 parent_class
= G_OBJECT_CLASS (g_type_class_peek_parent (klass
));
802 self
= (AlbumViewPlugin
*) parent_class
->constructor (type
, n_construct_properties
, construct_properties
);
804 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Constructing plugin");
806 /* setup private structure */
807 self
->priv
= g_malloc0(sizeof(AlbumViewPluginPrivate
));
809 self
->priv
->supported_rows
= 1;
810 self
->priv
->supported_columns
= 1;
811 self
->priv
->data
= NULL
;
812 self
->priv
->filter_entry
= NULL
;
813 self
->priv
->progress_bar
= NULL
;
814 self
->priv
->max_entries
= 0;
815 self
->priv
->current_entry
= 0;
816 self
->priv
->current_item
= NULL
;
817 self
->priv
->update_timeout
= 0;
818 self
->priv
->complete_list
= NULL
;
819 self
->priv
->item_table
= NULL
;
820 self
->priv
->albumview_ref
= NULL
;
821 self
->priv
->albumview_box
= NULL
;
823 /* Watch status changed signals */
824 g_signal_connect_object(G_OBJECT(gmpcconn
), "connection-changed", G_CALLBACK(albumview_connection_changed
), self
, 0);
826 /* Setup textdomain */
827 bindtextdomain(GETTEXT_PACKAGE
, PACKAGE_LOCALE_DIR
);
828 bind_textdomain_codeset(GETTEXT_PACKAGE
, "UTF-8");
830 GMPC_PLUGIN_BASE(self
)->translation_domain
= GETTEXT_PACKAGE
;
831 GMPC_PLUGIN_BASE(self
)->plugin_type
= GMPC_PLUGIN_NO_GUI
;
833 albumview_plugin_init(self
);
835 return G_OBJECT(self
);
837 static void albumview_plugin_finalize(GObject
*obj
) {
838 AlbumViewPlugin
*self
= (AlbumViewPlugin
*)obj
;
839 AlbumViewPluginClass
* klass
= (g_type_class_peek (play_queue_plugin_get_type()));
840 gpointer parent_class
= g_type_class_peek_parent (klass
);
842 g_log(AV_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
, "Destroying plugin");
845 if(self
->priv
->current_item
) g_list_free(self
->priv
->current_item
);
846 self
->priv
->current_item
= NULL
;
847 if(self
->priv
->complete_list
) mpd_data_free(self
->priv
->complete_list
);
848 self
->priv
->complete_list
= NULL
;
854 G_OBJECT_CLASS(parent_class
)->finalize(obj
);
858 static void albumview_plugin_class_init (AlbumViewPluginClass
*klass
)
860 G_OBJECT_CLASS(klass
)->finalize
= albumview_plugin_finalize
;
861 G_OBJECT_CLASS(klass
)->constructor
= albumview_plugin_constructor
;
862 /* Connect plugin functions */
863 GMPC_PLUGIN_BASE_CLASS(klass
)->get_version
= albumview_plugin_get_version
;
864 GMPC_PLUGIN_BASE_CLASS(klass
)->get_name
= albumview_plugin_get_name
;
866 GMPC_PLUGIN_BASE_CLASS(klass
)->get_enabled
= albumview_get_enabled
;
867 GMPC_PLUGIN_BASE_CLASS(klass
)->set_enabled
= albumview_set_enabled
;
869 GMPC_PLUGIN_BASE_CLASS(klass
)->save_yourself
= albumview_browser_save_myself
;
872 static void albumview_plugin_browser_iface_init(GmpcPluginBrowserIfaceIface
* iface
) {
873 iface
->browser_add
= albumview_add
;
874 iface
->browser_selected
= albumview_selected
;
875 iface
->browser_unselected
= albumview_unselected
;
878 const GType
albumview_plugin_get_type(void) {
879 static GType albumview_plugin_type_id
= 0;
880 if(albumview_plugin_type_id
== 0) {
881 static const GTypeInfo info
= {
882 .class_size
= sizeof(AlbumViewPluginClass
),
883 .class_init
= (GClassInitFunc
)albumview_plugin_class_init
,
884 .instance_size
= sizeof(AlbumViewPlugin
),
888 albumview_plugin_type_id
= g_type_register_static(GMPC_PLUGIN_TYPE_BASE
, "AlbumViewPlugin", &info
, 0);
890 /** Browser interface */
891 static const GInterfaceInfo iface_info
= { (GInterfaceInitFunc
) albumview_plugin_browser_iface_init
,
892 (GInterfaceFinalizeFunc
) NULL
, NULL
};
893 g_type_add_interface_static (albumview_plugin_type_id
, GMPC_PLUGIN_TYPE_BROWSER_IFACE
, &iface_info
);
895 return albumview_plugin_type_id
;
898 G_MODULE_EXPORT GType
plugin_get_type(void)
900 return albumview_plugin_get_type();