2 * @file gntplugin.c GNT Plugins API
8 * Finch is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
30 #include <gntbutton.h>
42 #include "gntplugin.h"
43 #include "gntrequest.h"
53 static GHashTable
*confwins
;
55 static GntWidget
*process_pref_frame(PurplePluginPrefFrame
*frame
);
58 free_stringlist(GList
*list
)
60 g_list_foreach(list
, (GFunc
)g_free
, NULL
);
65 decide_conf_button(PurplePlugin
*plugin
)
67 if (purple_plugin_is_loaded(plugin
) &&
68 ((PURPLE_IS_GNT_PLUGIN(plugin
) &&
69 FINCH_PLUGIN_UI_INFO(plugin
) != NULL
) ||
70 (plugin
->info
->prefs_info
&&
71 plugin
->info
->prefs_info
->get_plugin_pref_frame
)))
72 gnt_widget_set_visible(plugins
.conf
, TRUE
);
74 gnt_widget_set_visible(plugins
.conf
, FALSE
);
76 gnt_box_readjust(GNT_BOX(plugins
.window
));
77 gnt_widget_draw(plugins
.window
);
81 plugin_toggled_cb(GntWidget
*tree
, PurplePlugin
*plugin
, gpointer null
)
83 if (gnt_tree_get_choice(GNT_TREE(tree
), plugin
))
85 if (!purple_plugin_load(plugin
)) {
86 purple_notify_error(NULL
, _("ERROR"), _("loading plugin failed"), NULL
);
87 gnt_tree_set_choice(GNT_TREE(tree
), plugin
, FALSE
);
94 if (!purple_plugin_unload(plugin
)) {
95 purple_notify_error(NULL
, _("ERROR"), _("unloading plugin failed"), NULL
);
96 purple_plugin_disable(plugin
);
97 gnt_tree_set_choice(GNT_TREE(tree
), plugin
, TRUE
);
100 if (confwins
&& (win
= g_hash_table_lookup(confwins
, plugin
)) != NULL
)
102 gnt_widget_destroy(win
);
105 decide_conf_button(plugin
);
106 finch_plugins_save_loaded();
111 finch_plugins_save_loaded(void)
113 purple_plugins_save_loaded("/finch/plugins/loaded");
117 selection_changed(GntWidget
*widget
, gpointer old
, gpointer current
, gpointer null
)
119 PurplePlugin
*plugin
= current
;
121 GList
*list
= NULL
, *iter
= NULL
;
126 /* If the selected plugin was unseen before, mark it as seen. But save the list
127 * only when the plugin list is closed. So if the user enables a plugin, and it
128 * crashes, it won't get marked as seen so the user can fix the bug and still
129 * quickly find the plugin in the list.
130 * I probably mean 'plugin developers' by 'users' here. */
131 list
= g_object_get_data(G_OBJECT(widget
), "seen-list");
133 iter
= g_list_find_custom(list
, plugin
->path
, (GCompareFunc
)strcmp
);
135 list
= g_list_prepend(list
, g_strdup(plugin
->path
));
136 g_object_set_data(G_OBJECT(widget
), "seen-list", list
);
139 /* XXX: Use formatting and stuff */
140 gnt_text_view_clear(GNT_TEXT_VIEW(plugins
.aboot
));
141 text
= g_strdup_printf(_("Name: %s\nVersion: %s\nDescription: %s\nAuthor: %s\nWebsite: %s\nFilename: %s\n"),
142 SAFE(_(plugin
->info
->name
)), SAFE(_(plugin
->info
->version
)), SAFE(_(plugin
->info
->description
)),
143 SAFE(_(plugin
->info
->author
)), SAFE(_(plugin
->info
->homepage
)), SAFE(plugin
->path
));
144 gnt_text_view_append_text_with_flags(GNT_TEXT_VIEW(plugins
.aboot
),
145 text
, GNT_TEXT_FLAG_NORMAL
);
146 gnt_text_view_scroll(GNT_TEXT_VIEW(plugins
.aboot
), 0);
148 decide_conf_button(plugin
);
152 reset_plugin_window(GntWidget
*window
, gpointer null
)
154 GList
*list
= g_object_get_data(G_OBJECT(plugins
.tree
), "seen-list");
155 purple_prefs_set_path_list("/finch/plugins/seen", list
);
156 g_list_foreach(list
, (GFunc
)g_free
, NULL
);
159 plugins
.window
= NULL
;
161 plugins
.aboot
= NULL
;
165 plugin_compare(PurplePlugin
*p1
, PurplePlugin
*p2
)
167 char *s1
= g_utf8_strup(p1
->info
->name
, -1);
168 char *s2
= g_utf8_strup(p2
->info
->name
, -1);
169 int ret
= g_utf8_collate(s1
, s2
);
178 confwins
= g_hash_table_new(g_direct_hash
, g_direct_equal
);
182 remove_confwin(GntWidget
*window
, gpointer plugin
)
184 g_hash_table_remove(confwins
, plugin
);
188 configure_plugin_cb(GntWidget
*button
, gpointer null
)
190 PurplePlugin
*plugin
;
191 FinchPluginFrame callback
;
193 g_return_if_fail(plugins
.tree
!= NULL
);
195 plugin
= gnt_tree_get_selection_data(GNT_TREE(plugins
.tree
));
196 if (!purple_plugin_is_loaded(plugin
))
198 purple_notify_error(plugin
, _("Error"),
199 _("Plugin need to be loaded before you can configure it."), NULL
);
203 if (confwins
&& g_hash_table_lookup(confwins
, plugin
))
206 if (PURPLE_IS_GNT_PLUGIN(plugin
) &&
207 (callback
= FINCH_PLUGIN_UI_INFO(plugin
)) != NULL
)
209 GntWidget
*window
= gnt_vbox_new(FALSE
);
210 GntWidget
*box
, *button
;
212 gnt_box_set_toplevel(GNT_BOX(window
), TRUE
);
213 gnt_box_set_title(GNT_BOX(window
), plugin
->info
->name
);
214 gnt_box_set_alignment(GNT_BOX(window
), GNT_ALIGN_MID
);
217 gnt_box_add_widget(GNT_BOX(window
), box
);
219 box
= gnt_hbox_new(FALSE
);
220 gnt_box_add_widget(GNT_BOX(window
), box
);
222 button
= gnt_button_new(_("Close"));
223 gnt_box_add_widget(GNT_BOX(box
), button
);
224 g_signal_connect_swapped(G_OBJECT(button
), "activate",
225 G_CALLBACK(gnt_widget_destroy
), window
);
226 g_signal_connect(G_OBJECT(window
), "destroy", G_CALLBACK(remove_confwin
), plugin
);
228 gnt_widget_show(window
);
230 if (confwins
== NULL
)
232 g_hash_table_insert(confwins
, plugin
, window
);
234 else if (plugin
->info
->prefs_info
&&
235 plugin
->info
->prefs_info
->get_plugin_pref_frame
)
237 GntWidget
*win
= process_pref_frame(plugin
->info
->prefs_info
->get_plugin_pref_frame(plugin
));
238 if (confwins
== NULL
)
240 g_signal_connect(G_OBJECT(win
), "destroy", G_CALLBACK(remove_confwin
), plugin
);
241 g_hash_table_insert(confwins
, plugin
, win
);
246 purple_notify_info(plugin
, _("Error"),
247 _("No configuration options for this plugin."), NULL
);
253 install_selected_file_cb(gpointer handle
, const char *filename
)
255 /* Try to init the selected file.
256 * If it succeeds, try to make a copy of the file in $USERDIR/plugins/.
257 * If the copy succeeds, unload and destroy the plugin in the original
258 * location and init+load the new one.
259 * Select the plugin in the plugin list.
262 PurplePlugin
*plugin
;
264 g_return_if_fail(plugins
.window
);
266 plugin
= purple_plugin_probe(filename
);
268 purple_notify_error(handle
, _("Error loading plugin"),
269 _("The selected file is not a valid plugin."),
270 _("Please open the debug window and try again to see the exact error message."));
273 if (g_list_find(gnt_tree_get_rows(GNT_TREE(plugins
.tree
)), plugin
)) {
274 purple_plugin_load(plugin
);
275 gnt_tree_set_choice(GNT_TREE(plugins
.tree
), plugin
, purple_plugin_is_loaded(plugin
));
276 gnt_tree_set_selected(GNT_TREE(plugins
.tree
), plugin
);
280 path
= g_build_filename(purple_user_dir(), "plugins", NULL
);
281 if (purple_build_dir(path
, S_IRUSR
| S_IWUSR
| S_IXUSR
) == 0) {
282 char *content
= NULL
;
285 if (g_file_get_contents(filename
, &content
, &length
, NULL
)) {
286 char *file
= g_path_get_basename(filename
);
288 path
= g_build_filename(purple_user_dir(), "plugins", file
, NULL
);
289 if (purple_util_write_data_to_file_absolute(path
, content
, length
)) {
290 purple_plugin_destroy(plugin
);
291 plugin
= purple_plugin_probe(path
);
293 purple_debug_warning("gntplugin", "This is really strange. %s can be loaded, but %s can't!\n",
296 plugin
= purple_plugin_probe(filename
);
305 purple_plugin_load(plugin
);
307 if (plugin
->info
->type
== PURPLE_PLUGIN_LOADER
) {
309 for (cur
= PURPLE_PLUGIN_LOADER_INFO(plugin
)->exts
; cur
!= NULL
;
311 purple_plugins_probe(cur
->data
);
315 if (plugin
->info
->type
!= PURPLE_PLUGIN_STANDARD
||
316 (plugin
->info
->flags
& PURPLE_PLUGIN_FLAG_INVISIBLE
) ||
320 gnt_tree_add_choice(GNT_TREE(plugins
.tree
), plugin
,
321 gnt_tree_create_row(GNT_TREE(plugins
.tree
), plugin
->info
->name
), NULL
, NULL
);
322 gnt_tree_set_choice(GNT_TREE(plugins
.tree
), plugin
, purple_plugin_is_loaded(plugin
));
323 gnt_tree_set_row_flags(GNT_TREE(plugins
.tree
), plugin
, GNT_TEXT_FLAG_BOLD
);
324 gnt_tree_set_selected(GNT_TREE(plugins
.tree
), plugin
);
328 install_plugin_cb(GntWidget
*w
, gpointer null
)
332 purple_request_close_with_handle(&handle
);
333 purple_request_file(&handle
, _("Select plugin to install"), NULL
,
334 FALSE
, G_CALLBACK(install_selected_file_cb
), NULL
,
335 NULL
, NULL
, NULL
, &handle
);
336 g_signal_connect_swapped(G_OBJECT(w
), "destroy", G_CALLBACK(purple_request_close_with_handle
), &handle
);
339 void finch_plugins_show_all()
341 GntWidget
*window
, *tree
, *box
, *aboot
, *button
;
345 if (plugins
.window
) {
346 gnt_window_present(plugins
.window
);
350 purple_plugins_probe(G_MODULE_SUFFIX
);
352 plugins
.window
= window
= gnt_vbox_new(FALSE
);
353 gnt_box_set_toplevel(GNT_BOX(window
), TRUE
);
354 gnt_box_set_title(GNT_BOX(window
), _("Plugins"));
355 gnt_box_set_pad(GNT_BOX(window
), 0);
356 gnt_box_set_alignment(GNT_BOX(window
), GNT_ALIGN_MID
);
358 gnt_box_add_widget(GNT_BOX(window
),
359 gnt_label_new(_("You can (un)load plugins from the following list.")));
360 gnt_box_add_widget(GNT_BOX(window
), gnt_hline_new());
362 box
= gnt_hbox_new(FALSE
);
363 gnt_box_add_widget(GNT_BOX(window
), box
);
364 gnt_box_add_widget(GNT_BOX(window
), gnt_hline_new());
366 gnt_box_set_pad(GNT_BOX(box
), 0);
367 plugins
.tree
= tree
= gnt_tree_new();
368 gnt_tree_set_compare_func(GNT_TREE(tree
), (GCompareFunc
)plugin_compare
);
369 GNT_WIDGET_SET_FLAGS(tree
, GNT_WIDGET_NO_BORDER
);
370 gnt_box_add_widget(GNT_BOX(box
), tree
);
371 gnt_box_add_widget(GNT_BOX(box
), gnt_vline_new());
373 plugins
.aboot
= aboot
= gnt_text_view_new();
374 gnt_text_view_set_flag(GNT_TEXT_VIEW(aboot
), GNT_TEXT_VIEW_TOP_ALIGN
);
375 gnt_widget_set_size(aboot
, 40, 20);
376 gnt_box_add_widget(GNT_BOX(box
), aboot
);
378 seen
= purple_prefs_get_path_list("/finch/plugins/seen");
379 for (iter
= purple_plugins_get_all(); iter
; iter
= iter
->next
)
381 PurplePlugin
*plug
= iter
->data
;
383 if (plug
->info
->type
== PURPLE_PLUGIN_LOADER
) {
385 for (cur
= PURPLE_PLUGIN_LOADER_INFO(plug
)->exts
; cur
!= NULL
;
387 purple_plugins_probe(cur
->data
);
391 if (plug
->info
->type
!= PURPLE_PLUGIN_STANDARD
||
392 (plug
->info
->flags
& PURPLE_PLUGIN_FLAG_INVISIBLE
) ||
396 gnt_tree_add_choice(GNT_TREE(tree
), plug
,
397 gnt_tree_create_row(GNT_TREE(tree
), plug
->info
->name
), NULL
, NULL
);
398 gnt_tree_set_choice(GNT_TREE(tree
), plug
, purple_plugin_is_loaded(plug
));
399 if (!g_list_find_custom(seen
, plug
->path
, (GCompareFunc
)strcmp
))
400 gnt_tree_set_row_flags(GNT_TREE(tree
), plug
, GNT_TEXT_FLAG_BOLD
);
402 gnt_tree_set_col_width(GNT_TREE(tree
), 0, 30);
403 g_signal_connect(G_OBJECT(tree
), "toggled", G_CALLBACK(plugin_toggled_cb
), NULL
);
404 g_signal_connect(G_OBJECT(tree
), "selection_changed", G_CALLBACK(selection_changed
), NULL
);
405 g_object_set_data(G_OBJECT(tree
), "seen-list", seen
);
407 box
= gnt_hbox_new(FALSE
);
408 gnt_box_add_widget(GNT_BOX(window
), box
);
410 button
= gnt_button_new(_("Install Plugin..."));
411 gnt_box_add_widget(GNT_BOX(box
), button
);
412 gnt_util_set_trigger_widget(GNT_WIDGET(tree
), GNT_KEY_INS
, button
);
413 g_signal_connect(G_OBJECT(button
), "activate", G_CALLBACK(install_plugin_cb
), NULL
);
415 button
= gnt_button_new(_("Close"));
416 gnt_box_add_widget(GNT_BOX(box
), button
);
417 g_signal_connect_swapped(G_OBJECT(button
), "activate",
418 G_CALLBACK(gnt_widget_destroy
), window
);
420 plugins
.conf
= button
= gnt_button_new(_("Configure Plugin"));
421 gnt_box_add_widget(GNT_BOX(box
), button
);
422 g_signal_connect(G_OBJECT(button
), "activate", G_CALLBACK(configure_plugin_cb
), NULL
);
424 g_signal_connect(G_OBJECT(window
), "destroy", G_CALLBACK(reset_plugin_window
), NULL
);
426 gnt_widget_show(window
);
428 decide_conf_button(gnt_tree_get_selection_data(GNT_TREE(tree
)));
432 process_pref_frame(PurplePluginPrefFrame
*frame
)
434 PurpleRequestField
*field
;
435 PurpleRequestFields
*fields
;
436 PurpleRequestFieldGroup
*group
= NULL
;
438 GList
*stringlist
= NULL
;
439 GntWidget
*ret
= NULL
;
441 fields
= purple_request_fields_new();
443 for (prefs
= purple_plugin_pref_frame_get_prefs(frame
); prefs
; prefs
= prefs
->next
) {
444 PurplePluginPref
*pref
= prefs
->data
;
446 const char *name
= purple_plugin_pref_get_name(pref
);
447 const char *label
= purple_plugin_pref_get_label(pref
);
452 if(purple_plugin_pref_get_type(pref
) == PURPLE_PLUGIN_PREF_INFO
) {
453 field
= purple_request_field_label_new("*", purple_plugin_pref_get_label(pref
));
454 purple_request_field_group_add_field(group
, field
);
456 group
= purple_request_field_group_new(label
);
457 purple_request_fields_add_group(fields
, group
);
463 type
= purple_prefs_get_type(name
);
464 if(purple_plugin_pref_get_type(pref
) == PURPLE_PLUGIN_PREF_CHOICE
) {
465 GList
*list
= purple_plugin_pref_get_choices(pref
);
466 gpointer current_value
= NULL
;
469 case PURPLE_PREF_BOOLEAN
:
470 current_value
= g_strdup_printf("%d", (int)purple_prefs_get_bool(name
));
472 case PURPLE_PREF_INT
:
473 current_value
= g_strdup_printf("%d", (int)purple_prefs_get_int(name
));
475 case PURPLE_PREF_STRING
:
476 current_value
= g_strdup(purple_prefs_get_string(name
));
482 field
= purple_request_field_list_new(name
, label
);
483 purple_request_field_list_set_multi_select(field
, FALSE
);
484 while (list
&& list
->next
) {
485 const char *label
= list
->data
;
488 case PURPLE_PREF_BOOLEAN
:
489 value
= g_strdup_printf("%d", GPOINTER_TO_INT(list
->next
->data
));
491 case PURPLE_PREF_INT
:
492 value
= g_strdup_printf("%d", GPOINTER_TO_INT(list
->next
->data
));
494 case PURPLE_PREF_STRING
:
495 value
= g_strdup(list
->next
->data
);
500 stringlist
= g_list_prepend(stringlist
, value
);
501 purple_request_field_list_add_icon(field
, label
, NULL
, value
);
502 if (strcmp(value
, current_value
) == 0)
503 purple_request_field_list_add_selected(field
, label
);
504 list
= list
->next
->next
;
506 g_free(current_value
);
509 case PURPLE_PREF_BOOLEAN
:
510 field
= purple_request_field_bool_new(name
, label
, purple_prefs_get_bool(name
));
512 case PURPLE_PREF_INT
:
513 field
= purple_request_field_int_new(name
, label
, purple_prefs_get_int(name
));
515 case PURPLE_PREF_STRING
:
516 field
= purple_request_field_string_new(name
, label
, purple_prefs_get_string(name
),
517 purple_plugin_pref_get_format_type(pref
) & PURPLE_STRING_FORMAT_TYPE_MULTILINE
);
526 group
= purple_request_field_group_new(_("Preferences"));
527 purple_request_fields_add_group(fields
, group
);
529 purple_request_field_group_add_field(group
, field
);
533 ret
= purple_request_fields(NULL
, _("Preferences"), NULL
, NULL
, fields
,
534 _("Save"), G_CALLBACK(finch_request_save_in_prefs
), _("Cancel"), NULL
,
537 g_signal_connect_swapped(G_OBJECT(ret
), "destroy", G_CALLBACK(free_stringlist
), stringlist
);