Use conventional style for empty string check
[pidgin-git.git] / pidgin / gtkplugin.c
blobe5d196b97959967e2d1cd89873a9332c94671495
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),
531 pidgin_make_scrollable(box, GTK_POLICY_AUTOMATIC,
532 GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, 400, 400));
533 gtk_window_set_role(GTK_WINDOW(dialog), "plugin_config");
534 gtk_window_set_title(GTK_WINDOW(dialog), _(purple_plugin_get_name(plug)));
535 gtk_widget_show_all(dialog);
536 g_value_unset(&val);
537 break;
541 static void
542 show_plugin_prefs_cb(GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *column, GtkWidget *dialog)
544 GtkTreeSelection *sel;
545 GtkTreeIter iter;
546 PurplePlugin *plugin;
547 GtkTreeModel *model;
549 sel = gtk_tree_view_get_selection(view);
551 if (!gtk_tree_selection_get_selected(sel, &model, &iter))
552 return;
554 gtk_tree_model_get(model, &iter, 2, &plugin, -1);
556 if (!purple_plugin_is_loaded(plugin))
557 return;
559 /* Now show the pref-dialog for the plugin */
560 plugin_dialog_response_cb(dialog, PIDGIN_RESPONSE_CONFIGURE, sel);
563 static gboolean
564 pidgin_plugins_paint_tooltip(GtkWidget *tipwindow, gpointer data)
566 PangoLayout *layout = g_object_get_data(G_OBJECT(tipwindow), "tooltip-plugin");
567 gtk_paint_layout(tipwindow->style, tipwindow->window, GTK_STATE_NORMAL, FALSE,
568 NULL, tipwindow, "tooltip",
569 6, 6, layout);
570 return TRUE;
573 static gboolean
574 pidgin_plugins_create_tooltip(GtkWidget *tipwindow, GtkTreePath *path,
575 gpointer data, int *w, int *h)
577 GtkTreeIter iter;
578 GtkTreeView *treeview = GTK_TREE_VIEW(data);
579 PurplePlugin *plugin = NULL;
580 GtkTreeModel *model = gtk_tree_view_get_model(treeview);
581 PangoLayout *layout;
582 int width, height;
583 char *markup, *name, *desc, *author;
585 if (!gtk_tree_model_get_iter(model, &iter, path))
586 return FALSE;
588 gtk_tree_model_get(model, &iter, 2, &plugin, -1);
590 markup = g_strdup_printf("<span size='x-large' weight='bold'>%s</span>\n<b>%s:</b> %s\n<b>%s:</b> %s",
591 name = g_markup_escape_text(purple_plugin_get_name(plugin), -1),
592 _("Description"), desc = g_markup_escape_text(purple_plugin_get_description(plugin), -1),
593 _("Author"), author = g_markup_escape_text(purple_plugin_get_author(plugin), -1));
595 layout = gtk_widget_create_pango_layout(tipwindow, NULL);
596 pango_layout_set_markup(layout, markup, -1);
597 pango_layout_set_wrap(layout, PANGO_WRAP_WORD);
598 pango_layout_set_width(layout, 600000);
599 pango_layout_get_size(layout, &width, &height);
600 g_object_set_data_full(G_OBJECT(tipwindow), "tooltip-plugin", layout, g_object_unref);
602 if (w)
603 *w = PANGO_PIXELS(width) + 12;
604 if (h)
605 *h = PANGO_PIXELS(height) + 12;
607 g_free(markup);
608 g_free(name);
609 g_free(desc);
610 g_free(author);
612 return TRUE;
615 static gboolean
616 website_button_motion_cb(GtkWidget *button, GdkEventCrossing *event,
617 gpointer unused)
619 if (plugin_website_uri) {
620 pidgin_set_cursor(button, GDK_HAND2);
621 return TRUE;
623 return FALSE;
626 static gboolean
627 website_button_clicked_cb(GtkButton *button, GdkEventButton *event,
628 gpointer unused)
630 if (plugin_website_uri) {
631 purple_notify_uri(NULL, plugin_website_uri);
632 return TRUE;
634 return FALSE;
637 static GtkWidget *
638 create_details()
640 GtkBox *vbox = GTK_BOX(gtk_vbox_new(FALSE, 3));
641 GtkSizeGroup *sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
642 GtkWidget *label, *view, *website_button;
644 plugin_name = GTK_LABEL(gtk_label_new(NULL));
645 gtk_misc_set_alignment(GTK_MISC(plugin_name), 0, 0);
646 gtk_label_set_line_wrap(plugin_name, FALSE);
647 gtk_label_set_selectable(plugin_name, TRUE);
648 gtk_box_pack_start(vbox, GTK_WIDGET(plugin_name), FALSE, FALSE, 0);
650 view = gtk_text_view_new();
651 plugin_desc = gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));
652 g_object_set(view, "wrap-mode", GTK_WRAP_WORD,
653 "editable", FALSE,
654 "left-margin", PIDGIN_HIG_CAT_SPACE,
655 "right-margin", PIDGIN_HIG_CAT_SPACE,
656 NULL);
657 gtk_box_pack_start(vbox, view, TRUE, TRUE, 0);
659 plugin_error = GTK_LABEL(gtk_label_new(NULL));
660 gtk_misc_set_alignment(GTK_MISC(plugin_error), 0, 0);
661 gtk_label_set_line_wrap(plugin_error, FALSE);
662 gtk_label_set_selectable(plugin_error, TRUE);
663 gtk_box_pack_start(vbox, GTK_WIDGET(plugin_error), FALSE, FALSE, 0);
665 plugin_author = GTK_LABEL(gtk_label_new(NULL));
666 gtk_label_set_line_wrap(plugin_author, FALSE);
667 gtk_misc_set_alignment(GTK_MISC(plugin_author), 0, 0);
668 gtk_label_set_selectable(plugin_author, TRUE);
669 pidgin_add_widget_to_vbox(vbox, "", sg,
670 GTK_WIDGET(plugin_author), TRUE, &label);
671 gtk_label_set_markup(GTK_LABEL(label), _("<b>Written by:</b>"));
672 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
674 website_button = gtk_event_box_new();
675 gtk_event_box_set_visible_window(GTK_EVENT_BOX(website_button), FALSE);
677 plugin_website = GTK_LABEL(gtk_label_new(NULL));
678 g_object_set(G_OBJECT(plugin_website),
679 "ellipsize", PANGO_ELLIPSIZE_MIDDLE, NULL);
680 gtk_misc_set_alignment(GTK_MISC(plugin_website), 0, 0);
681 gtk_container_add(GTK_CONTAINER(website_button),
682 GTK_WIDGET(plugin_website));
683 g_signal_connect(website_button, "button-release-event",
684 G_CALLBACK(website_button_clicked_cb), NULL);
685 g_signal_connect(website_button, "enter-notify-event",
686 G_CALLBACK(website_button_motion_cb), NULL);
687 g_signal_connect(website_button, "leave-notify-event",
688 G_CALLBACK(pidgin_clear_cursor), NULL);
690 pidgin_add_widget_to_vbox(vbox, "", sg, website_button, TRUE, &label);
691 gtk_label_set_markup(GTK_LABEL(label), _("<b>Web site:</b>"));
692 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
694 plugin_filename = GTK_LABEL(gtk_label_new(NULL));
695 gtk_label_set_line_wrap(plugin_filename, FALSE);
696 gtk_misc_set_alignment(GTK_MISC(plugin_filename), 0, 0);
697 gtk_label_set_selectable(plugin_filename, TRUE);
698 pidgin_add_widget_to_vbox(vbox, "", sg,
699 GTK_WIDGET(plugin_filename), TRUE, &label);
700 gtk_label_set_markup(GTK_LABEL(label), _("<b>Filename:</b>"));
701 gtk_misc_set_alignment(GTK_MISC(label), 0, 0);
703 g_object_unref(sg);
705 return GTK_WIDGET(vbox);
709 void pidgin_plugin_dialog_show()
711 GtkWidget *event_view;
712 GtkListStore *ls;
713 GtkCellRenderer *rend, *rendt;
714 GtkTreeViewColumn *col;
715 GtkTreeSelection *sel;
717 if (plugin_dialog != NULL) {
718 gtk_window_present(GTK_WINDOW(plugin_dialog));
719 return;
722 plugin_dialog = gtk_dialog_new_with_buttons(_("Plugins"),
723 NULL,
724 GTK_DIALOG_NO_SEPARATOR,
725 NULL);
726 pref_button = gtk_dialog_add_button(GTK_DIALOG(plugin_dialog),
727 _("Configure Pl_ugin"), PIDGIN_RESPONSE_CONFIGURE);
728 gtk_dialog_add_button(GTK_DIALOG(plugin_dialog),
729 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE);
730 gtk_widget_set_sensitive(pref_button, FALSE);
731 gtk_window_set_role(GTK_WINDOW(plugin_dialog), "plugins");
733 ls = gtk_list_store_new(4, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN);
734 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(ls),
735 1, GTK_SORT_ASCENDING);
737 update_plugin_list(ls);
739 event_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(ls));
741 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(event_view), TRUE);
743 g_signal_connect(G_OBJECT(event_view), "row-activated",
744 G_CALLBACK(show_plugin_prefs_cb), plugin_dialog);
746 purple_signal_connect(purple_plugins_get_handle(), "plugin-load", plugin_dialog,
747 PURPLE_CALLBACK(plugin_load_cb), event_view);
748 purple_signal_connect(purple_plugins_get_handle(), "plugin-unload", plugin_dialog,
749 PURPLE_CALLBACK(plugin_unload_cb), event_view);
751 rend = gtk_cell_renderer_toggle_new();
752 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (event_view));
754 col = gtk_tree_view_column_new_with_attributes (_("Enabled"),
755 rend,
756 "active", 0,
757 NULL);
758 gtk_tree_view_append_column (GTK_TREE_VIEW(event_view), col);
759 gtk_tree_view_column_set_sort_column_id(col, 0);
760 g_signal_connect(G_OBJECT(rend), "toggled",
761 G_CALLBACK(plugin_toggled), ls);
763 rendt = gtk_cell_renderer_text_new();
764 g_object_set(rendt,
765 "foreground", "#c0c0c0",
766 NULL);
767 col = gtk_tree_view_column_new_with_attributes (_("Name"),
768 rendt,
769 "markup", 1,
770 "foreground-set", 3,
771 NULL);
772 gtk_tree_view_column_set_expand (col, TRUE);
773 g_object_set(rendt, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
774 gtk_tree_view_append_column (GTK_TREE_VIEW(event_view), col);
775 gtk_tree_view_column_set_sort_column_id(col, 1);
776 g_object_unref(G_OBJECT(ls));
777 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(plugin_dialog)->vbox),
778 pidgin_make_scrollable(event_view, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC, GTK_SHADOW_IN, -1, -1),
779 TRUE, TRUE, 0);
780 gtk_tree_view_set_search_column(GTK_TREE_VIEW(event_view), 1);
781 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(event_view),
782 pidgin_tree_view_search_equal_func, NULL, NULL);
784 pidgin_tooltip_setup_for_treeview(event_view, event_view,
785 pidgin_plugins_create_tooltip,
786 pidgin_plugins_paint_tooltip);
789 expander = gtk_expander_new(_("<b>Plugin Details</b>"));
790 gtk_expander_set_use_markup(GTK_EXPANDER(expander), TRUE);
791 gtk_widget_set_sensitive(expander, FALSE);
792 gtk_container_add(GTK_CONTAINER(expander), create_details());
793 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(plugin_dialog)->vbox), expander,
794 FALSE, FALSE, 0);
797 g_signal_connect (G_OBJECT (sel), "changed", G_CALLBACK (prefs_plugin_sel), NULL);
798 g_signal_connect(G_OBJECT(plugin_dialog), "response", G_CALLBACK(plugin_dialog_response_cb), sel);
799 gtk_window_set_default_size(GTK_WINDOW(plugin_dialog), 430, 530);
801 pidgin_auto_parent_window(plugin_dialog);
803 gtk_widget_show_all(plugin_dialog);