Fix a few small compile warnings
[gmpc-wikipedia.git] / src / plugin.c
blob764ca870661bc663a041a1e564a3ae8c3d1d4f03
1 /* gmpc-wikipedia (GMPC plugin)
2 * Copyright (C) 2006-2009 Qball Cow <qball@sarine.nl>
3 * Project homepage: http://gmpcwiki.sarine.nl/
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include <stdio.h>
21 #include <string.h>
22 #include <config.h>
23 #include <glib.h>
24 #include <glib/gi18n-lib.h>
25 #include <gmpc/plugin.h>
26 #include <gmpc/gmpc_easy_download.h>
29 #ifdef WEBKIT
30 #include <webkit/webkit.h>
31 #endif
32 #ifdef WEBKIT_APT
33 #include <WebKit/webkit.h>
34 #endif
36 #include <locale.h>
38 #include <libxml/parser.h>
39 #include <libxml/tree.h>
41 #include <libsoup/soup.h>
42 static void wp_add(GtkWidget *cat_tree);
43 static void wp_selected(GtkWidget *container);
44 static void wp_unselected(GtkWidget *container);
45 static void wp_changed(GtkWidget *tree, GtkTreeIter *iter);
46 static void wp_status_changed(MpdObj *mi, ChangedStatusType what, void *userdata);
47 static void wp_query_callback(const GEADAsyncHandler *handle, GEADStatus status, gpointer data);
48 static xmlNodePtr get_first_node_by_name(xmlNodePtr xml, gchar *name);
50 static GtkWidget *moz = NULL;
51 static GtkWidget *wp_vbox = NULL;
52 static GtkWidget *pgb = NULL;
54 static void wp_start_init(void);
55 static int wp_get_enabled();
56 static void wp_set_enabled(int enabled);
57 static int global_progress;
59 static gchar *old_artist;
60 static gchar locale[3];
61 static GRegex *page_guess_re;
62 static const char *wikipedia_langs[] = {"ar", "az", "bg", "bn", "ca", "cs", "de", "el", "en", "eo", "es", "fa", "fi", "fr", "gu", "he", "hi", "id", "mr", "it", "ja", "lt", "ms", "mk", "pa", "pl", "pt", "ro", "ru", "sv", "ta", "th", "tr", "uk", "vi", "yo", "zh"};
64 /* Needed plugin_wp stuff */
65 gmpcPlBrowserPlugin wp_gbp = {
66 .add = wp_add,
67 .selected = wp_selected,
68 .unselected = wp_unselected,
70 int plugin_api_version = PLUGIN_API_VERSION;
72 static const gchar *wp_get_translation_domain(void)
74 return GETTEXT_PACKAGE;
77 gmpcPlugin plugin = {
78 .name = N_("WikiPedia"),
79 .version = {PLUGIN_MAJOR_VERSION,PLUGIN_MINOR_VERSION,PLUGIN_MICRO_VERSION},
80 .plugin_type = GMPC_PLUGIN_PL_BROWSER,
81 .init = wp_start_init, /*init */
82 .browser = &wp_gbp,
83 .mpd_status_changed = wp_status_changed, /* status changed */
84 .get_enabled = wp_get_enabled,
85 .set_enabled = wp_set_enabled,
86 .get_translation_domain = wp_get_translation_domain
90 /* Playlist window row reference */
91 static GtkTreeRowReference *wiki_ref = NULL;
92 #define WP_SKINSTR "?useskin=chick"
93 static gchar *current_url = NULL;
95 static void wp_set_url(char *wp)
97 gchar *new_url;
99 new_url = malloc(strlen(wp) + strlen(WP_SKINSTR) + 1);
100 strcpy(new_url, wp);
101 strcat(new_url, WP_SKINSTR);
103 if (current_url)
104 g_free(current_url);
105 current_url = soup_uri_decode(new_url);
107 webkit_web_view_open(WEBKIT_WEB_VIEW(moz), new_url);
108 gtk_widget_show_all(moz);
109 g_free(new_url);
112 static void wp_query_callback(const GEADAsyncHandler *handle, GEADStatus status, gpointer ignored)
114 xmlDocPtr doc;
115 goffset size;
116 xmlNodePtr root, sect, desc, url, c1, query;
117 const gchar *data;
118 gchar *targeturl, *txt;
120 if(status != GEAD_DONE)
121 return;
122 data = gmpc_easy_handler_get_data(handle, &size);
123 doc = xmlParseMemory(data, size);
124 if(!doc)
125 return;
126 root = xmlDocGetRootElement(doc);
127 if(!root)
128 return;
129 sect = get_first_node_by_name(root,"Section");
130 if (!sect)
131 goto out_doc;
133 for(c1 = sect->xmlChildrenNode; c1; c1 = c1->next)
135 desc = get_first_node_by_name(c1, "Text");
136 url = get_first_node_by_name(c1, "Url");
137 if (!desc || !url)
138 continue;
139 txt = xmlNodeListGetString(doc, desc->xmlChildrenNode, 1);
140 if (!txt)
141 continue;
142 if (g_regex_match_full(page_guess_re, txt, strlen(txt), 0, 0, NULL, NULL) &&
143 xmlNodeListGetString(doc, url->xmlChildrenNode, 1))
145 wp_set_url(xmlNodeListGetString(doc, url->xmlChildrenNode, 1));
146 break;
150 /* nothing was found by regex, use first entry */
151 if (!c1) {
152 c1 = sect->xmlChildrenNode;
153 if (c1) {
154 url = get_first_node_by_name(c1, "Url");
155 if (url && xmlNodeListGetString(doc, url->xmlChildrenNode, 1))
156 wp_set_url(xmlNodeListGetString(doc,url->xmlChildrenNode,1));
157 } else {
158 /* nothing is found, if we are localized, grab our search string back and do some magic */
159 query = get_first_node_by_name(root, "Query");
160 if (!query)
161 goto out_doc;
162 txt = xmlNodeListGetString(doc, query->xmlChildrenNode, 1);
163 if (!txt)
164 goto out_doc;
166 /* fist try english wikipedia, it's the biggest after all */
167 const gchar *oldurl = gmpc_easy_handler_get_uri(handle);
168 if (!g_str_has_prefix(oldurl, "http://en.")) {
169 gchar *newurl = g_strdup_printf("http://en.wikipedia.org/w/api.php?action=opensearch&search=%s&format=xml", txt);
170 debug_printf(DEBUG_INFO, "Trying to fetch: %s\n", newurl);
171 gmpc_easy_async_downloader(newurl, wp_query_callback, NULL);
172 g_free(newurl);
173 goto out_doc;
175 /* nothing is found, display localized wikipedia
176 * "unknown article" page. Not loading anything is
177 * confusing */
178 targeturl = g_strdup_printf("http://%s.wikipedia.org/wiki/%s", locale, txt);
179 wp_set_url(targeturl);
180 g_free(targeturl);
183 out_doc:
184 xmlFreeDoc(doc);
187 /* We need to remove &, ? and /, the rest goes to the webkit widget */
188 static gchar *wp_clean_for_url(const gchar *str)
190 static GRegex *re;
191 gchar *nstr;
192 GError *error = NULL;
194 if (!re) {
195 re = g_regex_new("[&/\\?]", G_REGEX_MULTILINE, 0, &error);
196 if (error) {
197 debug_printf(DEBUG_INFO, "Build regexp %s\n", error->message);
198 g_error_free(error);
199 return NULL;
203 nstr = g_regex_replace(re, str, strlen(str), 0, "", 0, &error);
204 if (error) {
205 debug_printf(DEBUG_INFO, "regexp replace %s\n", error->message);
206 g_error_free(error);
207 return NULL;
209 return nstr;
212 void wp_changed(GtkWidget *tree, GtkTreeIter *iter)
214 mpd_Song *song = mpd_playlist_get_current_song(connection);
215 gchar *artist = NULL;
217 if(!song)
218 return;
220 /* copied from metadata.h */
221 if(song->artist) {
222 if (cfg_get_single_value_as_int_with_default(config, "metadata", "rename", FALSE))
224 gchar **str = g_strsplit(song->artist, ",", 2);
225 if(str[1])
226 artist = g_strdup_printf("%s %s", g_strstrip(str[1]), g_strstrip(str[0]));
227 else
228 artist = g_strdup(song->artist);
229 g_strfreev(str);
230 debug_printf(DEBUG_INFO, "string converted to: '%s'", artist);
231 } else {
232 artist = g_strdup(song->artist);
236 if(artist == NULL)
238 if (strcmp(old_artist, "NONE") != 0)
240 if (current_url)
241 g_free(current_url);
242 current_url = g_strdup("http://www.musicpd.org/");
243 webkit_web_view_open(WEBKIT_WEB_VIEW(moz), current_url);
244 old_artist = g_strdup("NONE");
246 return;
249 gchar *esc_artist = wp_clean_for_url(artist);
251 if (strcmp(old_artist, esc_artist) != 0)
253 gchar *url = g_strdup_printf("http://%s.wikipedia.org/w/api.php?action=opensearch&search=%s&format=xml", locale, esc_artist);
254 debug_printf(DEBUG_INFO, "Trying to fetch: %s\n", url);
255 gmpc_easy_async_downloader(url, wp_query_callback, NULL);
256 g_free(url);
259 old_artist = g_strdup(esc_artist);
260 g_free(esc_artist);
261 g_free(artist);
264 void wp_progress(GtkWidget *mozem, int progress, gpointer data)
266 float fprog = (float)progress/100.0;
267 gchar *text = g_strdup_printf("%d %%",progress);
268 global_progress=progress;
269 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(pgb),fprog);
270 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(pgb),text);
271 g_free(text);
274 void wp_progress_started(WebKitWebView *webkitwebview,WebKitWebFrame *arg1,gpointer user_data)
276 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(pgb),0);
277 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(pgb),"0 %");
278 gtk_widget_show(pgb);
281 void wp_progress_finished(WebKitWebView *webkitwebview,WebKitWebFrame
282 *arg1,gpointer user_data)
284 gtk_widget_hide(pgb);
287 /* open urls in external window. You don't want to navigate without a back
288 * button nor url bar.
289 * No clean way to handle this, so use global cur_url
291 static WebKitNavigationResponse wp_navigation_requested(WebKitWebView *web_view,
292 WebKitWebFrame *frame, WebKitNetworkRequest *request,
293 gpointer user_data)
295 const gchar *uri;
296 gchar *decoded_uri;
297 GError *error = NULL;
298 GdkScreen *screen;
300 uri = webkit_network_request_get_uri(request);
301 decoded_uri = soup_uri_decode(uri);
302 if (g_str_has_prefix(decoded_uri, current_url)) {
303 debug_printf(DEBUG_INFO, "Accepting %s\n", uri);
304 g_free(decoded_uri);
305 return WEBKIT_NAVIGATION_RESPONSE_ACCEPT;
307 g_free(decoded_uri);
309 debug_printf(DEBUG_INFO, "%s != %s\n", uri, current_url);
311 screen = gtk_widget_get_screen(GTK_WIDGET(web_view));
312 if (!screen)
313 screen = gdk_screen_get_default ();
315 gtk_show_uri(screen, uri, gtk_get_current_event_time(), &error);
316 if (error) {
317 debug_printf(DEBUG_INFO, "gtk_show_uri %s\n", error->message);
318 g_error_free(error);
320 return WEBKIT_NAVIGATION_RESPONSE_IGNORE;
323 void wp_init()
325 GError *error = NULL;
326 GtkWidget *sw =gtk_scrolled_window_new(NULL,NULL);
327 char *l;
329 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN);
330 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
331 wp_vbox = gtk_vbox_new(FALSE, 6);
332 moz = webkit_web_view_new();//moz_new();
333 if(moz == NULL)
336 webkit_web_view_can_go_back_or_forward(WEBKIT_WEB_VIEW(moz), 0);
338 gtk_container_add(GTK_CONTAINER(sw), moz);
339 gtk_box_pack_start_defaults(GTK_BOX(wp_vbox), sw);
340 pgb = gtk_progress_bar_new();
341 gtk_box_pack_start(GTK_BOX(wp_vbox), pgb, FALSE, TRUE, 0);
342 gtk_widget_grab_focus (GTK_WIDGET (moz));
343 g_signal_connect(moz, "load_progress_changed", G_CALLBACK(wp_progress), NULL);
344 g_signal_connect(moz, "load_finished", G_CALLBACK(wp_progress_finished), NULL);
345 g_signal_connect(moz, "load_started", G_CALLBACK(wp_progress_started), NULL);
346 g_signal_connect(moz, "navigation-requested", G_CALLBACK(wp_navigation_requested), NULL);
347 gtk_widget_show_all(wp_vbox);
348 gtk_widget_hide(pgb);
349 g_object_ref(G_OBJECT(wp_vbox));
350 old_artist = g_strdup("NONE");
351 /* we should add inernationalized artist|band|musician|singer|rapper|group in the regex */
352 page_guess_re = g_regex_new("\\(.*(artist|band|musician|singer|rapper|group).*\\)", G_REGEX_CASELESS, 0, &error);
354 strcpy(locale,"en");
355 /*find locale */
356 l = setlocale(LC_ALL,"");
357 if(l!=NULL)
359 int i;
360 for (i = 0; i < sizeof(wikipedia_langs) / sizeof(char *); i++) {
361 if (strncmp(l, wikipedia_langs[i], 2) == 0) {
362 strncpy(locale,l,2);
363 locale[2] = 0;
364 break;
370 void wp_add(GtkWidget *cat_tree)
372 GtkTreePath *path = NULL;
373 GtkListStore *pl3_tree = (GtkListStore *)gtk_tree_view_get_model(GTK_TREE_VIEW(cat_tree));
374 GtkTreeIter iter;
375 if(!cfg_get_single_value_as_int_with_default(config, "wp-plugin", "enable", 0)) return;
376 gtk_list_store_append(pl3_tree, &iter);
377 gtk_list_store_set(pl3_tree, &iter,
378 PL3_CAT_TYPE, plugin.id,
379 PL3_CAT_TITLE, _("Wikipedia Lookup"),
380 PL3_CAT_ICON_ID, "wikipedia",
381 -1);
382 if (wiki_ref)
384 gtk_tree_row_reference_free(wiki_ref);
385 wiki_ref = NULL;
388 path = gtk_tree_model_get_path(GTK_TREE_MODEL(playlist3_get_category_tree_store()), &iter);
389 if (path)
391 wiki_ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(playlist3_get_category_tree_store()), path);
392 gtk_tree_path_free(path);
397 void wp_selected(GtkWidget *container)
400 if(wp_vbox== NULL)
402 wp_init();
405 gtk_container_add(GTK_CONTAINER(container), wp_vbox);
406 gtk_widget_show_all(wp_vbox);
407 if(global_progress==100)
409 gtk_widget_hide(pgb);
413 void wp_unselected(GtkWidget *container)
415 gtk_widget_hide(moz);
416 gtk_container_remove(GTK_CONTAINER(container),wp_vbox);
420 void wp_set_enabled(int enabled)
422 cfg_set_single_value_as_int(config, "wp-plugin", "enable", enabled);
423 if (enabled)
425 if(wiki_ref == NULL)
427 wp_add(GTK_WIDGET(playlist3_get_category_tree_view()));
430 else if (wiki_ref)
432 GtkTreePath *path = gtk_tree_row_reference_get_path(wiki_ref);
433 if (path){
434 GtkTreeIter iter;
435 if (gtk_tree_model_get_iter(GTK_TREE_MODEL(playlist3_get_category_tree_store()), &iter, path)){
436 gtk_list_store_remove(playlist3_get_category_tree_store(), &iter);
438 gtk_tree_path_free(path);
439 gtk_tree_row_reference_free(wiki_ref);
440 wiki_ref = NULL;
445 int wp_get_enabled()
447 return cfg_get_single_value_as_int_with_default(config, "wp-plugin", "enable", 0);
449 static void wp_start_init(void)
451 gchar *path = gmpc_plugin_get_data_path(&plugin);
452 gchar *url = g_build_path(G_DIR_SEPARATOR_S,path, "wikipedia", NULL);
453 gtk_icon_theme_append_search_path(gtk_icon_theme_get_default (),url);
454 g_free(path);
455 g_free(url);
457 bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
458 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
462 static void wp_status_changed(MpdObj *mi, ChangedStatusType what, void *userdata)
464 if(pl3_cat_get_selected_browser() == plugin.id)
466 wp_changed(NULL, NULL);
470 static xmlNodePtr get_first_node_by_name(xmlNodePtr xml, gchar *name) {
471 if(name == NULL) return NULL;
472 if(xml) {
473 xmlNodePtr c = xml->xmlChildrenNode;
474 for(;c;c=c->next) {
475 if(c->name && xmlStrEqual(c->name, (xmlChar *) name))
476 return c;
479 return NULL;