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
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
25 #include <gntbutton.h>
37 #include "gntplugin.h"
38 #include "gntrequest.h"
42 FinchPluginPrefFrameCb pref_frame_cb
;
43 } FinchPluginInfoPrivate
;
48 PROP_GNT_PREF_FRAME_CB
,
64 FINCH_PLUGIN_UI_DATA_TYPE_WINDOW
,
65 FINCH_PLUGIN_UI_DATA_TYPE_REQUEST
71 gpointer request_handle
;
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 */
82 finch_plugin_info_set_property(GObject
*obj
, guint param_id
, const GValue
*value
,
85 FinchPluginInfoPrivate
*priv
= finch_plugin_info_get_instance_private(
86 FINCH_PLUGIN_INFO(obj
));
89 case PROP_GNT_PREF_FRAME_CB
:
90 priv
->pref_frame_cb
= g_value_get_pointer(value
);
93 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
98 /* Get method for GObject properties */
100 finch_plugin_info_get_property(GObject
*obj
, guint param_id
, GValue
*value
,
103 FinchPluginInfoPrivate
*priv
= finch_plugin_info_get_instance_private(
104 FINCH_PLUGIN_INFO(obj
));
107 case PROP_GNT_PREF_FRAME_CB
:
108 g_value_set_pointer(value
, priv
->pref_frame_cb
);
111 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
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
));
139 finch_plugin_info_new(const char *first_property
, ...)
144 /* at least ID is required */
148 va_start(var_args
, first_property
);
149 info
= g_object_new_valist(FINCH_TYPE_PLUGIN_INFO
, first_property
,
153 g_object_set(info
, "ui-requirement", FINCH_UI
, NULL
);
155 return FINCH_PLUGIN_INFO(info
);
159 free_stringlist(GList
*list
)
161 g_list_foreach(list
, (GFunc
)g_free
, NULL
);
166 has_prefs(PurplePlugin
*plugin
)
168 PurplePluginInfo
*info
= purple_plugin_get_info(plugin
);
169 FinchPluginInfoPrivate
*priv
= NULL
;
172 g_return_val_if_fail(plugin
!= NULL
, FALSE
);
174 if (!purple_plugin_is_loaded(plugin
))
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
));
189 decide_conf_button(PurplePlugin
*plugin
)
191 if (has_prefs(plugin
))
192 gnt_widget_set_visible(plugins
.conf
, TRUE
);
194 gnt_widget_set_visible(plugins
.conf
, FALSE
);
196 gnt_box_readjust(GNT_BOX(plugins
.window
));
197 gnt_widget_draw(plugins
.window
);
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
);
214 if (ui_data
->type
== FINCH_PLUGIN_UI_DATA_TYPE_REQUEST
) {
215 purple_request_close(PURPLE_REQUEST_FIELDS
,
216 ui_data
->u
.request_handle
);
220 g_return_if_fail(ui_data
->type
== FINCH_PLUGIN_UI_DATA_TYPE_WINDOW
);
222 gnt_widget_destroy(ui_data
->u
.window
);
225 purple_plugin_info_set_ui_data(info
, NULL
);
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
);
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
);
250 finch_plugin_pref_close(plugin
);
252 decide_conf_button(plugin
);
253 finch_plugins_save_loaded();
258 finch_plugins_save_loaded(void)
260 purple_plugins_save_loaded("/finch/plugins/loaded");
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
;
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
);
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");
290 iter
= g_list_find_custom(list
, filename
, (GCompareFunc
)strcmp
);
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
))),
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);
318 decide_conf_button(plugin
);
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
);
329 plugins
.window
= NULL
;
331 plugins
.aboot
= NULL
;
335 plugin_compare(PurplePlugin
*p1
, PurplePlugin
*p2
)
338 g_utf8_strup(gplugin_plugin_info_get_name(GPLUGIN_PLUGIN_INFO(
339 purple_plugin_get_info(p1
))),
342 g_utf8_strup(gplugin_plugin_info_get_name(GPLUGIN_PLUGIN_INFO(
343 purple_plugin_get_info(p2
))),
345 int ret
= g_utf8_collate(s1
, s2
);
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
);
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
);
380 info
= purple_plugin_get_info(plugin
);
382 if (purple_plugin_info_get_ui_data(info
))
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
);
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
;
441 purple_notify_info(plugin
, _("Error"), _("No configuration "
442 "options for this plugin."), NULL
, NULL
);
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
;
454 if (plugins
.window
) {
455 gnt_window_present(plugins
.window
);
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
))
498 GNT_TREE(tree
), plug
,
501 gplugin_plugin_info_get_name(
503 purple_plugin_get_info(plug
)))),
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
)));
538 process_pref_frame(PurplePluginPrefFrame
*frame
)
540 PurpleRequestField
*field
;
541 PurpleRequestFields
*fields
;
542 PurpleRequestFieldGroup
*group
= NULL
;
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
;
552 const char *name
= purple_plugin_pref_get_name(pref
);
553 const char *label
= purple_plugin_pref_get_label(pref
);
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
);
562 group
= purple_request_field_group_new(label
);
563 purple_request_fields_add_group(fields
, group
);
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
;
575 case PURPLE_PREF_BOOLEAN
:
576 current_value
= g_strdup_printf("%d", (int)purple_prefs_get_bool(name
));
578 case PURPLE_PREF_INT
:
579 current_value
= g_strdup_printf("%d", (int)purple_prefs_get_int(name
));
581 case PURPLE_PREF_STRING
:
582 current_value
= g_strdup(purple_prefs_get_string(name
));
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
;
594 case PURPLE_PREF_BOOLEAN
:
595 value
= g_strdup_printf("%d", GPOINTER_TO_INT(list
->next
->data
));
597 case PURPLE_PREF_INT
:
598 value
= g_strdup_printf("%d", GPOINTER_TO_INT(list
->next
->data
));
600 case PURPLE_PREF_STRING
:
601 value
= g_strdup(list
->next
->data
);
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
);
615 case PURPLE_PREF_BOOLEAN
:
616 field
= purple_request_field_bool_new(name
, label
, purple_prefs_get_bool(name
));
618 case PURPLE_PREF_INT
:
619 field
= purple_request_field_int_new(name
, label
, purple_prefs_get_int(name
), INT_MIN
, INT_MAX
);
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
);
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
,
642 g_signal_connect_swapped(G_OBJECT(ret
), "destroy", G_CALLBACK(free_stringlist
), stringlist
);