mark PurpleImageClass as private
[pidgin-git.git] / finch / gntplugin.c
blobdc11756c02a7a0e1f5c3671a4641b288923ca669
1 /* finch
3 * Finch is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
5 * source distribution.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
21 #include <internal.h>
23 #include <gnt.h>
24 #include <gntbox.h>
25 #include <gntbutton.h>
26 #include <gntlabel.h>
27 #include <gntline.h>
28 #include <gnttree.h>
29 #include <gntutils.h>
31 #include "finch.h"
33 #include "debug.h"
34 #include "notify.h"
35 #include "request.h"
37 #include "gntplugin.h"
38 #include "gntrequest.h"
40 typedef struct
42 FinchPluginPrefFrameCb pref_frame_cb;
43 } FinchPluginInfoPrivate;
45 enum
47 PROP_0,
48 PROP_GNT_PREF_FRAME_CB,
49 PROP_LAST
52 static struct
54 GntWidget *tree;
55 GntWidget *window;
56 GntWidget *aboot;
57 GntWidget *conf;
58 } plugins;
60 typedef struct
62 enum
64 FINCH_PLUGIN_UI_DATA_TYPE_WINDOW,
65 FINCH_PLUGIN_UI_DATA_TYPE_REQUEST
66 } type;
68 union
70 GntWidget *window;
71 gpointer request_handle;
72 } u;
73 } FinchPluginUiData;
75 G_DEFINE_TYPE_WITH_PRIVATE(FinchPluginInfo, finch_plugin_info,
76 PURPLE_TYPE_PLUGIN_INFO);
78 static GntWidget *process_pref_frame(PurplePluginPrefFrame *frame);
80 /* Set method for GObject properties */
81 static void
82 finch_plugin_info_set_property(GObject *obj, guint param_id, const GValue *value,
83 GParamSpec *pspec)
85 FinchPluginInfoPrivate *priv = finch_plugin_info_get_instance_private(
86 FINCH_PLUGIN_INFO(obj));
88 switch (param_id) {
89 case PROP_GNT_PREF_FRAME_CB:
90 priv->pref_frame_cb = g_value_get_pointer(value);
91 break;
92 default:
93 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
94 break;
98 /* Get method for GObject properties */
99 static void
100 finch_plugin_info_get_property(GObject *obj, guint param_id, GValue *value,
101 GParamSpec *pspec)
103 FinchPluginInfoPrivate *priv = finch_plugin_info_get_instance_private(
104 FINCH_PLUGIN_INFO(obj));
106 switch (param_id) {
107 case PROP_GNT_PREF_FRAME_CB:
108 g_value_set_pointer(value, priv->pref_frame_cb);
109 break;
110 default:
111 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, param_id, pspec);
112 break;
116 static void
117 finch_plugin_info_init(FinchPluginInfo *info)
121 /* Class initializer function */
122 static void finch_plugin_info_class_init(FinchPluginInfoClass *klass)
124 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
126 /* Setup properties */
127 obj_class->get_property = finch_plugin_info_get_property;
128 obj_class->set_property = finch_plugin_info_set_property;
130 g_object_class_install_property(obj_class, PROP_GNT_PREF_FRAME_CB,
131 g_param_spec_pointer("gnt-pref-frame-cb",
132 "GNT preferences frame callback",
133 "Callback that returns a GNT preferences frame",
134 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
135 G_PARAM_STATIC_STRINGS));
138 FinchPluginInfo *
139 finch_plugin_info_new(const char *first_property, ...)
141 GObject *info;
142 va_list var_args;
144 /* at least ID is required */
145 if (!first_property)
146 return NULL;
148 va_start(var_args, first_property);
149 info = g_object_new_valist(FINCH_TYPE_PLUGIN_INFO, first_property,
150 var_args);
151 va_end(var_args);
153 g_object_set(info, "ui-requirement", FINCH_UI, NULL);
155 return FINCH_PLUGIN_INFO(info);
158 static void
159 free_stringlist(GList *list)
161 g_list_foreach(list, (GFunc)g_free, NULL);
162 g_list_free(list);
165 static gboolean
166 has_prefs(PurplePlugin *plugin)
168 PurplePluginInfo *info = purple_plugin_get_info(plugin);
169 FinchPluginInfoPrivate *priv = NULL;
170 gboolean ret;
172 g_return_val_if_fail(plugin != NULL, FALSE);
174 if (!purple_plugin_is_loaded(plugin))
175 return FALSE;
177 if (FINCH_IS_PLUGIN_INFO(info))
178 priv = finch_plugin_info_get_instance_private(
179 FINCH_PLUGIN_INFO(info));
181 ret = ((priv && priv->pref_frame_cb) ||
182 purple_plugin_info_get_pref_frame_cb(info) ||
183 purple_plugin_info_get_pref_request_cb(info));
185 return ret;
188 static void
189 decide_conf_button(PurplePlugin *plugin)
191 if (has_prefs(plugin))
192 gnt_widget_set_visible(plugins.conf, TRUE);
193 else
194 gnt_widget_set_visible(plugins.conf, FALSE);
196 gnt_box_readjust(GNT_BOX(plugins.window));
197 gnt_widget_draw(plugins.window);
200 static void
201 finch_plugin_pref_close(PurplePlugin *plugin)
203 PurplePluginInfo *info;
204 FinchPluginUiData *ui_data;
206 g_return_if_fail(plugin != NULL);
208 info = purple_plugin_get_info(plugin);
209 ui_data = purple_plugin_info_get_ui_data(info);
211 if (!ui_data)
212 return;
214 if (ui_data->type == FINCH_PLUGIN_UI_DATA_TYPE_REQUEST) {
215 purple_request_close(PURPLE_REQUEST_FIELDS,
216 ui_data->u.request_handle);
217 return;
220 g_return_if_fail(ui_data->type == FINCH_PLUGIN_UI_DATA_TYPE_WINDOW);
222 gnt_widget_destroy(ui_data->u.window);
224 g_free(ui_data);
225 purple_plugin_info_set_ui_data(info, NULL);
228 static void
229 plugin_toggled_cb(GntWidget *tree, PurplePlugin *plugin, gpointer null)
231 GError *error = NULL;
233 if (gnt_tree_get_choice(GNT_TREE(tree), plugin))
235 if (!purple_plugin_load(plugin, &error)) {
236 purple_notify_error(NULL, _("ERROR"), _("loading plugin failed"), error->message, NULL);
237 gnt_tree_set_choice(GNT_TREE(tree), plugin, FALSE);
238 g_error_free(error);
241 else
243 if (!purple_plugin_unload(plugin, &error)) {
244 purple_notify_error(NULL, _("ERROR"), _("unloading plugin failed"), error->message, NULL);
245 purple_plugin_disable(plugin);
246 gnt_tree_set_choice(GNT_TREE(tree), plugin, TRUE);
247 g_error_free(error);
250 finch_plugin_pref_close(plugin);
252 decide_conf_button(plugin);
253 finch_plugins_save_loaded();
256 /* Xerox */
257 void
258 finch_plugins_save_loaded(void)
260 purple_plugins_save_loaded("/finch/plugins/loaded");
263 static void
264 selection_changed(GntWidget *widget, gpointer old, gpointer current, gpointer null)
266 PurplePlugin *plugin = current;
267 const gchar *filename;
268 GPluginPluginInfo *info;
269 char *text, *authors = NULL;
270 const char * const *authorlist;
271 GList *list = NULL, *iter = NULL;
273 if (!plugin)
274 return;
276 filename = gplugin_plugin_get_filename(GPLUGIN_PLUGIN(plugin));
277 info = GPLUGIN_PLUGIN_INFO(purple_plugin_get_info(plugin));
278 authorlist = gplugin_plugin_info_get_authors(info);
280 if (authorlist)
281 authors = g_strjoinv(", ", (gchar **)authorlist);
283 /* If the selected plugin was unseen before, mark it as seen. But save the list
284 * only when the plugin list is closed. So if the user enables a plugin, and it
285 * crashes, it won't get marked as seen so the user can fix the bug and still
286 * quickly find the plugin in the list.
287 * I probably mean 'plugin developers' by 'users' here. */
288 list = g_object_get_data(G_OBJECT(widget), "seen-list");
289 if (list) {
290 iter = g_list_find_custom(list, filename, (GCompareFunc)strcmp);
292 if (!iter) {
293 list = g_list_prepend(list, g_strdup(filename));
294 g_object_set_data(G_OBJECT(widget), "seen-list", list);
297 /* XXX: Use formatting and stuff */
298 gnt_text_view_clear(GNT_TEXT_VIEW(plugins.aboot));
299 text = g_strdup_printf(
300 (g_strv_length((gchar **)authorlist) > 1
301 ? _("Name: %s\nVersion: %s\nDescription: %s\nAuthors: "
302 "%s\nWebsite: %s\nFilename: %s\n")
303 : _("Name: %s\nVersion: %s\nDescription: %s\nAuthor: "
304 "%s\nWebsite: %s\nFilename: %s\n")),
305 SAFE(_(gplugin_plugin_info_get_name(info))),
306 SAFE(_(gplugin_plugin_info_get_version(info))),
307 SAFE(_(gplugin_plugin_info_get_description(info))),
308 SAFE(authors), SAFE(_(gplugin_plugin_info_get_website(info))),
309 SAFE(filename));
311 gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(plugins.aboot),
312 text, GNT_TEXT_FLAG_NORMAL);
313 gnt_text_view_scroll(GNT_TEXT_VIEW(plugins.aboot), 0);
315 g_free(text);
316 g_free(authors);
318 decide_conf_button(plugin);
321 static void
322 reset_plugin_window(GntWidget *window, gpointer null)
324 GList *list = g_object_get_data(G_OBJECT(plugins.tree), "seen-list");
325 purple_prefs_set_path_list("/finch/plugins/seen", list);
326 g_list_foreach(list, (GFunc)g_free, NULL);
327 g_list_free(list);
329 plugins.window = NULL;
330 plugins.tree = NULL;
331 plugins.aboot = NULL;
334 static int
335 plugin_compare(PurplePlugin *p1, PurplePlugin *p2)
337 char *s1 =
338 g_utf8_strup(gplugin_plugin_info_get_name(GPLUGIN_PLUGIN_INFO(
339 purple_plugin_get_info(p1))),
340 -1);
341 char *s2 =
342 g_utf8_strup(gplugin_plugin_info_get_name(GPLUGIN_PLUGIN_INFO(
343 purple_plugin_get_info(p2))),
344 -1);
345 int ret = g_utf8_collate(s1, s2);
346 g_free(s1);
347 g_free(s2);
349 return ret;
352 static void
353 remove_confwin(GntWidget *window, gpointer _plugin)
355 PurplePlugin *plugin = _plugin;
356 PurplePluginInfo *info = purple_plugin_get_info(plugin);
358 g_free(info->ui_data);
359 purple_plugin_info_set_ui_data(info, NULL);
362 static void
363 configure_plugin_cb(GntWidget *button, gpointer null)
365 PurplePlugin *plugin;
366 PurplePluginInfo *info;
367 FinchPluginInfoPrivate *priv = NULL;
368 FinchPluginUiData *ui_data;
370 g_return_if_fail(plugins.tree != NULL);
372 plugin = gnt_tree_get_selection_data(GNT_TREE(plugins.tree));
373 if (!purple_plugin_is_loaded(plugin))
375 purple_notify_error(plugin, _("Error"),
376 _("Plugin need to be loaded before you can configure it."), NULL, NULL);
377 return;
380 info = purple_plugin_get_info(plugin);
382 if (purple_plugin_info_get_ui_data(info))
383 return;
384 ui_data = g_new0(FinchPluginUiData, 1);
385 purple_plugin_info_set_ui_data(info, ui_data);
387 if (FINCH_IS_PLUGIN_INFO(info))
388 priv = finch_plugin_info_get_instance_private(
389 FINCH_PLUGIN_INFO(info));
391 if (priv && priv->pref_frame_cb != NULL)
393 GntWidget *window = gnt_vbox_new(FALSE);
394 GntWidget *box, *button;
396 gnt_box_set_toplevel(GNT_BOX(window), TRUE);
397 gnt_box_set_title(GNT_BOX(window),
398 gplugin_plugin_info_get_name(
399 GPLUGIN_PLUGIN_INFO(info)));
400 gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
402 box = priv->pref_frame_cb();
403 gnt_box_add_widget(GNT_BOX(window), box);
405 box = gnt_hbox_new(FALSE);
406 gnt_box_add_widget(GNT_BOX(window), box);
408 button = gnt_button_new(_("Close"));
409 gnt_box_add_widget(GNT_BOX(box), button);
410 g_signal_connect_swapped(G_OBJECT(button), "activate",
411 G_CALLBACK(gnt_widget_destroy), window);
412 g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(remove_confwin), plugin);
414 gnt_widget_show(window);
416 ui_data->type = FINCH_PLUGIN_UI_DATA_TYPE_WINDOW;
417 ui_data->u.window = window;
419 else if (purple_plugin_info_get_pref_request_cb(info))
421 PurplePluginPrefRequestCb pref_request_cb = purple_plugin_info_get_pref_request_cb(info);
422 gpointer handle;
424 ui_data->type = FINCH_PLUGIN_UI_DATA_TYPE_REQUEST;
425 ui_data->u.request_handle = handle = pref_request_cb(plugin);
426 purple_request_add_close_notify(handle,
427 purple_callback_set_zero, &info->ui_data);
428 purple_request_add_close_notify(handle, g_free, ui_data);
430 else if (purple_plugin_info_get_pref_frame_cb(info))
432 PurplePluginPrefFrameCb pref_frame_cb = purple_plugin_info_get_pref_frame_cb(info);
433 GntWidget *win = process_pref_frame(pref_frame_cb(plugin));
434 g_signal_connect(G_OBJECT(win), "destroy", G_CALLBACK(remove_confwin), plugin);
436 ui_data->type = FINCH_PLUGIN_UI_DATA_TYPE_WINDOW;
437 ui_data->u.window = win;
439 else
441 purple_notify_info(plugin, _("Error"), _("No configuration "
442 "options for this plugin."), NULL, NULL);
443 g_free(ui_data);
444 purple_plugin_info_set_ui_data(info, NULL);
448 void finch_plugins_show_all(void)
450 GntWidget *window, *tree, *box, *aboot, *button;
451 GList *plugin_list, *iter;
452 GList *seen;
454 if (plugins.window) {
455 gnt_window_present(plugins.window);
456 return;
459 purple_plugins_refresh();
461 plugins.window = window = gnt_vbox_new(FALSE);
462 gnt_box_set_toplevel(GNT_BOX(window), TRUE);
463 gnt_box_set_title(GNT_BOX(window), _("Plugins"));
464 gnt_box_set_pad(GNT_BOX(window), 0);
465 gnt_box_set_alignment(GNT_BOX(window), GNT_ALIGN_MID);
467 gnt_box_add_widget(GNT_BOX(window),
468 gnt_label_new(_("You can (un)load plugins from the following list.")));
469 gnt_box_add_widget(GNT_BOX(window), gnt_hline_new());
471 box = gnt_hbox_new(FALSE);
472 gnt_box_add_widget(GNT_BOX(window), box);
473 gnt_box_add_widget(GNT_BOX(window), gnt_hline_new());
475 gnt_box_set_pad(GNT_BOX(box), 0);
476 plugins.tree = tree = gnt_tree_new();
477 gnt_tree_set_compare_func(GNT_TREE(tree), (GCompareFunc)plugin_compare);
478 gnt_widget_set_has_border(tree, FALSE);
479 gnt_box_add_widget(GNT_BOX(box), tree);
480 gnt_box_add_widget(GNT_BOX(box), gnt_vline_new());
482 plugins.aboot = aboot = gnt_text_view_new();
483 gnt_text_view_set_flag(GNT_TEXT_VIEW(aboot), GNT_TEXT_VIEW_TOP_ALIGN);
484 gnt_widget_set_size(aboot, 40, 20);
485 gnt_box_add_widget(GNT_BOX(box), aboot);
487 seen = purple_prefs_get_path_list("/finch/plugins/seen");
489 plugin_list = purple_plugins_find_all();
490 for (iter = plugin_list; iter; iter = iter->next)
492 PurplePlugin *plug = PURPLE_PLUGIN(iter->data);
494 if (purple_plugin_is_internal(plug))
495 continue;
497 gnt_tree_add_choice(
498 GNT_TREE(tree), plug,
499 gnt_tree_create_row(
500 GNT_TREE(tree),
501 gplugin_plugin_info_get_name(
502 GPLUGIN_PLUGIN_INFO(
503 purple_plugin_get_info(plug)))),
504 NULL, NULL);
505 gnt_tree_set_choice(GNT_TREE(tree), plug, purple_plugin_is_loaded(plug));
506 if (!g_list_find_custom(seen, gplugin_plugin_get_filename(plug),
507 (GCompareFunc)strcmp)) {
508 gnt_tree_set_row_flags(GNT_TREE(tree), plug, GNT_TEXT_FLAG_BOLD);
511 g_list_free(plugin_list);
513 gnt_tree_set_col_width(GNT_TREE(tree), 0, 30);
514 g_signal_connect(G_OBJECT(tree), "toggled", G_CALLBACK(plugin_toggled_cb), NULL);
515 g_signal_connect(G_OBJECT(tree), "selection_changed", G_CALLBACK(selection_changed), NULL);
516 g_object_set_data(G_OBJECT(tree), "seen-list", seen);
518 box = gnt_hbox_new(FALSE);
519 gnt_box_add_widget(GNT_BOX(window), box);
521 button = gnt_button_new(_("Close"));
522 gnt_box_add_widget(GNT_BOX(box), button);
523 g_signal_connect_swapped(G_OBJECT(button), "activate",
524 G_CALLBACK(gnt_widget_destroy), window);
526 plugins.conf = button = gnt_button_new(_("Configure Plugin"));
527 gnt_box_add_widget(GNT_BOX(box), button);
528 g_signal_connect(G_OBJECT(button), "activate", G_CALLBACK(configure_plugin_cb), NULL);
530 g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(reset_plugin_window), NULL);
532 gnt_widget_show(window);
534 decide_conf_button(gnt_tree_get_selection_data(GNT_TREE(tree)));
537 static GntWidget*
538 process_pref_frame(PurplePluginPrefFrame *frame)
540 PurpleRequestField *field;
541 PurpleRequestFields *fields;
542 PurpleRequestFieldGroup *group = NULL;
543 GList *prefs;
544 GList *stringlist = NULL;
545 GntWidget *ret = NULL;
547 fields = purple_request_fields_new();
549 for (prefs = purple_plugin_pref_frame_get_prefs(frame); prefs; prefs = prefs->next) {
550 PurplePluginPref *pref = prefs->data;
551 PurplePrefType type;
552 const char *name = purple_plugin_pref_get_name(pref);
553 const char *label = purple_plugin_pref_get_label(pref);
554 if(name == NULL) {
555 if(label == NULL)
556 continue;
558 if(purple_plugin_pref_get_pref_type(pref) == PURPLE_PLUGIN_PREF_INFO) {
559 field = purple_request_field_label_new("*", purple_plugin_pref_get_label(pref));
560 purple_request_field_group_add_field(group, field);
561 } else {
562 group = purple_request_field_group_new(label);
563 purple_request_fields_add_group(fields, group);
565 continue;
568 field = NULL;
569 type = purple_prefs_get_pref_type(name);
570 if(purple_plugin_pref_get_pref_type(pref) == PURPLE_PLUGIN_PREF_CHOICE) {
571 GList *list = purple_plugin_pref_get_choices(pref);
572 gpointer current_value = NULL;
574 switch(type) {
575 case PURPLE_PREF_BOOLEAN:
576 current_value = g_strdup_printf("%d", (int)purple_prefs_get_bool(name));
577 break;
578 case PURPLE_PREF_INT:
579 current_value = g_strdup_printf("%d", (int)purple_prefs_get_int(name));
580 break;
581 case PURPLE_PREF_STRING:
582 current_value = g_strdup(purple_prefs_get_string(name));
583 break;
584 default:
585 continue;
588 field = purple_request_field_list_new(name, label);
589 purple_request_field_list_set_multi_select(field, FALSE);
590 while (list && list->next) {
591 const char *label = list->data;
592 char *value = NULL;
593 switch(type) {
594 case PURPLE_PREF_BOOLEAN:
595 value = g_strdup_printf("%d", GPOINTER_TO_INT(list->next->data));
596 break;
597 case PURPLE_PREF_INT:
598 value = g_strdup_printf("%d", GPOINTER_TO_INT(list->next->data));
599 break;
600 case PURPLE_PREF_STRING:
601 value = g_strdup(list->next->data);
602 break;
603 default:
604 break;
606 stringlist = g_list_prepend(stringlist, value);
607 purple_request_field_list_add_icon(field, label, NULL, value);
608 if (purple_strequal(value, current_value))
609 purple_request_field_list_add_selected(field, label);
610 list = list->next->next;
612 g_free(current_value);
613 } else {
614 switch(type) {
615 case PURPLE_PREF_BOOLEAN:
616 field = purple_request_field_bool_new(name, label, purple_prefs_get_bool(name));
617 break;
618 case PURPLE_PREF_INT:
619 field = purple_request_field_int_new(name, label, purple_prefs_get_int(name), INT_MIN, INT_MAX);
620 break;
621 case PURPLE_PREF_STRING:
622 field = purple_request_field_string_new(name, label, purple_prefs_get_string(name),
623 purple_plugin_pref_get_format_type(pref) & PURPLE_STRING_FORMAT_TYPE_MULTILINE);
624 break;
625 default:
626 break;
630 if (field) {
631 if (group == NULL) {
632 group = purple_request_field_group_new(_("Preferences"));
633 purple_request_fields_add_group(fields, group);
635 purple_request_field_group_add_field(group, field);
639 ret = purple_request_fields(NULL, _("Preferences"), NULL, NULL, fields,
640 _("Save"), G_CALLBACK(finch_request_save_in_prefs), _("Cancel"), NULL,
641 NULL, NULL);
642 g_signal_connect_swapped(G_OBJECT(ret), "destroy", G_CALLBACK(free_stringlist), stringlist);
643 return ret;