3 #include <glade/glade.h>
4 #include <libmpd/libmpd.h>
5 #include <libmpd/debug_printf.h>
6 #include <gmpc/plugin.h>
8 GladeXML
*apl_xml
= NULL
;
9 int e_util_utf8_strstrcase (const gchar
*haystack
, const gchar
*needle
);
10 int apl_right_mouse_menu(GtkWidget
*menu
, int type
, GtkWidget
*tree
, GdkEventButton
*event
);
13 int apl_get_enabled();
14 void apl_set_enabled(int enabled
);
17 void pl3_option_menu_activate();
19 gmpcPlBrowserPlugin apl_gpb
= {
22 NULL
, /** Unselected */
23 NULL
, /** slection changed */
25 apl_right_mouse_menu
, /* right mouse menu */
26 NULL
, /** cat key press */
27 NULL
, /** add go menu */
28 NULL
/** key press event */
30 int plugin_api_version
= PLUGIN_API_VERSION
;
32 "Automatic Playlist Creation",
34 GMPC_PLUGIN_PL_BROWSER
,
36 NULL
, /* path to plugin */
37 NULL
, /* initialization */
39 &apl_gpb
, /* browser intergration */
40 NULL
, /* status changed */
41 NULL
, /* connection changed */
42 NULL
, /* preferences */
50 return cfg_get_single_value_as_int_with_default(config
, "autoplaylist", "enable", TRUE
);
52 void apl_set_enabled(int enabled
)
54 cfg_set_single_value_as_int(config
, "autoplaylist", "enable", enabled
);
55 pl3_option_menu_activate();
58 static void apl_close(GtkWidget
*dialog
, GladeXML
*apl_xml
)
60 GtkWidget
*tree
= glade_xml_get_widget(apl_xml
, "result_tree");
61 GtkListStore
*model
= (GtkListStore
*)gtk_tree_view_get_model(GTK_TREE_VIEW(tree
));
62 gtk_widget_destroy(GTK_WIDGET(dialog
));
63 g_object_unref(model
);
64 g_object_unref(apl_xml
);
67 static int apl_data_matched(MpdData
*data
, int field
, int rule
, char *match
)
71 /* get the correct field */
72 if(field
== MPD_TAG_ITEM_ARTIST
&& data
->song
->artist
) key
= data
->song
->artist
;
73 else if(field
== MPD_TAG_ITEM_ALBUM
&& data
->song
->album
) key
= data
->song
->album
;
74 else if(field
== MPD_TAG_ITEM_TITLE
&& data
->song
->title
) key
= data
->song
->title
;
75 else if(field
== MPD_TAG_ITEM_TRACK
&& data
->song
->track
) key
= data
->song
->track
;
76 else if(field
== MPD_TAG_ITEM_NAME
&& data
->song
->name
) key
= data
->song
->name
;
77 else if(field
== MPD_TAG_ITEM_GENRE
&& data
->song
->genre
) key
= data
->song
->genre
;
78 else if(field
== MPD_TAG_ITEM_DATE
&& data
->song
->date
) key
= data
->song
->date
;
79 else if(field
== MPD_TAG_ITEM_COMPOSER
&& data
->song
->composer
) key
= data
->song
->composer
;
80 else key
= data
->song
->file
;
85 if(rule
== 0) matched
= e_util_utf8_strstrcase (key
,match
);
86 else if(rule
== 1) matched
= !e_util_utf8_strstrcase (key
, match
);
87 else matched
= g_utf8_collate(match
,key
)?FALSE
:TRUE
;
93 static MpdData
* apl_data_filter(MpdData
*data
, int field
, int rule
, char *match
)
97 if(apl_data_matched(data
, field
, rule
, match
) == 0)
100 data
= mpd_data_delete_item(data
);
103 if(!mpd_data_is_last(data
))
105 data
= mpd_data_get_next(data
);
109 return mpd_data_get_first(data
);
116 static void apl_data_filter_itemwise(MpdData
**total_list
, int field
, int rule
, char *match
)
118 GtkWidget
*tree
= glade_xml_get_widget(apl_xml
, "result_tree");
119 GtkListStore
*model
=(GtkListStore
*) gtk_tree_view_get_model(GTK_TREE_VIEW(tree
));
122 if(apl_data_matched(*total_list
, field
, rule
, match
))
125 // mpd_playlist_queue_add(connection, (*total_list)->song->file);
126 gtk_list_store_append(model
, &iter
);
127 gtk_list_store_set(model
, &iter
,
128 0, (*total_list
)->song
->title
,
129 1,(*total_list
)->song
->artist
,
130 2,(*total_list
)->song
->album
,
131 3,(*total_list
)->song
->file
,-1);
132 *total_list
= mpd_data_delete_item(*total_list
);
136 if(!mpd_data_is_last(*total_list
))
138 *total_list
= mpd_data_get_next(*total_list
);
142 *total_list
= mpd_data_get_first(*total_list
);
147 *total_list
= mpd_data_get_first(*total_list
);
150 static void apl_search()
152 GtkWidget
*tree
= glade_xml_get_widget(apl_xml
, "result_tree");
153 GtkListStore
*rs_store
=(GtkListStore
*) gtk_tree_view_get_model(GTK_TREE_VIEW(tree
));
155 GtkTreeModel
*model
= gtk_tree_view_get_model
156 (GTK_TREE_VIEW (glade_xml_get_widget(apl_xml
, "apl_tree")));
157 int match_type
= gtk_toggle_button_get_active
158 (GTK_TOGGLE_BUTTON(glade_xml_get_widget(apl_xml
, "ck_any_item")));
159 MpdData
*data
= mpd_database_get_complete(connection
);
160 /** clear previous results */
161 gtk_list_store_clear(rs_store
);
162 /* if(!gtk_toggle_button_get_active
163 (GTK_TOGGLE_BUTTON(glade_xml_get_widget(apl_xml, "ck_append_playlist"))))
165 mpd_playlist_clear(connection);
170 if(gtk_tree_model_get_iter_first(model
, &iter
))
174 gchar
*cfield
, *crule
, *match
;
175 gtk_tree_model_get(model
, &iter
,
181 field
= mpd_misc_get_tag_by_name (cfield
);
182 if(!strcmp(crule
, "Contains")) rule
= 0;
183 else if (!strcmp(crule
, "Does not contain")) rule
= 1;
187 apl_data_filter_itemwise(&data
, field
, rule
, match
);
191 data
= apl_data_filter(data
, field
, rule
, match
);
196 }while(gtk_tree_model_iter_next(model
, &iter
));
203 for(;data
; data
= mpd_data_get_next(data
))
205 // mpd_playlist_queue_add(connection, data->song->file);
207 gtk_list_store_append(rs_store
,&iter
);
208 gtk_list_store_set(rs_store
, &iter
,
209 0, data
->song
->title
,
210 1, data
->song
->artist
,
211 2, data
->song
->album
,
212 3, data
->song
->file
,-1);
215 if(gtk_tree_model_iter_n_children(GTK_TREE_MODEL(rs_store
), NULL
) >0)
217 gtk_widget_set_sensitive(glade_xml_get_widget(apl_xml
, "okbutton1"), TRUE
);
220 gtk_widget_set_sensitive(glade_xml_get_widget(apl_xml
, "okbutton1"), FALSE
);
224 static void apl_response(GtkWidget
*dialog
, gint response
, GladeXML
*apl_xml
)
226 if(response
== GTK_RESPONSE_OK
)
229 GtkWidget
*tree
= glade_xml_get_widget(apl_xml
, "result_tree");
230 GtkListStore
*rs_store
=(GtkListStore
*)gtk_tree_view_get_model(GTK_TREE_VIEW(tree
));
232 if(!gtk_toggle_button_get_active
233 (GTK_TOGGLE_BUTTON(glade_xml_get_widget(apl_xml
, "ck_append_playlist"))))
235 mpd_playlist_clear(connection
);
237 if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(rs_store
), &iter
))
241 gtk_tree_model_get(GTK_TREE_MODEL(rs_store
), &iter
, 3, &path
, -1);
242 mpd_playlist_queue_add(connection
, path
);
244 }while(gtk_tree_model_iter_next(GTK_TREE_MODEL(rs_store
), &iter
));
245 mpd_playlist_queue_commit(connection
);
250 apl_close(dialog
, apl_xml
);
254 static void field_combo_edited_cb (GtkCellRendererText
*cell
,
260 if(gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(list
), &iter
, path
))
262 gtk_list_store_set(list
, &iter
,
268 static void rule_combo_edited_cb (GtkCellRendererText
*cell
,
274 if(gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(list
), &iter
, path
))
276 gtk_list_store_set(list
, &iter
, 1,value
, -1);
279 static void text_edited_cb (GtkCellRendererText
*cell
,
285 if(gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(list
), &iter
, path
))
287 gtk_list_store_set(list
, &iter
, 2,value
, -1);
290 static void apl_add_row(GtkWidget
*button
, GladeXML
*apl_xml
)
293 GtkListStore
*lstore
= (GtkListStore
*)gtk_tree_view_get_model(
294 GTK_TREE_VIEW (glade_xml_get_widget(apl_xml
, "apl_tree")));
295 gtk_list_store_append(lstore
, &iter
);
296 gtk_list_store_set(lstore
, &iter
,
304 static void apl_remove_row(GtkWidget
*button
, GladeXML
*apl_xml
)
307 GtkTreeView
*tree
= GTK_TREE_VIEW (glade_xml_get_widget(apl_xml
, "apl_tree"));
308 GtkTreeModel
*lstore
= gtk_tree_view_get_model(tree
);
310 if(gtk_tree_selection_get_selected(gtk_tree_view_get_selection(tree
),&lstore
,&iter
))
312 gtk_list_store_remove(GTK_LIST_STORE(lstore
), &iter
);
315 static void apl_start()
319 GtkCellRenderer
*renderer
;
321 GtkTreeViewColumn
*column
;
322 GtkWidget
*tree
= NULL
;
323 GtkListStore
*lstore
= NULL
, *tmp_store
= NULL
;
324 gchar
*path
= g_strdup_printf("%s/apl/apl.glade", plugin
.path
);
325 apl_xml
= glade_xml_new(path
, "apl_win", NULL
);
329 debug_printf(DEBUG_ERROR
, "apl_start: Cannot find: %s/apl/apl.glade", plugin
.path
);
332 debug_printf(DEBUG_INFO
, "apl_start: Starting");
333 lstore
= gtk_list_store_new(4, G_TYPE_STRING
,G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_BOOLEAN
);
334 gtk_tree_view_set_model(GTK_TREE_VIEW (glade_xml_get_widget(apl_xml
, "apl_tree")), GTK_TREE_MODEL(lstore
));
335 g_object_unref(lstore
);
338 renderer
= gtk_cell_renderer_combo_new();
339 column
= gtk_tree_view_column_new();
340 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
341 gtk_tree_view_column_set_attributes(column
, renderer
, "text", 0,NULL
);
342 gtk_tree_view_append_column (GTK_TREE_VIEW (glade_xml_get_widget(apl_xml
, "apl_tree")), column
);
343 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_AUTOSIZE
);
344 tmp_store
= gtk_list_store_new(1, G_TYPE_STRING
);
346 if(mpd_server_check_version(connection
,0,12,0))
348 max
= MPD_TAG_NUM_OF_ITEM_TYPES
;
352 if(i
!= MPD_TAG_ITEM_COMMENT
) /* I Don't want COMMENT */
354 gtk_list_store_append(tmp_store
, &iter
);
355 gtk_list_store_set(tmp_store
,&iter
, 0, mpdTagItemKeys
[i
],-1);
358 g_object_set(G_OBJECT(renderer
), "model", tmp_store
, NULL
);
359 g_object_set(G_OBJECT(renderer
), "text-column", 0, NULL
);
360 g_object_set(G_OBJECT(renderer
), "has-entry", FALSE
, NULL
);
361 g_object_set(G_OBJECT(renderer
), "editable", TRUE
, NULL
);
362 g_signal_connect(G_OBJECT(renderer
), "edited", G_CALLBACK(field_combo_edited_cb
), lstore
);
366 renderer
= gtk_cell_renderer_combo_new();
367 column
= gtk_tree_view_column_new();
368 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
369 gtk_tree_view_column_set_attributes(column
, renderer
, "text", 1,NULL
);
370 gtk_tree_view_append_column (GTK_TREE_VIEW (glade_xml_get_widget(apl_xml
, "apl_tree")), column
);
371 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_AUTOSIZE
);
372 tmp_store
= gtk_list_store_new(1, G_TYPE_STRING
);
373 gtk_list_store_append(tmp_store
, &iter
);
374 gtk_list_store_set(tmp_store
, &iter
, 0, "Contains",-1);
375 gtk_list_store_append(tmp_store
, &iter
);
376 gtk_list_store_set(tmp_store
, &iter
, 0, "Does not contain",-1);
377 gtk_list_store_append(tmp_store
, &iter
);
378 gtk_list_store_set(tmp_store
, &iter
, 0, "Equals",-1);
379 g_object_set(G_OBJECT(renderer
), "model", tmp_store
, NULL
);
380 g_object_set(G_OBJECT(renderer
), "text-column", 0, NULL
);
381 g_object_set(G_OBJECT(renderer
), "has-entry", FALSE
, NULL
);
382 g_object_set(G_OBJECT(renderer
), "editable", TRUE
, NULL
);
383 g_signal_connect(G_OBJECT(renderer
), "edited", G_CALLBACK(rule_combo_edited_cb
), lstore
);
387 renderer
= gtk_cell_renderer_text_new();
388 column
= gtk_tree_view_column_new();
389 gtk_tree_view_column_pack_start(column
, renderer
, TRUE
);
390 gtk_tree_view_column_set_attributes(column
, renderer
, "text", 2,"editable", 3,NULL
);
391 gtk_tree_view_append_column (GTK_TREE_VIEW (glade_xml_get_widget(apl_xml
, "apl_tree")), column
);
392 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_AUTOSIZE
);
393 g_signal_connect(G_OBJECT(renderer
), "edited", G_CALLBACK(text_edited_cb
), lstore
);
396 gtk_list_store_append(lstore
, &iter
);
397 gtk_list_store_set(lstore
, &iter
,
406 tree
= glade_xml_get_widget(apl_xml
, "result_tree");
407 lstore
= gtk_list_store_new(4, G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_STRING
);
408 gtk_tree_view_set_model(GTK_TREE_VIEW(tree
), GTK_TREE_MODEL(lstore
));
409 renderer
= gtk_cell_renderer_text_new();
410 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree
),-1,"Title", renderer
,
412 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree
),-1,"Artist", renderer
,
414 renderer
= gtk_cell_renderer_text_new();
415 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(tree
),-1,"Album", renderer
,
427 glade_xml_signal_connect_data (apl_xml
, "apl_search",
428 G_CALLBACK(apl_search
), apl_xml
);
431 glade_xml_signal_connect_data (apl_xml
, "on_apl_win_close",
432 G_CALLBACK(apl_close
), apl_xml
);
433 glade_xml_signal_connect_data (apl_xml
, "on_apl_win_response",
434 G_CALLBACK(apl_response
), apl_xml
);
435 glade_xml_signal_connect_data (apl_xml
, "on_apl_add_clicked",
436 G_CALLBACK(apl_add_row
), apl_xml
);
437 glade_xml_signal_connect_data (apl_xml
, "on_apl_remove_clicked",
438 G_CALLBACK(apl_remove_row
), apl_xml
);
443 int apl_right_mouse_menu(GtkWidget
*menu
, int type
, GtkWidget
*tree
, GdkEventButton
*event
)
445 gmpcPlugin
*plug
= plugin_get_from_id(type
);
446 if(!cfg_get_single_value_as_int_with_default(config
, "autoplaylist", "enable", TRUE
)) {
449 debug_printf(DEBUG_INFO
,"Automatic playlist right mouse clicked");
450 if(!strcmp(plug
->name
, "Current Playlist Browser") && mpd_server_check_version(connection
, 0,12,0))
453 debug_printf(DEBUG_INFO
,"Automatic playlist right mouse for me");
454 item
= gtk_image_menu_item_new_with_label("Generate Playlist");
455 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item
),
456 gtk_image_new_from_stock(GTK_STOCK_ADD
, GTK_ICON_SIZE_MENU
));
457 gtk_menu_shell_append(GTK_MENU_SHELL(menu
), item
);
458 g_signal_connect(G_OBJECT(item
), "activate", G_CALLBACK(apl_start
), NULL
);
465 * e_util_unicode_get_utf8:
466 * @text: The string to take the UTF-8 character from.
467 * @out: The location to store the UTF-8 character in.
469 * Get a UTF-8 character from the beginning of @text.
471 * Returns: A pointer to the next character in @text after @out.
473 static guchar
* e_util_unicode_get_utf8 (const gchar
*text
, gunichar
*out
)
475 *out
= g_utf8_get_char (text
);
476 return (guchar
*)((*out
== (gunichar
)-1) ? NULL
: g_utf8_next_char (text
));
481 * e_util_utf8_strstrcase:
482 * @haystack: The string to search in.
483 * @needle: The string to search for.
485 * Find the first instance of @needle in @haystack, ignoring case. (No
486 * proper case folding or decomposing is done.) Both @needle and
487 * @haystack are UTF-8 strings.
489 * Returns: A pointer to the first instance of @needle in @haystack, or
490 * %NULL if no match is found, or if either of the strings are
491 * not legal UTF-8 strings.
493 int e_util_utf8_strstrcase (const gchar
*haystack
, const gchar
*needle
)
500 if (haystack
== NULL
) return FALSE
;
501 if (needle
== NULL
) return FALSE
;
502 if (strlen (needle
) == 0) return FALSE
;
503 if (strlen (haystack
) == 0) return FALSE
;
505 nuni
= g_alloca (sizeof (gunichar
) * strlen (needle
));
508 for (p
= e_util_unicode_get_utf8 (needle
, &unival
);
510 p
= e_util_unicode_get_utf8 ((const gchar
*)p
, &unival
))
512 nuni
[nlen
++] = g_unichar_tolower (unival
);
514 /* NULL means there was illegal utf-8 sequence */
521 o
= (const guchar
*)haystack
;
522 for (p
= e_util_unicode_get_utf8 ((const gchar
*)o
, &unival
);
524 p
= e_util_unicode_get_utf8 ((const gchar
*)p
, &unival
))
527 sc
= g_unichar_tolower (unival
);
528 /* We have valid stripped char */
532 while (npos
< nlen
) {
533 q
= e_util_unicode_get_utf8 ((const gchar
*)q
, &unival
);
534 if (!q
|| !unival
) return FALSE
;
535 sc
= g_unichar_tolower (unival
);
536 if (sc
!= nuni
[npos
]) break;