2006-12-12 James Livingston <doclivingston@gmail.com>
[rhythmbox.git] / plugins / rb-plugins-engine.c
blob5c89e51e416ba0ff1363b42dcd81bcfcce00f440
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
3 * Plugin manager for Rhythmbox, based heavily on the code from gedit.
5 * Copyright (C) 2002-2005 Paolo Maggi
6 * 2006 James Livingston <jrl@ids.org.au>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor,
21 * Boston, MA 02110-1301 USA.
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
28 #include <string.h>
30 #include <glib/gi18n.h>
31 #include <glib/gkeyfile.h>
33 #include "eel-gconf-extensions.h"
34 #include "rb-file-helpers.h"
35 #include "rb-preferences.h"
36 #include "rb-util.h"
37 #include "rb-plugin.h"
38 #include "rb-debug.h"
39 #include "rb-dialog.h"
41 #include "rb-module.h"
42 #ifdef ENABLE_PYTHON
43 #include "rb-python-module.h"
44 #endif
46 #include "rb-plugins-engine.h"
48 #define PLUGIN_EXT ".rb-plugin"
50 typedef enum
52 RB_PLUGIN_LOADER_C,
53 RB_PLUGIN_LOADER_PY,
54 } RBPluginLang;
56 struct _RBPluginInfo
58 gchar *file;
60 gchar *location;
61 RBPluginLang lang;
62 GTypeModule *module;
64 gchar *name;
65 gchar *desc;
66 gchar **authors;
67 gchar *copyright;
68 gchar *website;
70 gchar *icon_name;
71 GdkPixbuf *icon_pixbuf;
73 RBPlugin *plugin;
75 gboolean active;
76 gboolean visible;
77 guint active_notification_id;
78 guint visible_notification_id;
81 static void rb_plugin_info_free (RBPluginInfo *info);
82 static void rb_plugins_engine_plugin_active_cb (GConfClient *client,
83 guint cnxn_id,
84 GConfEntry *entry,
85 RBPluginInfo *info);
86 static void rb_plugins_engine_plugin_visible_cb (GConfClient *client,
87 guint cnxn_id,
88 GConfEntry *entry,
89 RBPluginInfo *info);
90 static gboolean rb_plugins_engine_activate_plugin_real (RBPluginInfo *info,
91 RBShell *shell);
92 static void rb_plugins_engine_deactivate_plugin_real (RBPluginInfo *info,
93 RBShell *shell);
95 static GHashTable *rb_plugins = NULL;
96 guint garbage_collect_id = 0;
97 RBShell *rb_plugins_shell = NULL;
99 static RBPluginInfo *
100 rb_plugins_engine_load (const gchar *file)
102 RBPluginInfo *info;
103 GKeyFile *plugin_file = NULL;
104 gchar *str;
106 g_return_val_if_fail (file != NULL, NULL);
108 rb_debug ("Loading plugin: %s", file);
110 info = g_new0 (RBPluginInfo, 1);
111 info->file = g_strdup (file);
113 plugin_file = g_key_file_new ();
114 if (!g_key_file_load_from_file (plugin_file, file, G_KEY_FILE_NONE, NULL))
116 g_warning ("Bad plugin file: %s", file);
117 goto error;
120 if (!g_key_file_has_key (plugin_file,
121 "RB Plugin",
122 "IAge",
123 NULL))
125 rb_debug ("IAge key does not exist in file: %s", file);
126 goto error;
129 /* Check IAge=1 */
130 if (g_key_file_get_integer (plugin_file,
131 "RB Plugin",
132 "IAge",
133 NULL) != 1)
135 rb_debug ("Wrong IAge in file: %s", file);
136 goto error;
139 /* Get Location */
140 str = g_key_file_get_string (plugin_file,
141 "RB Plugin",
142 "Module",
143 NULL);
144 if (str)
146 info->location = str;
148 else
150 g_warning ("Could not find 'Module' in %s", file);
151 goto error;
154 /* Get the loader for this plugin */
155 str = g_key_file_get_string (plugin_file,
156 "RB Plugin",
157 "Loader",
158 NULL);
159 if (str && strcmp(str, "python") == 0)
161 info->lang = RB_PLUGIN_LOADER_PY;
162 #ifndef ENABLE_PYTHON
163 rb_debug ("Cannot load python extension '%s', Rhythmbox was not "
164 "compiled with python support", file);
165 goto error;
166 #endif
168 else
170 info->lang = RB_PLUGIN_LOADER_C;
172 g_free (str);
174 /* Get Name */
175 str = g_key_file_get_locale_string (plugin_file,
176 "RB Plugin",
177 "Name",
178 NULL, NULL);
179 if (str)
180 info->name = str;
181 else
183 g_warning ("Could not find 'Name' in %s", file);
184 goto error;
187 /* Get Description */
188 str = g_key_file_get_locale_string (plugin_file,
189 "RB Plugin",
190 "Description",
191 NULL, NULL);
192 if (str)
193 info->desc = str;
194 else
195 rb_debug ("Could not find 'Description' in %s", file);
197 /* Get icon name */
198 str = g_key_file_get_string (plugin_file,
199 "RB Plugin",
200 "Icon",
201 NULL);
202 if (str)
203 info->icon_name = str;
204 else
205 rb_debug ("Could not find 'Description' in %s", file);
207 /* Get Authors */
208 info->authors = g_key_file_get_string_list (plugin_file,
209 "RB Plugin",
210 "Authors",
211 NULL, NULL);
212 if (info->authors == NULL)
213 rb_debug ("Could not find 'Authors' in %s", file);
215 /* Get Copyright */
216 str = g_key_file_get_string (plugin_file,
217 "RB Plugin",
218 "Copyright",
219 NULL);
220 if (str)
221 info->copyright = str;
222 else
223 rb_debug ("Could not find 'Copyright' in %s", file);
225 /* Get Copyright */
226 str = g_key_file_get_string (plugin_file,
227 "RB Plugin",
228 "Website",
229 NULL);
230 if (str)
231 info->website = str;
232 else
233 rb_debug ("Could not find 'Website' in %s", file);
235 g_key_file_free (plugin_file);
237 return info;
239 error:
240 g_free (info->file);
241 g_free (info->location);
242 g_free (info->name);
243 g_free (info);
244 g_key_file_free (plugin_file);
246 return NULL;
249 static void
250 rb_plugins_engine_load_cb (const char *uri, gboolean dir, gpointer userdata)
252 gchar *plugin_file;
253 RBPluginInfo *info;
254 char *key_name;
255 gboolean activate;
257 if (dir || !g_str_has_suffix (uri, PLUGIN_EXT))
258 return;
260 plugin_file = gnome_vfs_get_local_path_from_uri (uri);
261 info = rb_plugins_engine_load (plugin_file);
262 g_free (plugin_file);
264 if (info == NULL)
265 return;
267 if (g_hash_table_lookup (rb_plugins, info->location)) {
268 rb_plugin_info_free (info);
269 return;
272 g_hash_table_insert (rb_plugins, info->location, info);
273 rb_debug ("Plugin %s loaded", info->name);
275 key_name = g_strdup_printf (CONF_PLUGIN_ACTIVE_KEY, info->location);
276 info->active_notification_id = eel_gconf_notification_add (key_name,
277 (GConfClientNotifyFunc)rb_plugins_engine_plugin_active_cb,
278 info);
279 activate = eel_gconf_get_boolean (key_name);
280 g_free (key_name);
282 key_name = g_strdup_printf (CONF_PLUGIN_HIDDEN_KEY, info->location);
283 info->visible_notification_id = eel_gconf_notification_add (key_name,
284 (GConfClientNotifyFunc)rb_plugins_engine_plugin_visible_cb,
285 info);
286 info->visible = !eel_gconf_get_boolean (key_name);
287 g_free (key_name);
289 if (activate)
290 rb_plugins_engine_activate_plugin (info);
293 static void
294 rb_plugins_engine_load_dir (const gchar *path)
296 char *uri;
298 uri = rb_uri_resolve_relative (path);
299 rb_uri_handle_recursively (uri, (RBUriRecurseFunc)rb_plugins_engine_load_cb, NULL, NULL);
300 g_free (uri);
303 static void
304 rb_plugins_engine_load_all (void)
306 GList *paths;
308 paths = rb_get_plugin_paths ();
309 while (paths != NULL) {
310 rb_plugins_engine_load_dir (paths->data);
311 g_free (paths->data);
312 paths = g_list_delete_link (paths, paths);
316 static gboolean
317 garbage_collect_cb (gpointer data)
319 rb_plugins_engine_garbage_collect ();
320 return TRUE;
323 gboolean
324 rb_plugins_engine_init (RBShell *shell)
326 g_return_val_if_fail (rb_plugins == NULL, FALSE);
328 if (!g_module_supported ())
330 g_warning ("rb is not able to initialize the plugins engine.");
331 return FALSE;
333 rb_profile_start ("plugins engine init");
335 rb_plugins = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)rb_plugin_info_free);
337 rb_plugins_shell = shell;
338 g_object_ref (G_OBJECT (rb_plugins_shell));
340 rb_plugins_engine_load_all ();
342 garbage_collect_id = g_timeout_add_full (G_PRIORITY_LOW, 20000, garbage_collect_cb, NULL, NULL);
344 rb_profile_end ("plugins engine init");
346 return TRUE;
349 void
350 rb_plugins_engine_garbage_collect (void)
352 #ifdef ENABLE_PYTHON
353 rb_python_garbage_collect ();
354 #endif
357 static void
358 rb_plugin_info_free (RBPluginInfo *info)
360 if (info->active)
361 rb_plugins_engine_deactivate_plugin_real (info, rb_plugins_shell);
363 if (info->plugin != NULL) {
364 rb_debug ("Unref plugin %s", info->name);
366 g_object_unref (info->plugin);
368 /* info->module must not be unref since it is not possible to finalize
369 * a type module */
372 eel_gconf_notification_remove (info->active_notification_id);
373 eel_gconf_notification_remove (info->visible_notification_id);
375 g_free (info->file);
376 g_free (info->location);
377 g_free (info->name);
378 g_free (info->desc);
379 g_free (info->website);
380 g_free (info->copyright);
381 g_free (info->icon_name);
383 if (info->icon_pixbuf)
384 g_object_unref (info->icon_pixbuf);
385 g_strfreev (info->authors);
387 g_free (info);
390 void
391 rb_plugins_engine_shutdown (void)
393 g_hash_table_destroy (rb_plugins);
394 rb_plugins = NULL;
396 g_object_unref (rb_plugins_shell);
397 rb_plugins_shell = NULL;
399 g_source_remove (garbage_collect_id);
400 rb_plugins_engine_garbage_collect ();
402 #ifdef ENABLE_PYTHON
403 rb_python_shutdown ();
404 #endif
407 GList *
408 rb_plugins_engine_get_plugins_list (void)
410 return rb_collate_hash_table_values (rb_plugins);
413 static gboolean
414 load_plugin_module (RBPluginInfo *info)
416 gchar *path;
417 gchar *dirname;
419 g_return_val_if_fail (info != NULL, FALSE);
420 g_return_val_if_fail (info->file != NULL, FALSE);
421 g_return_val_if_fail (info->location != NULL, FALSE);
422 g_return_val_if_fail (info->plugin == NULL, FALSE);
424 switch (info->lang) {
425 case RB_PLUGIN_LOADER_C:
426 dirname = g_path_get_dirname (info->file);
427 g_return_val_if_fail (dirname != NULL, FALSE);
429 path = g_module_build_path (dirname, info->location);
430 #ifdef SHARE_UNINSTALLED_DIR
431 if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
432 char *temp;
434 g_free (path);
435 temp = g_build_filename (dirname, ".libs", NULL);
437 path = g_module_build_path (temp, info->location);
438 g_free (temp);
440 #endif
442 g_free (dirname);
443 g_return_val_if_fail (path != NULL, FALSE);
445 info->module = G_TYPE_MODULE (rb_module_new (path, info->location));
446 g_free (path);
447 break;
448 case RB_PLUGIN_LOADER_PY:
449 #ifdef ENABLE_PYTHON
450 info->module = G_TYPE_MODULE (rb_python_module_new (info->file, info->location));
451 #else
452 rb_debug ("cannot load plugin %s, python plugin support is disabled", info->location);
453 #endif
454 break;
457 if (g_type_module_use (info->module) == FALSE) {
458 g_warning ("Could not load plugin %s\n", info->location);
460 g_object_unref (G_OBJECT (info->module));
461 info->module = NULL;
463 return FALSE;
466 switch (info->lang) {
467 case RB_PLUGIN_LOADER_C:
468 info->plugin = RB_PLUGIN (rb_module_new_object (RB_MODULE (info->module)));
469 break;
470 case RB_PLUGIN_LOADER_PY:
471 #ifdef ENABLE_PYTHON
472 info->plugin = RB_PLUGIN (rb_python_module_new_object (RB_PYTHON_MODULE (info->module)));
473 #endif
474 break;
477 return TRUE;
480 static gboolean
481 rb_plugins_engine_activate_plugin_real (RBPluginInfo *info, RBShell *shell)
483 gboolean res = TRUE;
485 if (info->plugin == NULL)
486 res = load_plugin_module (info);
488 if (res)
489 rb_plugin_activate (info->plugin, shell);
490 else
491 g_warning ("Error, impossible to activate plugin '%s'", info->name);
493 return res;
496 gboolean
497 rb_plugins_engine_activate_plugin (RBPluginInfo *info)
499 g_return_val_if_fail (info != NULL, FALSE);
501 if (info->active)
502 return TRUE;
504 if (rb_plugins_engine_activate_plugin_real (info, rb_plugins_shell)) {
505 char *key_name;
507 key_name = g_strdup_printf (CONF_PLUGIN_ACTIVE_KEY, info->location);
508 eel_gconf_set_boolean (key_name, TRUE);
509 g_free (key_name);
511 info->active = TRUE;
513 return TRUE;
516 rb_error_dialog (NULL, _("Plugin Error"), _("Unable to activate plugin %s"), info->name);
518 return FALSE;
521 static void
522 rb_plugins_engine_deactivate_plugin_real (RBPluginInfo *info, RBShell *shell)
524 rb_plugin_deactivate (info->plugin, rb_plugins_shell);
527 gboolean
528 rb_plugins_engine_deactivate_plugin (RBPluginInfo *info)
530 char *key_name;
532 g_return_val_if_fail (info != NULL, FALSE);
534 if (!info->active)
535 return TRUE;
537 rb_plugins_engine_deactivate_plugin_real (info, rb_plugins_shell);
539 /* Update plugin state */
540 info->active = FALSE;
542 key_name = g_strdup_printf (CONF_PLUGIN_ACTIVE_KEY, info->location);
543 eel_gconf_set_boolean (key_name, FALSE);
544 g_free (key_name);
546 return TRUE;
549 gboolean
550 rb_plugins_engine_plugin_is_active (RBPluginInfo *info)
552 g_return_val_if_fail (info != NULL, FALSE);
554 return info->active;
557 gboolean
558 rb_plugins_engine_plugin_is_visible (RBPluginInfo *info)
560 g_return_val_if_fail (info != NULL, FALSE);
562 return info->visible;
565 gboolean
566 rb_plugins_engine_plugin_is_configurable (RBPluginInfo *info)
568 g_return_val_if_fail (info != NULL, FALSE);
570 if ((info->plugin == NULL) || !info->active)
571 return FALSE;
573 return rb_plugin_is_configurable (info->plugin);
576 void
577 rb_plugins_engine_configure_plugin (RBPluginInfo *info,
578 GtkWindow *parent)
580 GtkWidget *conf_dlg;
582 GtkWindowGroup *wg;
584 g_return_if_fail (info != NULL);
586 conf_dlg = rb_plugin_create_configure_dialog (info->plugin);
587 g_return_if_fail (conf_dlg != NULL);
588 gtk_window_set_transient_for (GTK_WINDOW (conf_dlg),
589 parent);
591 wg = parent->group;
592 if (wg == NULL)
594 wg = gtk_window_group_new ();
595 gtk_window_group_add_window (wg, parent);
598 gtk_window_group_add_window (wg,
599 GTK_WINDOW (conf_dlg));
601 gtk_window_set_modal (GTK_WINDOW (conf_dlg), TRUE);
602 gtk_widget_show (conf_dlg);
605 static void
606 rb_plugins_engine_plugin_active_cb (GConfClient *client,
607 guint cnxn_id,
608 GConfEntry *entry,
609 RBPluginInfo *info)
611 if (gconf_value_get_bool (entry->value)) {
612 rb_plugins_engine_activate_plugin (info);
613 } else {
614 rb_plugins_engine_deactivate_plugin (info);
618 static void
619 rb_plugins_engine_plugin_visible_cb (GConfClient *client,
620 guint cnxn_id,
621 GConfEntry *entry,
622 RBPluginInfo *info)
624 info->visible = !gconf_value_get_bool (entry->value);
627 const gchar *
628 rb_plugins_engine_get_plugin_name (RBPluginInfo *info)
630 g_return_val_if_fail (info != NULL, NULL);
632 return info->name;
635 const gchar *
636 rb_plugins_engine_get_plugin_description (RBPluginInfo *info)
638 g_return_val_if_fail (info != NULL, NULL);
640 return info->desc;
643 const gchar **
644 rb_plugins_engine_get_plugin_authors (RBPluginInfo *info)
646 g_return_val_if_fail (info != NULL, (const gchar **)NULL);
648 return (const gchar **)info->authors;
651 const gchar *
652 rb_plugins_engine_get_plugin_website (RBPluginInfo *info)
654 g_return_val_if_fail (info != NULL, NULL);
656 return info->website;
659 const gchar *
660 rb_plugins_engine_get_plugin_copyright (RBPluginInfo *info)
662 g_return_val_if_fail (info != NULL, NULL);
664 return info->copyright;
667 GdkPixbuf *
668 rb_plugins_engine_get_plugin_icon (RBPluginInfo *info)
670 if (info->icon_name == NULL)
671 return NULL;
673 if (info->icon_pixbuf == NULL) {
674 char *filename = NULL;
675 char *dirname;
677 dirname = g_path_get_dirname (info->file);
678 filename = g_build_filename (dirname, info->icon_name, NULL);
679 g_free (dirname);
681 info->icon_pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
682 g_free (filename);
685 return info->icon_pixbuf;