3 * Pidgin 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
23 #include "gtkplugin.h"
24 #include "gtkpluginpref.h"
29 #include "pidgintooltip.h"
33 #include "gtk3compat.h"
35 #define PIDGIN_RESPONSE_CONFIGURE 98121
39 PidginPluginConfigFrameCb config_frame_cb
;
40 } PidginPluginInfoPrivate
;
45 PROP_GTK_CONFIG_FRAME_CB
,
53 PIDGIN_PLUGIN_UI_DATA_TYPE_FRAME
,
54 PIDGIN_PLUGIN_UI_DATA_TYPE_REQUEST
62 PurplePluginPrefFrame
*pref_frame
;
65 gpointer request_handle
;
69 G_DEFINE_TYPE_WITH_PRIVATE(PidginPluginInfo
, pidgin_plugin_info
,
70 PURPLE_TYPE_PLUGIN_INFO
);
72 static void plugin_toggled_stage_two(PurplePlugin
*plug
, GtkTreeModel
*model
,
73 GtkTreeIter
*iter
, GError
*error
, gboolean unload
);
75 static GtkWidget
*expander
= NULL
;
76 static GtkWidget
*plugin_dialog
= NULL
;
78 static GtkLabel
*plugin_name
= NULL
;
79 static GtkTextBuffer
*plugin_desc
= NULL
;
80 static GtkLabel
*plugin_error
= NULL
;
81 static GtkLabel
*plugin_authors
= NULL
;
82 static GtkLabel
*plugin_website
= NULL
;
83 static gchar
*plugin_website_uri
= NULL
;
84 static GtkLabel
*plugin_filename
= NULL
;
86 static GtkWidget
*pref_button
= NULL
;
88 /* Set method for GObject properties */
90 pidgin_plugin_info_set_property(GObject
*obj
, guint param_id
, const GValue
*value
,
93 PidginPluginInfoPrivate
*priv
=
94 pidgin_plugin_info_get_instance_private(
95 PIDGIN_PLUGIN_INFO(obj
));
98 case PROP_GTK_CONFIG_FRAME_CB
:
99 priv
->config_frame_cb
= g_value_get_pointer(value
);
102 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
107 /* Get method for GObject properties */
109 pidgin_plugin_info_get_property(GObject
*obj
, guint param_id
, GValue
*value
,
112 PidginPluginInfoPrivate
*priv
=
113 pidgin_plugin_info_get_instance_private(
114 PIDGIN_PLUGIN_INFO(obj
));
117 case PROP_GTK_CONFIG_FRAME_CB
:
118 g_value_set_pointer(value
, priv
->config_frame_cb
);
121 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
126 /* Class initializer function */
127 static void pidgin_plugin_info_class_init(PidginPluginInfoClass
*klass
)
129 GObjectClass
*obj_class
= G_OBJECT_CLASS(klass
);
131 /* Setup properties */
132 obj_class
->get_property
= pidgin_plugin_info_get_property
;
133 obj_class
->set_property
= pidgin_plugin_info_set_property
;
135 g_object_class_install_property(obj_class
, PROP_GTK_CONFIG_FRAME_CB
,
136 g_param_spec_pointer("gtk-config-frame-cb",
137 "GTK configuration frame callback",
138 "Callback that returns a GTK configuration frame",
139 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
|
140 G_PARAM_STATIC_STRINGS
));
144 pidgin_plugin_info_init(PidginPluginInfo
*info
)
149 pidgin_plugin_info_new(const char *first_property
, ...)
154 /* at least ID is required */
158 va_start(var_args
, first_property
);
159 info
= g_object_new_valist(PIDGIN_TYPE_PLUGIN_INFO
, first_property
,
163 g_object_set(info
, "ui-requirement", PIDGIN_UI
, NULL
);
165 return PIDGIN_PLUGIN_INFO(info
);
169 pidgin_plugin_has_prefs(PurplePlugin
*plugin
)
171 PurplePluginInfo
*info
= purple_plugin_get_info(plugin
);
172 PidginPluginInfoPrivate
*priv
= NULL
;
175 g_return_val_if_fail(plugin
!= NULL
, FALSE
);
177 if (!purple_plugin_is_loaded(plugin
))
180 if (PIDGIN_IS_PLUGIN_INFO(info
))
181 priv
= pidgin_plugin_info_get_instance_private(
182 PIDGIN_PLUGIN_INFO(info
));
184 ret
= ((priv
&& priv
->config_frame_cb
) ||
185 purple_plugin_info_get_pref_frame_cb(info
) ||
186 purple_plugin_info_get_pref_request_cb(info
));
192 pidgin_plugin_get_config_frame(PurplePlugin
*plugin
,
193 PurplePluginPrefFrame
**purple_pref_frame
)
195 GtkWidget
*config
= NULL
;
196 PurplePluginInfo
*info
;
197 PurplePluginPrefFrameCb pref_frame_cb
= NULL
;
199 g_return_val_if_fail(PURPLE_IS_PLUGIN(plugin
), NULL
);
201 info
= purple_plugin_get_info(plugin
);
202 if(!PURPLE_IS_PLUGIN_INFO(info
))
205 pref_frame_cb
= purple_plugin_info_get_pref_frame_cb(info
);
207 PurplePluginPrefFrame
*frame
= pref_frame_cb(plugin
);
210 config
= pidgin_plugin_pref_create_frame(frame
);
212 *purple_pref_frame
= frame
;
220 pref_dialog_close(PurplePlugin
*plugin
)
222 PurplePluginInfo
*info
;
223 PidginPluginUiData
*ui_data
;
225 g_return_if_fail(plugin
!= NULL
);
227 info
= purple_plugin_get_info(plugin
);
229 ui_data
= purple_plugin_info_get_ui_data(info
);
233 if (ui_data
->type
== PIDGIN_PLUGIN_UI_DATA_TYPE_REQUEST
) {
234 purple_request_close(PURPLE_REQUEST_FIELDS
,
235 ui_data
->u
.request_handle
);
239 g_return_if_fail(ui_data
->type
== PIDGIN_PLUGIN_UI_DATA_TYPE_FRAME
);
241 gtk_widget_destroy(ui_data
->u
.frame
.dialog
);
243 if (ui_data
->u
.frame
.pref_frame
)
244 purple_plugin_pref_frame_destroy(ui_data
->u
.frame
.pref_frame
);
247 purple_plugin_info_set_ui_data(info
, NULL
);
252 pref_dialog_response_cb(GtkWidget
*dialog
, int response
, PurplePlugin
*plugin
)
254 if (response
== GTK_RESPONSE_CLOSE
||
255 response
== GTK_RESPONSE_DELETE_EVENT
)
257 pref_dialog_close(plugin
);
262 pidgin_plugin_open_config(PurplePlugin
*plugin
, GtkWindow
*parent
)
264 PurplePluginInfo
*info
;
265 PidginPluginInfoPrivate
*priv
= NULL
;
266 PidginPluginUiData
*ui_data
;
267 PurplePluginPrefFrameCb pref_frame_cb
;
268 PurplePluginPrefRequestCb pref_request_cb
;
269 PidginPluginConfigFrameCb get_pidgin_frame
= NULL
;
272 g_return_if_fail(plugin
!= NULL
);
274 info
= purple_plugin_get_info(plugin
);
276 if (!pidgin_plugin_has_prefs(plugin
)) {
277 purple_debug_warning("gtkplugin", "Plugin has no prefs");
281 if (purple_plugin_info_get_ui_data(info
))
284 if (PIDGIN_IS_PLUGIN_INFO(info
))
285 priv
= pidgin_plugin_info_get_instance_private(
286 PIDGIN_PLUGIN_INFO(info
));
288 pref_frame_cb
= purple_plugin_info_get_pref_frame_cb(info
);
289 pref_request_cb
= purple_plugin_info_get_pref_request_cb(info
);
292 get_pidgin_frame
= priv
->config_frame_cb
;
299 if (get_pidgin_frame
)
302 if (prefs_count
> 1) {
303 purple_debug_warning("gtkplugin",
304 "Plugin %s contains more than one prefs "
305 "callback, some will be ignored.",
306 gplugin_plugin_info_get_name(
307 GPLUGIN_PLUGIN_INFO(info
)));
309 g_return_if_fail(prefs_count
> 0);
311 ui_data
= g_new0(PidginPluginUiData
, 1);
312 purple_plugin_info_set_ui_data(info
, ui_data
);
314 /* Priority: pidgin frame > purple request > purple frame
315 * Purple frame could be replaced with purple request some day.
317 if (pref_request_cb
&& !get_pidgin_frame
) {
318 ui_data
->type
= PIDGIN_PLUGIN_UI_DATA_TYPE_REQUEST
;
319 ui_data
->u
.request_handle
= pref_request_cb(plugin
);
320 purple_request_add_close_notify(ui_data
->u
.request_handle
,
321 purple_callback_set_zero
, &info
->ui_data
);
322 purple_request_add_close_notify(ui_data
->u
.request_handle
,
325 GtkWidget
*box
, *dialog
;
327 ui_data
->type
= PIDGIN_PLUGIN_UI_DATA_TYPE_FRAME
;
329 box
= pidgin_plugin_get_config_frame(plugin
,
330 &ui_data
->u
.frame
.pref_frame
);
332 purple_debug_error("gtkplugin",
333 "Failed to display prefs frame");
335 purple_plugin_info_set_ui_data(info
, NULL
);
338 gtk_widget_set_vexpand(box
, TRUE
);
340 ui_data
->u
.frame
.dialog
= dialog
= gtk_dialog_new_with_buttons(
341 PIDGIN_ALERT_TITLE
, parent
,
342 GTK_DIALOG_DESTROY_WITH_PARENT
, GTK_STOCK_CLOSE
,
343 GTK_RESPONSE_CLOSE
, NULL
);
345 g_signal_connect(G_OBJECT(dialog
), "response",
346 G_CALLBACK(pref_dialog_response_cb
), plugin
);
348 gtk_container_add(GTK_CONTAINER(
349 gtk_dialog_get_content_area(GTK_DIALOG(dialog
))),
350 pidgin_make_scrollable(box
, GTK_POLICY_NEVER
,
351 GTK_POLICY_AUTOMATIC
, GTK_SHADOW_IN
, 400, 400));
353 gtk_window_set_role(GTK_WINDOW(dialog
), "plugin_config");
354 gtk_window_set_title(GTK_WINDOW(dialog
),
355 _(gplugin_plugin_info_get_name(
356 GPLUGIN_PLUGIN_INFO(info
))));
357 gtk_widget_show_all(dialog
);
362 pidgin_plugins_save(void)
364 purple_plugins_save_loaded(PIDGIN_PREFS_ROOT
"/plugins/loaded");
368 update_plugin_list(void *data
)
370 GtkListStore
*ls
= GTK_LIST_STORE(data
);
374 PurplePluginInfo
*info
;
375 GPluginPluginInfo
*ginfo
;
377 gtk_list_store_clear(ls
);
378 purple_plugins_refresh();
380 plugins
= purple_plugins_find_all();
382 for (l
= plugins
; l
!= NULL
; l
= l
->next
)
388 plug
= PURPLE_PLUGIN(l
->data
);
389 info
= purple_plugin_get_info(plug
);
390 ginfo
= GPLUGIN_PLUGIN_INFO(info
);
392 if (purple_plugin_is_internal(plug
))
395 gtk_list_store_append (ls
, &iter
);
397 if (gplugin_plugin_info_get_name(ginfo
)) {
398 name
= g_markup_escape_text(
399 _(gplugin_plugin_info_get_name(ginfo
)), -1);
401 char *tmp
= g_path_get_basename(
402 gplugin_plugin_get_filename(plug
));
403 name
= g_markup_escape_text(tmp
, -1);
406 version
= g_markup_escape_text(
407 gplugin_plugin_info_get_version(ginfo
), -1);
408 summary
= g_markup_escape_text(
409 gplugin_plugin_info_get_summary(ginfo
), -1);
411 desc
= g_strdup_printf("<b>%s</b> %s\n%s", name
,
418 gtk_list_store_set(ls
, &iter
,
419 0, purple_plugin_is_loaded(plug
),
422 3, purple_plugin_info_get_error(info
),
427 g_list_free(plugins
);
431 check_if_loaded(GtkTreeModel
*model
, GtkTreePath
*path
, GtkTreeIter
*iter
, gpointer data
)
433 PurplePlugin
*plugin
;
434 gtk_tree_model_get(model
, iter
, 2, &plugin
, -1);
435 gtk_list_store_set(GTK_LIST_STORE(model
), iter
,
436 0, purple_plugin_is_loaded(plugin
),
442 update_loaded_plugins(GtkTreeModel
*model
)
444 gtk_tree_model_foreach(model
, check_if_loaded
, NULL
);
447 static void plugin_loading_common(PurplePlugin
*plugin
, GtkTreeView
*view
, gboolean loaded
)
450 GtkTreeModel
*model
= gtk_tree_view_get_model(view
);
452 if (gtk_tree_model_get_iter_first(model
, &iter
)) {
455 GtkTreeSelection
*sel
;
457 gtk_tree_model_get(model
, &iter
, 2, &plug
, -1);
462 gtk_list_store_set(GTK_LIST_STORE(model
), &iter
, 0, loaded
, -1);
464 /* If the loaded/unloaded plugin is the selected row,
465 * update the pref_button. */
466 sel
= gtk_tree_view_get_selection(view
);
467 if (gtk_tree_selection_get_selected(sel
, &model
, &iter
))
469 gtk_tree_model_get(model
, &iter
, 2, &plug
, -1);
472 gtk_widget_set_sensitive(pref_button
,
473 pidgin_plugin_has_prefs(plug
));
478 } while (gtk_tree_model_iter_next(model
, &iter
));
482 static void plugin_load_cb(PurplePlugin
*plugin
, gpointer data
)
484 GtkTreeView
*view
= (GtkTreeView
*)data
;
485 plugin_loading_common(plugin
, view
, TRUE
);
488 static void plugin_unload_cb(PurplePlugin
*plugin
, gpointer data
)
490 GtkTreeView
*view
= (GtkTreeView
*)data
;
491 plugin_loading_common(plugin
, view
, FALSE
);
494 static void plugin_unload_confirm_cb(gpointer
*data
)
496 PurplePlugin
*plugin
= (PurplePlugin
*)data
[0];
497 GtkTreeModel
*model
= (GtkTreeModel
*)data
[1];
498 GtkTreeIter
*iter
= (GtkTreeIter
*)data
[2];
500 plugin_toggled_stage_two(plugin
, model
, iter
, NULL
, TRUE
);
505 static void plugin_toggled(GtkCellRendererToggle
*cell
, gchar
*pth
, gpointer data
)
507 GtkTreeModel
*model
= (GtkTreeModel
*)data
;
508 GtkTreeIter
*iter
= g_new(GtkTreeIter
, 1);
509 GtkTreePath
*path
= gtk_tree_path_new_from_string(pth
);
511 GError
*error
= NULL
;
513 gtk_tree_model_get_iter(model
, iter
, path
);
514 gtk_tree_path_free(path
);
515 gtk_tree_model_get(model
, iter
, 2, &plug
, -1);
517 if (!purple_plugin_is_loaded(plug
))
519 pidgin_set_cursor(plugin_dialog
, GDK_WATCH
);
521 purple_plugin_load(plug
, &error
);
522 plugin_toggled_stage_two(plug
, model
, iter
, error
, FALSE
);
524 pidgin_clear_cursor(plugin_dialog
);
528 pref_dialog_close(plug
);
530 if (purple_plugin_get_dependent_plugins(plug
) != NULL
)
532 GString
*tmp
= g_string_new(_("The following plugins will be unloaded."));
536 for (l
= purple_plugin_get_dependent_plugins(plug
); l
!= NULL
; l
= l
->next
)
538 const char *dep_name
= (const char *)l
->data
;
539 PurplePlugin
*dep_plugin
= purple_plugins_find_plugin(dep_name
);
540 GPluginPluginInfo
*dep_info
;
542 if (dep_plugin
== NULL
) {
543 purple_debug_error("gtkplugin",
544 "The %s plugin could not be found.",
549 dep_info
= GPLUGIN_PLUGIN_INFO(
550 purple_plugin_get_info(dep_plugin
));
551 g_string_append_printf(
553 gplugin_plugin_info_get_name(dep_info
));
556 cb_data
= g_new(gpointer
, 3);
561 purple_request_action(plugin_dialog
, NULL
,
562 _("Multiple plugins will be unloaded."),
563 tmp
->str
, 0, NULL
, cb_data
, 2,
565 G_CALLBACK(plugin_unload_confirm_cb
),
566 _("Cancel"), g_free
);
567 g_string_free(tmp
, TRUE
);
570 plugin_toggled_stage_two(plug
, model
, iter
, NULL
, TRUE
);
574 static void plugin_toggled_stage_two(PurplePlugin
*plug
, GtkTreeModel
*model
, GtkTreeIter
*iter
, GError
*error
, gboolean unload
)
576 GPluginPluginInfo
*info
=
577 GPLUGIN_PLUGIN_INFO(purple_plugin_get_info(plug
));
581 pidgin_set_cursor(plugin_dialog
, GDK_WATCH
);
583 if (!purple_plugin_unload(plug
, &error
))
585 const char *primary
= _("Could not unload plugin");
586 const char *reload
= _("The plugin could not be unloaded now, but will be disabled at the next startup.");
588 char *tmp
= g_strdup_printf("%s\n\n%s", reload
, error
->message
);
589 purple_notify_warning(NULL
, NULL
, primary
, tmp
, NULL
);
592 purple_plugin_disable(plug
);
595 pidgin_clear_cursor(plugin_dialog
);
597 purple_notify_warning(NULL
, NULL
, _("Could not load plugin"), error
->message
, NULL
);
600 gtk_widget_set_sensitive(pref_button
, pidgin_plugin_has_prefs(plug
));
604 gchar
*name
= g_markup_escape_text(
605 gplugin_plugin_info_get_name(info
), -1);
607 gchar
*disp_error
= g_markup_escape_text(error
->message
, -1);
610 text
= g_strdup_printf("<b>%s</b> %s\n<span weight=\"bold\" "
611 "color=\"red\">%s</span>",
612 gplugin_plugin_info_get_name(info
),
613 gplugin_plugin_info_get_version(info
),
615 gtk_list_store_set(GTK_LIST_STORE (model
), iter
,
620 text
= g_strdup_printf(
621 "<span weight=\"bold\" color=\"red\">%s</span>",
623 gtk_label_set_markup(plugin_error
, text
);
632 if ((unload
&& purple_plugin_get_dependent_plugins(plug
)) ||
633 (!unload
&& gplugin_plugin_info_get_dependencies(info
))) {
634 update_loaded_plugins(model
);
636 gtk_list_store_set(GTK_LIST_STORE (model
), iter
,
637 0, purple_plugin_is_loaded(plug
),
642 pidgin_plugins_save();
645 static gboolean
ensure_plugin_visible(void *data
)
647 GtkTreeSelection
*sel
= GTK_TREE_SELECTION(data
);
648 GtkTreeView
*tv
= gtk_tree_selection_get_tree_view(sel
);
649 GtkTreeModel
*model
= gtk_tree_view_get_model(tv
);
652 if (!gtk_tree_selection_get_selected (sel
, &model
, &iter
))
654 path
= gtk_tree_model_get_path(model
, &iter
);
655 gtk_tree_view_scroll_to_cell(gtk_tree_selection_get_tree_view(sel
), path
, NULL
, FALSE
, 0, 0);
656 gtk_tree_path_free(path
);
660 static void prefs_plugin_sel (GtkTreeSelection
*sel
, GtkTreeModel
*model
)
662 gchar
*buf
, *tmp
, *name
, *version
;
663 gchar
*authors
= NULL
;
664 const gchar
* const *authorlist
;
668 PurplePluginInfo
*info
;
669 GPluginPluginInfo
*ginfo
;
671 if (!gtk_tree_selection_get_selected (sel
, &model
, &iter
))
673 gtk_widget_set_sensitive(pref_button
, FALSE
);
675 /* Collapse and disable the expander widget */
676 gtk_expander_set_expanded(GTK_EXPANDER(expander
), FALSE
);
677 gtk_widget_set_sensitive(expander
, FALSE
);
682 gtk_widget_set_sensitive(expander
, TRUE
);
685 gtk_tree_model_get_value (model
, &iter
, 2, &val
);
686 plug
= g_value_get_pointer(&val
);
687 info
= purple_plugin_get_info(plug
);
688 ginfo
= GPLUGIN_PLUGIN_INFO(info
);
690 name
= g_markup_escape_text(gplugin_plugin_info_get_name(ginfo
), -1);
691 version
= g_markup_escape_text(gplugin_plugin_info_get_version(ginfo
),
693 buf
= g_strdup_printf(
694 "<span size=\"larger\" weight=\"bold\">%s</span> "
695 "<span size=\"smaller\">%s</span>",
697 gtk_label_set_markup(plugin_name
, buf
);
702 gtk_text_buffer_set_text(
703 plugin_desc
, gplugin_plugin_info_get_description(ginfo
), -1);
705 authorlist
= gplugin_plugin_info_get_authors(ginfo
);
707 authors
= g_strjoinv(",\n", (gchar
**)authorlist
);
708 gtk_label_set_text(plugin_authors
, authors
);
711 gtk_label_set_text(plugin_filename
, gplugin_plugin_get_filename(plug
));
713 g_free(plugin_website_uri
);
714 plugin_website_uri
= g_strdup(gplugin_plugin_info_get_website(ginfo
));
716 if (plugin_website_uri
)
718 tmp
= g_markup_escape_text(plugin_website_uri
, -1);
719 buf
= g_strdup_printf("<span underline=\"single\" "
720 "foreground=\"blue\">%s</span>", tmp
);
721 gtk_label_set_markup(plugin_website
, buf
);
727 gtk_label_set_text(plugin_website
, NULL
);
730 if (purple_plugin_info_get_error(info
) == NULL
)
732 gtk_label_set_text(plugin_error
, NULL
);
736 tmp
= g_markup_escape_text(purple_plugin_info_get_error(info
), -1);
737 buf
= g_strdup_printf(
738 _("<span foreground=\"red\" weight=\"bold\">"
740 "Check the plugin website for an update."
743 gtk_label_set_markup(plugin_error
, buf
);
748 gtk_widget_set_sensitive(pref_button
, pidgin_plugin_has_prefs(plug
));
750 /* Make sure the selected plugin is still visible */
751 g_idle_add(ensure_plugin_visible
, sel
);
756 static void plugin_dialog_response_cb(GtkWidget
*d
, int response
, GtkTreeSelection
*sel
)
758 PurplePlugin
*plugin
;
764 g_return_if_fail(d
!= NULL
);
767 case GTK_RESPONSE_CLOSE
:
768 case GTK_RESPONSE_DELETE_EVENT
:
769 purple_request_close_with_handle(plugin_dialog
);
770 purple_signals_disconnect_by_handle(plugin_dialog
);
771 list
= purple_plugins_find_all();
772 g_list_free_full(list
, (GDestroyNotify
)pref_dialog_close
);
773 gtk_widget_destroy(d
);
774 plugin_dialog
= NULL
;
776 case PIDGIN_RESPONSE_CONFIGURE
:
777 if (! gtk_tree_selection_get_selected (sel
, &model
, &iter
))
780 gtk_tree_model_get_value(model
, &iter
, 2, &val
);
781 plugin
= g_value_get_pointer(&val
);
786 pidgin_plugin_open_config(plugin
, GTK_WINDOW(d
));
793 show_plugin_prefs_cb(GtkTreeView
*view
, GtkTreePath
*path
, GtkTreeViewColumn
*column
, GtkWidget
*dialog
)
795 GtkTreeSelection
*sel
;
797 PurplePlugin
*plugin
;
800 sel
= gtk_tree_view_get_selection(view
);
802 if (!gtk_tree_selection_get_selected(sel
, &model
, &iter
))
805 gtk_tree_model_get(model
, &iter
, 2, &plugin
, -1);
807 if (!purple_plugin_is_loaded(plugin
))
810 /* Now show the pref-dialog for the plugin */
811 plugin_dialog_response_cb(dialog
, PIDGIN_RESPONSE_CONFIGURE
, sel
);
815 pidgin_plugins_paint_tooltip(GtkWidget
*tipwindow
, cairo_t
*cr
, gpointer data
)
817 PangoLayout
*layout
= g_object_get_data(G_OBJECT(tipwindow
), "tooltip-plugin");
818 GtkStyleContext
*context
= gtk_widget_get_style_context(tipwindow
);
819 gtk_style_context_add_class(context
, GTK_STYLE_CLASS_TOOLTIP
);
820 gtk_render_layout(context
, cr
, 6, 6, layout
);
825 pidgin_plugins_create_tooltip(GtkWidget
*tipwindow
, GtkTreePath
*path
,
826 gpointer data
, int *w
, int *h
)
829 GtkTreeView
*treeview
= GTK_TREE_VIEW(data
);
830 PurplePlugin
*plugin
= NULL
;
831 GPluginPluginInfo
*info
;
832 GtkTreeModel
*model
= gtk_tree_view_get_model(treeview
);
835 const char * const *authorlist
;
836 char *markup
, *name
, *desc
;
837 char *authors
= NULL
, *pauthors
= NULL
;
839 if (!gtk_tree_model_get_iter(model
, &iter
, path
))
842 gtk_tree_model_get(model
, &iter
, 2, &plugin
, -1);
843 info
= GPLUGIN_PLUGIN_INFO(purple_plugin_get_info(plugin
));
844 authorlist
= gplugin_plugin_info_get_authors(info
);
847 authors
= g_strjoinv(", ", (gchar
**)authorlist
);
849 pauthors
= g_markup_escape_text(authors
, -1);
851 markup
= g_strdup_printf(
852 "<span size='x-large' weight='bold'>%s</span>\n<b>%s:</b> "
854 name
= g_markup_escape_text(gplugin_plugin_info_get_name(info
), -1),
856 desc
= g_markup_escape_text(
857 gplugin_plugin_info_get_description(info
), -1),
858 (authorlist
&& g_strv_length((gchar
**)authorlist
) > 1
863 layout
= gtk_widget_create_pango_layout(tipwindow
, NULL
);
864 pango_layout_set_markup(layout
, markup
, -1);
865 pango_layout_set_wrap(layout
, PANGO_WRAP_WORD
);
866 pango_layout_set_width(layout
, 600000);
867 pango_layout_get_size(layout
, &width
, &height
);
868 g_object_set_data_full(G_OBJECT(tipwindow
), "tooltip-plugin", layout
, g_object_unref
);
871 *w
= PANGO_PIXELS(width
) + 12;
873 *h
= PANGO_PIXELS(height
) + 12;
885 website_button_motion_cb(GtkWidget
*button
, GdkEventCrossing
*event
,
888 if (plugin_website_uri
) {
889 pidgin_set_cursor(button
, GDK_HAND2
);
896 website_button_clicked_cb(GtkButton
*button
, GdkEventButton
*event
,
899 if (plugin_website_uri
) {
900 purple_notify_uri(NULL
, plugin_website_uri
);
909 GtkBox
*vbox
= GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL
, 3));
910 GtkSizeGroup
*sg
= gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL
);
911 GtkWidget
*label
, *view
, *website_button
;
913 plugin_name
= GTK_LABEL(gtk_label_new(NULL
));
914 gtk_label_set_xalign(plugin_name
, 0);
915 gtk_label_set_yalign(plugin_name
, 0);
916 gtk_label_set_line_wrap(plugin_name
, FALSE
);
917 gtk_label_set_selectable(plugin_name
, TRUE
);
918 gtk_box_pack_start(vbox
, GTK_WIDGET(plugin_name
), FALSE
, FALSE
, 0);
920 view
= gtk_text_view_new();
921 plugin_desc
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(view
));
922 g_object_set(view
, "wrap-mode", GTK_WRAP_WORD
,
924 "left-margin", PIDGIN_HIG_CAT_SPACE
,
925 "right-margin", PIDGIN_HIG_CAT_SPACE
,
927 gtk_box_pack_start(vbox
, view
, TRUE
, TRUE
, 0);
929 plugin_error
= GTK_LABEL(gtk_label_new(NULL
));
930 gtk_label_set_xalign(plugin_error
, 0);
931 gtk_label_set_yalign(plugin_error
, 0);
932 gtk_label_set_line_wrap(plugin_error
, FALSE
);
933 gtk_label_set_selectable(plugin_error
, TRUE
);
934 gtk_box_pack_start(vbox
, GTK_WIDGET(plugin_error
), FALSE
, FALSE
, 0);
936 plugin_authors
= GTK_LABEL(gtk_label_new(NULL
));
937 gtk_label_set_line_wrap(plugin_authors
, FALSE
);
938 gtk_label_set_xalign(plugin_authors
, 0);
939 gtk_label_set_yalign(plugin_authors
, 0);
940 gtk_label_set_selectable(plugin_authors
, TRUE
);
941 pidgin_add_widget_to_vbox(vbox
, "", sg
,
942 GTK_WIDGET(plugin_authors
), TRUE
, &label
);
943 gtk_label_set_markup(GTK_LABEL(label
), _("<b>Written by:</b>"));
944 gtk_label_set_xalign(GTK_LABEL(label
), 0);
945 gtk_label_set_yalign(GTK_LABEL(label
), 0);
947 website_button
= gtk_event_box_new();
948 gtk_event_box_set_visible_window(GTK_EVENT_BOX(website_button
), FALSE
);
950 plugin_website
= GTK_LABEL(gtk_label_new(NULL
));
951 g_object_set(G_OBJECT(plugin_website
),
952 "ellipsize", PANGO_ELLIPSIZE_MIDDLE
, NULL
);
953 gtk_label_set_xalign(plugin_website
, 0);
954 gtk_label_set_yalign(plugin_website
, 0);
955 gtk_container_add(GTK_CONTAINER(website_button
),
956 GTK_WIDGET(plugin_website
));
957 g_signal_connect(website_button
, "button-release-event",
958 G_CALLBACK(website_button_clicked_cb
), NULL
);
959 g_signal_connect(website_button
, "enter-notify-event",
960 G_CALLBACK(website_button_motion_cb
), NULL
);
961 g_signal_connect(website_button
, "leave-notify-event",
962 G_CALLBACK(pidgin_clear_cursor
), NULL
);
964 pidgin_add_widget_to_vbox(vbox
, "", sg
, website_button
, TRUE
, &label
);
965 gtk_label_set_markup(GTK_LABEL(label
), _("<b>Web site:</b>"));
966 gtk_label_set_xalign(GTK_LABEL(label
), 0);
968 plugin_filename
= GTK_LABEL(gtk_label_new(NULL
));
969 gtk_label_set_line_wrap(plugin_filename
, FALSE
);
970 gtk_label_set_xalign(plugin_filename
, 0);
971 gtk_label_set_yalign(plugin_filename
, 0);
972 gtk_label_set_selectable(plugin_filename
, TRUE
);
973 pidgin_add_widget_to_vbox(vbox
, "", sg
,
974 GTK_WIDGET(plugin_filename
), TRUE
, &label
);
975 gtk_label_set_markup(GTK_LABEL(label
), _("<b>Filename:</b>"));
976 gtk_label_set_xalign(GTK_LABEL(label
), 0);
977 gtk_label_set_yalign(GTK_LABEL(label
), 0);
981 return GTK_WIDGET(vbox
);
985 void pidgin_plugin_dialog_show()
987 GtkWidget
*event_view
;
989 GtkCellRenderer
*rend
, *rendt
;
990 GtkTreeViewColumn
*col
;
991 GtkTreeSelection
*sel
;
993 if (plugin_dialog
!= NULL
) {
994 gtk_window_present(GTK_WINDOW(plugin_dialog
));
998 plugin_dialog
= gtk_dialog_new();
999 gtk_window_set_title(GTK_WINDOW(plugin_dialog
), _("Plugins"));
1000 pref_button
= gtk_dialog_add_button(GTK_DIALOG(plugin_dialog
),
1001 _("Configure Pl_ugin"), PIDGIN_RESPONSE_CONFIGURE
);
1002 gtk_dialog_add_button(GTK_DIALOG(plugin_dialog
),
1003 GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
);
1004 gtk_widget_set_sensitive(pref_button
, FALSE
);
1005 gtk_window_set_role(GTK_WINDOW(plugin_dialog
), "plugins");
1007 ls
= gtk_list_store_new(4, G_TYPE_BOOLEAN
, G_TYPE_STRING
, G_TYPE_POINTER
, G_TYPE_BOOLEAN
);
1008 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(ls
),
1009 1, GTK_SORT_ASCENDING
);
1011 update_plugin_list(ls
);
1013 event_view
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(ls
));
1015 g_signal_connect(G_OBJECT(event_view
), "row-activated",
1016 G_CALLBACK(show_plugin_prefs_cb
), plugin_dialog
);
1018 purple_signal_connect(purple_plugins_get_handle(), "plugin-load", plugin_dialog
,
1019 PURPLE_CALLBACK(plugin_load_cb
), event_view
);
1020 purple_signal_connect(purple_plugins_get_handle(), "plugin-unload", plugin_dialog
,
1021 PURPLE_CALLBACK(plugin_unload_cb
), event_view
);
1023 rend
= gtk_cell_renderer_toggle_new();
1024 sel
= gtk_tree_view_get_selection (GTK_TREE_VIEW (event_view
));
1026 col
= gtk_tree_view_column_new_with_attributes (_("Enabled"),
1030 gtk_tree_view_append_column (GTK_TREE_VIEW(event_view
), col
);
1031 gtk_tree_view_column_set_sort_column_id(col
, 0);
1032 g_signal_connect(G_OBJECT(rend
), "toggled",
1033 G_CALLBACK(plugin_toggled
), ls
);
1035 rendt
= gtk_cell_renderer_text_new();
1037 "foreground", "#c0c0c0",
1039 col
= gtk_tree_view_column_new_with_attributes (_("Name"),
1042 "foreground-set", 3,
1044 gtk_tree_view_column_set_expand (col
, TRUE
);
1045 g_object_set(rendt
, "ellipsize", PANGO_ELLIPSIZE_END
, NULL
);
1046 gtk_tree_view_append_column (GTK_TREE_VIEW(event_view
), col
);
1047 gtk_tree_view_column_set_sort_column_id(col
, 1);
1048 g_object_unref(G_OBJECT(ls
));
1049 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(plugin_dialog
))),
1050 pidgin_make_scrollable(event_view
, GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
, GTK_SHADOW_IN
, -1, -1),
1052 gtk_tree_view_set_search_column(GTK_TREE_VIEW(event_view
), 1);
1053 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(event_view
),
1054 pidgin_tree_view_search_equal_func
, NULL
, NULL
);
1056 pidgin_tooltip_setup_for_treeview(event_view
, event_view
,
1057 pidgin_plugins_create_tooltip
,
1058 pidgin_plugins_paint_tooltip
);
1061 expander
= gtk_expander_new(_("<b>Plugin Details</b>"));
1062 gtk_expander_set_use_markup(GTK_EXPANDER(expander
), TRUE
);
1063 gtk_widget_set_sensitive(expander
, FALSE
);
1064 gtk_container_add(GTK_CONTAINER(expander
), create_details());
1065 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(plugin_dialog
))),
1066 expander
, FALSE
, FALSE
, 0);
1069 g_signal_connect (G_OBJECT (sel
), "changed", G_CALLBACK (prefs_plugin_sel
), NULL
);
1070 g_signal_connect(G_OBJECT(plugin_dialog
), "response", G_CALLBACK(plugin_dialog_response_cb
), sel
);
1071 gtk_window_set_default_size(GTK_WINDOW(plugin_dialog
), 430, 530);
1073 pidgin_auto_parent_window(plugin_dialog
);
1075 gtk_widget_show_all(plugin_dialog
);