Cast back and forth between int and pointer instead of putting pointers
[pidgin-git.git] / pidgin / gtkplugin.c
blobc47a0cd12529b5fa21555b236ae075d0432c463b
1 /**
2 * @file gtkplugin.c GTK+ Plugins support
3 * @ingroup pidgin
4 */
6 /* pidgin
8 * Pidgin 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
26 #include "internal.h"
27 #include "pidgin.h"
28 #include "gtkplugin.h"
29 #include "gtkpluginpref.h"
30 #include "gtkutils.h"
31 #include "debug.h"
32 #include "prefs.h"
33 #include "request.h"
34 #include "pidgintooltip.h"
36 #include <string.h>
38 #define PIDGIN_RESPONSE_CONFIGURE 98121
40 static void plugin_toggled_stage_two(PurplePlugin *plug, GtkTreeModel *model,
41 GtkTreeIter *iter, gboolean unload);
43 static GtkWidget *expander = NULL;
44 static GtkWidget *plugin_dialog = NULL;
46 static GtkLabel *plugin_name = NULL;
47 static GtkTextBuffer *plugin_desc = NULL;
48 static GtkLabel *plugin_error = NULL;
49 static GtkLabel *plugin_author = NULL;
50 static GtkLabel *plugin_website = NULL;
51 static gchar *plugin_website_uri = NULL;
52 static GtkLabel *plugin_filename = NULL;
54 static GtkWidget *pref_button = NULL;
55 static GHashTable *plugin_pref_dialogs = NULL;
57 GtkWidget *
58 pidgin_plugin_get_config_frame(PurplePlugin *plugin)
60 GtkWidget *config = NULL;
62 g_return_val_if_fail(plugin != NULL, NULL);
64 if (PIDGIN_IS_PIDGIN_PLUGIN(plugin) && plugin->info->ui_info
65 && PIDGIN_PLUGIN_UI_INFO(plugin)->get_config_frame)
67 PidginPluginUiInfo *ui_info;
69 ui_info = PIDGIN_PLUGIN_UI_INFO(plugin);
71 config = ui_info->get_config_frame(plugin);
73 if (plugin->info->prefs_info
74 && plugin->info->prefs_info->get_plugin_pref_frame)
76 purple_debug_warning("gtkplugin",
77 "Plugin %s contains both, ui_info and "
78 "prefs_info preferences; prefs_info will be "
79 "ignored.", plugin->info->name);
83 if (config == NULL && plugin->info->prefs_info
84 && plugin->info->prefs_info->get_plugin_pref_frame)
86 PurplePluginPrefFrame *frame;
88 frame = plugin->info->prefs_info->get_plugin_pref_frame(plugin);
90 config = pidgin_plugin_pref_create_frame(frame);
92 plugin->info->prefs_info->frame = frame;
95 return config;
98 void
99 pidgin_plugins_save(void)
101 purple_plugins_save_loaded(PIDGIN_PREFS_ROOT "/plugins/loaded");
104 static void
105 update_plugin_list(void *data)
107 GtkListStore *ls = GTK_LIST_STORE(data);
108 GtkTreeIter iter;
109 GList *probes;
110 PurplePlugin *plug;
112 gtk_list_store_clear(ls);
113 purple_plugins_probe(G_MODULE_SUFFIX);
115 for (probes = purple_plugins_get_all();
116 probes != NULL;
117 probes = probes->next)
119 char *name;
120 char *version;
121 char *summary;
122 char *desc;
123 plug = probes->data;
125 if (plug->info->type == PURPLE_PLUGIN_LOADER) {
126 GList *cur;
127 for (cur = PURPLE_PLUGIN_LOADER_INFO(plug)->exts; cur != NULL;
128 cur = cur->next)
129 purple_plugins_probe(cur->data);
130 continue;
131 } else if (plug->info->type != PURPLE_PLUGIN_STANDARD ||
132 (plug->info->flags & PURPLE_PLUGIN_FLAG_INVISIBLE)) {
133 continue;
136 gtk_list_store_append (ls, &iter);
138 if (plug->info->name) {
139 name = g_markup_escape_text(_(plug->info->name), -1);
140 } else {
141 char *tmp = g_path_get_basename(plug->path);
142 name = g_markup_escape_text(tmp, -1);
143 g_free(tmp);
145 version = g_markup_escape_text(purple_plugin_get_version(plug), -1);
146 summary = g_markup_escape_text(purple_plugin_get_summary(plug), -1);
148 desc = g_strdup_printf("<b>%s</b> %s\n%s", name,
149 version,
150 summary);
151 g_free(name);
152 g_free(version);
153 g_free(summary);
155 gtk_list_store_set(ls, &iter,
156 0, purple_plugin_is_loaded(plug),
157 1, desc,
158 2, plug,
159 3, purple_plugin_is_unloadable(plug),
160 -1);
161 g_free(desc);
165 static void plugin_loading_common(PurplePlugin *plugin, GtkTreeView *view, gboolean loaded)
167 GtkTreeIter iter;
168 GtkTreeModel *model = gtk_tree_view_get_model(view);
170 if (gtk_tree_model_get_iter_first(model, &iter)) {
171 do {
172 PurplePlugin *plug;
173 GtkTreeSelection *sel;
175 gtk_tree_model_get(model, &iter, 2, &plug, -1);
177 if (plug != plugin)
178 continue;
180 gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, loaded, -1);
182 /* If the loaded/unloaded plugin is the selected row,
183 * update the pref_button. */
184 sel = gtk_tree_view_get_selection(view);
185 if (gtk_tree_selection_get_selected(sel, &model, &iter))
187 gtk_tree_model_get(model, &iter, 2, &plug, -1);
188 if (plug == plugin)
190 gtk_widget_set_sensitive(pref_button,
191 loaded
192 && ((PIDGIN_IS_PIDGIN_PLUGIN(plug) && plug->info->ui_info
193 && PIDGIN_PLUGIN_UI_INFO(plug)->get_config_frame)
194 || (plug->info->prefs_info
195 && plug->info->prefs_info->get_plugin_pref_frame)));
199 break;
200 } while (gtk_tree_model_iter_next(model, &iter));
204 static void plugin_load_cb(PurplePlugin *plugin, gpointer data)
206 GtkTreeView *view = (GtkTreeView *)data;
207 plugin_loading_common(plugin, view, TRUE);
210 static void plugin_unload_cb(PurplePlugin *plugin, gpointer data)
212 GtkTreeView *view = (GtkTreeView *)data;
213 plugin_loading_common(plugin, view, FALSE);
216 static void pref_dialog_response_cb(GtkWidget *d, int response, PurplePlugin *plug)
218 switch (response) {
219 case GTK_RESPONSE_CLOSE:
220 case GTK_RESPONSE_DELETE_EVENT:
221 g_hash_table_remove(plugin_pref_dialogs, plug);
222 if (g_hash_table_size(plugin_pref_dialogs) == 0) {
223 g_hash_table_destroy(plugin_pref_dialogs);
224 plugin_pref_dialogs = NULL;
226 gtk_widget_destroy(d);
228 if (plug->info->prefs_info && plug->info->prefs_info->frame) {
229 purple_plugin_pref_frame_destroy(plug->info->prefs_info->frame);
230 plug->info->prefs_info->frame = NULL;
233 break;
237 static void plugin_unload_confirm_cb(gpointer *data)
239 PurplePlugin *plugin = (PurplePlugin *)data[0];
240 GtkTreeModel *model = (GtkTreeModel *)data[1];
241 GtkTreeIter *iter = (GtkTreeIter *)data[2];
243 plugin_toggled_stage_two(plugin, model, iter, TRUE);
245 g_free(data);
248 static void plugin_toggled(GtkCellRendererToggle *cell, gchar *pth, gpointer data)
250 GtkTreeModel *model = (GtkTreeModel *)data;
251 GtkTreeIter *iter = g_new(GtkTreeIter, 1);
252 GtkTreePath *path = gtk_tree_path_new_from_string(pth);
253 PurplePlugin *plug;
254 GtkWidget *dialog = NULL;
256 gtk_tree_model_get_iter(model, iter, path);
257 gtk_tree_path_free(path);
258 gtk_tree_model_get(model, iter, 2, &plug, -1);
260 /* Apparently, GTK+ won't honor the sensitive flag on cell renderers for booleans. */
261 if (purple_plugin_is_unloadable(plug))
263 g_free(iter);
264 return;
267 if (!purple_plugin_is_loaded(plug))
269 pidgin_set_cursor(plugin_dialog, GDK_WATCH);
271 purple_plugin_load(plug);
272 plugin_toggled_stage_two(plug, model, iter, FALSE);
274 pidgin_clear_cursor(plugin_dialog);
276 else
278 if (plugin_pref_dialogs != NULL &&
279 (dialog = g_hash_table_lookup(plugin_pref_dialogs, plug)))
280 pref_dialog_response_cb(dialog, GTK_RESPONSE_DELETE_EVENT, plug);
282 if (plug->dependent_plugins != NULL)
284 GString *tmp = g_string_new(_("The following plugins will be unloaded."));
285 GList *l;
286 gpointer *cb_data;
288 for (l = plug->dependent_plugins ; l != NULL ; l = l->next)
290 const char *dep_name = (const char *)l->data;
291 PurplePlugin *dep_plugin = purple_plugins_find_with_id(dep_name);
292 g_return_if_fail(dep_plugin != NULL);
294 g_string_append_printf(tmp, "\n\t%s\n", purple_plugin_get_name(dep_plugin));
297 cb_data = g_new(gpointer, 3);
298 cb_data[0] = plug;
299 cb_data[1] = model;
300 cb_data[2] = iter;
302 purple_request_action(plugin_dialog, NULL,
303 _("Multiple plugins will be unloaded."),
304 tmp->str, 0,
305 NULL, NULL, NULL,
306 cb_data, 2,
307 _("Unload Plugins"), G_CALLBACK(plugin_unload_confirm_cb),
308 _("Cancel"), g_free);
309 g_string_free(tmp, TRUE);
311 else
312 plugin_toggled_stage_two(plug, model, iter, TRUE);
316 static void plugin_toggled_stage_two(PurplePlugin *plug, GtkTreeModel *model, GtkTreeIter *iter, gboolean unload)
318 if (unload)
320 pidgin_set_cursor(plugin_dialog, GDK_WATCH);
322 if (!purple_plugin_unload(plug))
324 const char *primary = _("Could not unload plugin");
325 const char *reload = _("The plugin could not be unloaded now, but will be disabled at the next startup.");
327 if (!plug->error)
329 purple_notify_warning(NULL, NULL, primary, reload);
331 else
333 char *tmp = g_strdup_printf("%s\n\n%s", reload, plug->error);
334 purple_notify_warning(NULL, NULL, primary, tmp);
335 g_free(tmp);
338 purple_plugin_disable(plug);
341 pidgin_clear_cursor(plugin_dialog);
344 gtk_widget_set_sensitive(pref_button,
345 purple_plugin_is_loaded(plug)
346 && ((PIDGIN_IS_PIDGIN_PLUGIN(plug) && plug->info->ui_info
347 && PIDGIN_PLUGIN_UI_INFO(plug)->get_config_frame)
348 || (plug->info->prefs_info
349 && plug->info->prefs_info->get_plugin_pref_frame)));
351 if (plug->error != NULL)
353 gchar *name = g_markup_escape_text(purple_plugin_get_name(plug), -1);
355 gchar *error = g_markup_escape_text(plug->error, -1);
356 gchar *text;
358 text = g_strdup_printf(
359 "<b>%s</b> %s\n<span weight=\"bold\" color=\"red\"%s</span>",
360 purple_plugin_get_name(plug), purple_plugin_get_version(plug), error);
361 gtk_list_store_set(GTK_LIST_STORE (model), iter,
362 1, text,
363 -1);
364 g_free(text);
366 text = g_strdup_printf(
367 "<span weight=\"bold\" color=\"red\">%s</span>",
368 error);
369 gtk_label_set_markup(plugin_error, text);
370 g_free(text);
372 g_free(error);
373 g_free(name);
376 gtk_list_store_set(GTK_LIST_STORE (model), iter,
377 0, purple_plugin_is_loaded(plug),
378 -1);
379 g_free(iter);
381 pidgin_plugins_save();
384 static gboolean ensure_plugin_visible(void *data)
386 GtkTreeSelection *sel = GTK_TREE_SELECTION(data);
387 GtkTreeView *tv = gtk_tree_selection_get_tree_view(sel);
388 GtkTreeModel *model = gtk_tree_view_get_model(tv);
389 GtkTreePath *path;
390 GtkTreeIter iter;
391 if (!gtk_tree_selection_get_selected (sel, &model, &iter))
392 return FALSE;
393 path = gtk_tree_model_get_path(model, &iter);
394 gtk_tree_view_scroll_to_cell(gtk_tree_selection_get_tree_view(sel), path, NULL, FALSE, 0, 0);
395 gtk_tree_path_free(path);
396 return FALSE;
399 static void prefs_plugin_sel (GtkTreeSelection *sel, GtkTreeModel *model)
401 gchar *buf, *tmp, *name, *version;
402 GtkTreeIter iter;
403 GValue val;
404 PurplePlugin *plug;
406 if (!gtk_tree_selection_get_selected (sel, &model, &iter))
408 gtk_widget_set_sensitive(pref_button, FALSE);
410 /* Collapse and disable the expander widget */
411 gtk_expander_set_expanded(GTK_EXPANDER(expander), FALSE);
412 gtk_widget_set_sensitive(expander, FALSE);
414 return;
417 gtk_widget_set_sensitive(expander, TRUE);
419 val.g_type = 0;
420 gtk_tree_model_get_value (model, &iter, 2, &val);
421 plug = g_value_get_pointer(&val);
423 name = g_markup_escape_text(purple_plugin_get_name(plug), -1);
424 version = g_markup_escape_text(purple_plugin_get_version(plug), -1);
425 buf = g_strdup_printf(
426 "<span size=\"larger\" weight=\"bold\">%s</span> "
427 "<span size=\"smaller\">%s</span>",
428 name, version);
429 gtk_label_set_markup(plugin_name, buf);
430 g_free(name);
431 g_free(version);
432 g_free(buf);
434 gtk_text_buffer_set_text(plugin_desc, purple_plugin_get_description(plug), -1);
435 gtk_label_set_text(plugin_author, purple_plugin_get_author(plug));
436 gtk_label_set_text(plugin_filename, plug->path);
438 g_free(plugin_website_uri);
439 plugin_website_uri = g_strdup(purple_plugin_get_homepage(plug));
440 if (plugin_website_uri)
442 tmp = g_markup_escape_text(plugin_website_uri, -1);
443 buf = g_strdup_printf("<span underline=\"single\" "
444 "foreground=\"blue\">%s</span>", tmp);
445 gtk_label_set_markup(plugin_website, buf);
446 g_free(tmp);
447 g_free(buf);
449 else
451 gtk_label_set_text(plugin_website, NULL);
454 if (plug->error == NULL)
456 gtk_label_set_text(plugin_error, NULL);
458 else
460 tmp = g_markup_escape_text(plug->error, -1);
461 buf = g_strdup_printf(
462 _("<span foreground=\"red\" weight=\"bold\">"
463 "Error: %s\n"
464 "Check the plugin website for an update."
465 "</span>"),
466 tmp);
467 gtk_label_set_markup(plugin_error, buf);
468 g_free(buf);
469 g_free(tmp);
472 gtk_widget_set_sensitive(pref_button,
473 purple_plugin_is_loaded(plug)
474 && ((PIDGIN_IS_PIDGIN_PLUGIN(plug) && plug->info->ui_info
475 && PIDGIN_PLUGIN_UI_INFO(plug)->get_config_frame)
476 || (plug->info->prefs_info
477 && plug->info->prefs_info->get_plugin_pref_frame)));
479 /* Make sure the selected plugin is still visible */
480 g_idle_add(ensure_plugin_visible, sel);
482 g_value_unset(&val);
485 static void plugin_dialog_response_cb(GtkWidget *d, int response, GtkTreeSelection *sel)
487 PurplePlugin *plug;
488 GtkWidget *dialog, *box;
489 GtkTreeModel *model;
490 GValue val;
491 GtkTreeIter iter;
493 switch (response) {
494 case GTK_RESPONSE_CLOSE:
495 case GTK_RESPONSE_DELETE_EVENT:
496 purple_request_close_with_handle(plugin_dialog);
497 purple_signals_disconnect_by_handle(plugin_dialog);
498 gtk_widget_destroy(d);
499 if (plugin_pref_dialogs != NULL) {
500 g_hash_table_destroy(plugin_pref_dialogs);
501 plugin_pref_dialogs = NULL;
503 plugin_dialog = NULL;
504 break;
505 case PIDGIN_RESPONSE_CONFIGURE:
506 if (! gtk_tree_selection_get_selected (sel, &model, &iter))
507 return;
508 val.g_type = 0;
509 gtk_tree_model_get_value(model, &iter, 2, &val);
510 plug = g_value_get_pointer(&val);
511 if (plug == NULL)
512 break;
513 if (plugin_pref_dialogs != NULL &&
514 g_hash_table_lookup(plugin_pref_dialogs, plug))
515 break;
516 box = pidgin_plugin_get_config_frame(plug);
517 if (box == NULL)
518 break;
520 dialog = gtk_dialog_new_with_buttons(PIDGIN_ALERT_TITLE, GTK_WINDOW(d),
521 GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_DESTROY_WITH_PARENT,
522 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
523 NULL);
524 if (plugin_pref_dialogs == NULL)
525 plugin_pref_dialogs = g_hash_table_new(NULL, NULL);
527 g_hash_table_insert(plugin_pref_dialogs, plug, dialog);
529 g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK(pref_dialog_response_cb), plug);
530 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), box);
531 gtk_window_set_role(GTK_WINDOW(dialog), "plugin_config");
532 gtk_window_set_title(GTK_WINDOW(dialog), _(purple_plugin_get_name(plug)));
533 gtk_widget_show_all(dialog);
534 g_value_unset(&val);
535 break;
539 static void
540 show_plugin_prefs_cb(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *column, GtkWidget *dialog)
542 GtkTreeSelection *sel;
543 GtkTreeIter iter;
544 PurplePlugin *plugin;
545 GtkTreeModel *model;
547 sel = gtk_tree_view_get_selection(view);
549 if (!gtk_tree_selection_get_selected(sel, &model, &iter))
550 return;
552 gtk_tree_model_get(model, &iter, 2, &plugin, -1);
554 if (!purple_plugin_is_loaded(plugin))
555 return;
557 /* Now show the pref-dialog for the plugin */
558 plugin_dialog_response_cb(dialog, PIDGIN_RESPONSE_CONFIGURE, sel);
561 static gboolean
562 pidgin_plugins_paint_tooltip(GtkWidget *tipwindow, gpointer data)
564 PangoLayout *layout = g_object_get_data(G_OBJECT(tipwindow), "tooltip-plugin");
565 gtk_paint_layout(tipwindow->style, tipwindow->window, GTK_STATE_NORMAL, FALSE,
566 NULL, tipwindow, "tooltip",
567 6, 6, layout);
568 return TRUE;
571 static gboolean
572 pidgin_plugins_create_tooltip(GtkWidget *tipwindow, GtkTreePath *path,
573 gpointer data, int *w, int *h)
575 GtkTreeIter iter;
576 GtkTreeView *treeview = GTK_TREE_VIEW(data);
577 PurplePlugin *plugin = NULL;
578 GtkTreeModel *model = gtk_tree_view_get_model(treeview);
579 PangoLayout *layout;
580 int width, height;
581 char *markup, *name, *desc, *author;
583 if (!gtk_tree_model_get_iter(model, &iter, path))
584 return FALSE;
586 gtk_tree_model_get(model, &iter, 2, &plugin, -1);
588 markup = g_strdup_printf("<span size='x-large' weight='bold'>%s</span>\n<b>%s:</b> %s\n<b>%s:</b> %s",
589 name = g_markup_escape_text(purple_plugin_get_name(plugin), -1),
590 _("Description"), desc = g_markup_escape_text(purple_plugin_get_description(plugin), -1),
591 _("Author"), author = g_markup_escape_text(purple_plugin_get_author(plugin), -1));
593 layout = gtk_widget_create_pango_layout(tipwindow, NULL);
594 pango_layout_set_markup(layout, markup, -1);
595 pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
596 pango_layout_set_width(layout, 600000);
597 pango_layout_get_size(layout, &width, &height);
598 g_object_set_data_full(G_OBJECT(tipwindow), "tooltip-plugin", layout, g_object_unref);
600 if (w)
601 *w = PANGO_PIXELS(width) + 12;
602 if (h)
603 *h = PANGO_PIXELS(height) + 12;
605 g_free(markup);
606 g_free(name);
607 g_free(desc);
608 g_free(author);
610 return TRUE;
613 static gboolean
614 website_button_motion_cb(GtkWidget *button, GdkEventCrossing *event,
615 gpointer unused)
617 if (plugin_website_uri) {
618 pidgin_set_cursor(button, GDK_HAND2);
619 return TRUE;
621 return FALSE;
624 static gboolean
625 website_button_clicked_cb(GtkButton *button, GdkEventButton *event,
626 gpointer unused)
628 if (plugin_website_uri) {
629 purple_notify_uri(NULL, plugin_website_uri);
630 return TRUE;
632 return FALSE;
635 static GtkWidget *
636 create_details()
638 GtkBox *vbox = GTK_BOX(gtk_vbox_new(FALSE, 3));
639 GtkSizeGroup *sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
640 GtkWidget *label, *view, *website_button;
642 plugin_name = GTK_LABEL(gtk_label_new(NULL));
643 gtk_misc_set_alignment(GTK_MISC(plugin_name), 0, 0);
644 gtk_label_set_line_wrap(plugin_name, FALSE);
645 gtk_label_set_selectable(plugin_name, TRUE);
646 gtk_box_pack_start(vbox, GTK_WIDGET(plugin_name), FALSE, FALSE, 0);
648 view = gtk_text_view_new();
649 plugin_desc = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
650 g_object_set(view, "wrap-mode", GTK_WRAP_WORD,
651 "editable", FALSE,
652 "left-margin", PIDGIN_HIG_CAT_SPACE,
653 "right-margin", PIDGIN_HIG_CAT_SPACE,
654 NULL);
655 gtk_box_pack_start(vbox, view, TRUE, TRUE, 0);
657 plugin_error = GTK_LABEL(gtk_label_new(NULL));
658 gtk_misc_set_alignment(GTK_MISC(plugin_error), 0, 0);
659 gtk_label_set_line_wrap(plugin_error, FALSE);
660 gtk_label_set_selectable(plugin_error, TRUE);
661 gtk_box_pack_start(vbox, GTK_WIDGET(plugin_error), FALSE, FALSE, 0);
663 plugin_author = GTK_LABEL(gtk_label_new(NULL));
664 gtk_label_set_line_wrap(plugin_author, FALSE);
665 gtk_misc_set_alignment(GTK_MISC(plugin_author), 0, 0);
666 gtk_label_set_selectable(plugin_author, TRUE);
667 pidgin_add_widget_to_vbox(vbox, "", sg,
668 GTK_WIDGET(plugin_author), TRUE, &label);
669 gtk_label_set_markup(GTK_LABEL(label), _("<b>Written by:</b>"));
670 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
672 website_button = gtk_event_box_new();
673 gtk_event_box_set_visible_window(GTK_EVENT_BOX(website_button), FALSE);
675 plugin_website = GTK_LABEL(gtk_label_new(NULL));
676 g_object_set(G_OBJECT(plugin_website),
677 "ellipsize", PANGO_ELLIPSIZE_MIDDLE, NULL);
678 gtk_misc_set_alignment(GTK_MISC(plugin_website), 0, 0);
679 gtk_container_add(GTK_CONTAINER(website_button),
680 GTK_WIDGET(plugin_website));
681 g_signal_connect(website_button, "button-release-event",
682 G_CALLBACK(website_button_clicked_cb), NULL);
683 g_signal_connect(website_button, "enter-notify-event",
684 G_CALLBACK(website_button_motion_cb), NULL);
685 g_signal_connect(website_button, "leave-notify-event",
686 G_CALLBACK(pidgin_clear_cursor), NULL);
688 pidgin_add_widget_to_vbox(vbox, "", sg, website_button, TRUE, &label);
689 gtk_label_set_markup(GTK_LABEL(label), _("<b>Web site:</b>"));
690 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
692 plugin_filename = GTK_LABEL(gtk_label_new(NULL));
693 gtk_label_set_line_wrap(plugin_filename, FALSE);
694 gtk_misc_set_alignment(GTK_MISC(plugin_filename), 0, 0);
695 gtk_label_set_selectable(plugin_filename, TRUE);
696 pidgin_add_widget_to_vbox(vbox, "", sg,
697 GTK_WIDGET(plugin_filename), TRUE, &label);
698 gtk_label_set_markup(GTK_LABEL(label), _("<b>Filename:</b>"));
699 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
701 g_object_unref(sg);
703 return GTK_WIDGET(vbox);
707 void pidgin_plugin_dialog_show()
709 GtkWidget *sw;
710 GtkWidget *event_view;
711 GtkListStore *ls;
712 GtkCellRenderer *rend, *rendt;
713 GtkTreeViewColumn *col;
714 GtkTreeSelection *sel;
716 if (plugin_dialog != NULL) {
717 gtk_window_present(GTK_WINDOW(plugin_dialog));
718 return;
721 plugin_dialog = gtk_dialog_new_with_buttons(_("Plugins"),
722 NULL,
723 GTK_DIALOG_NO_SEPARATOR,
724 NULL);
725 pref_button = gtk_dialog_add_button(GTK_DIALOG(plugin_dialog),
726 _("Configure Pl_ugin"), PIDGIN_RESPONSE_CONFIGURE);
727 gtk_dialog_add_button(GTK_DIALOG(plugin_dialog),
728 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
729 gtk_widget_set_sensitive(pref_button, FALSE);
730 gtk_window_set_role(GTK_WINDOW(plugin_dialog), "plugins");
732 sw = gtk_scrolled_window_new(NULL,NULL);
733 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
734 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN);
736 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(plugin_dialog)->vbox), sw, TRUE, TRUE, 0);
738 ls = gtk_list_store_new(4, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN);
739 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(ls),
740 1, GTK_SORT_ASCENDING);
742 update_plugin_list(ls);
744 event_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(ls));
746 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(event_view), TRUE);
748 g_signal_connect(G_OBJECT(event_view), "row-activated",
749 G_CALLBACK(show_plugin_prefs_cb), plugin_dialog);
751 purple_signal_connect(purple_plugins_get_handle(), "plugin-load", plugin_dialog,
752 PURPLE_CALLBACK(plugin_load_cb), event_view);
753 purple_signal_connect(purple_plugins_get_handle(), "plugin-unload", plugin_dialog,
754 PURPLE_CALLBACK(plugin_unload_cb), event_view);
756 rend = gtk_cell_renderer_toggle_new();
757 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (event_view));
759 col = gtk_tree_view_column_new_with_attributes (_("Enabled"),
760 rend,
761 "active", 0,
762 NULL);
763 gtk_tree_view_append_column (GTK_TREE_VIEW(event_view), col);
764 gtk_tree_view_column_set_sort_column_id(col, 0);
765 g_signal_connect(G_OBJECT(rend), "toggled",
766 G_CALLBACK(plugin_toggled), ls);
768 rendt = gtk_cell_renderer_text_new();
769 g_object_set(rendt,
770 "foreground", "#c0c0c0",
771 NULL);
772 col = gtk_tree_view_column_new_with_attributes (_("Name"),
773 rendt,
774 "markup", 1,
775 "foreground-set", 3,
776 NULL);
777 gtk_tree_view_column_set_expand (col, TRUE);
778 g_object_set(rendt, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
779 gtk_tree_view_append_column (GTK_TREE_VIEW(event_view), col);
780 gtk_tree_view_column_set_sort_column_id(col, 1);
781 g_object_unref(G_OBJECT(ls));
782 gtk_container_add(GTK_CONTAINER(sw), event_view);
783 gtk_tree_view_set_search_column(GTK_TREE_VIEW(event_view), 1);
784 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(event_view),
785 pidgin_tree_view_search_equal_func, NULL, NULL);
787 pidgin_tooltip_setup_for_treeview(event_view, event_view,
788 pidgin_plugins_create_tooltip,
789 pidgin_plugins_paint_tooltip);
792 expander = gtk_expander_new(_("<b>Plugin Details</b>"));
793 gtk_expander_set_use_markup(GTK_EXPANDER(expander), TRUE);
794 gtk_widget_set_sensitive(expander, FALSE);
795 gtk_container_add(GTK_CONTAINER(expander), create_details());
796 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(plugin_dialog)->vbox), expander,
797 FALSE, FALSE, 0);
800 g_signal_connect (G_OBJECT (sel), "changed", G_CALLBACK (prefs_plugin_sel), NULL);
801 g_signal_connect(G_OBJECT(plugin_dialog), "response", G_CALLBACK(plugin_dialog_response_cb), sel);
802 gtk_window_set_default_size(GTK_WINDOW(plugin_dialog), 430, 530);
804 pidgin_auto_parent_window(plugin_dialog);
806 gtk_widget_show_all(plugin_dialog);