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_PLUGIN_INFO_GET_PRIVATE(obj) \
36 (G_TYPE_INSTANCE_GET_PRIVATE((obj), PIDGIN_TYPE_PLUGIN_INFO, PidginPluginInfoPrivate))
38 #define PIDGIN_RESPONSE_CONFIGURE 98121
42 PidginPluginConfigFrameCb config_frame_cb
;
43 } PidginPluginInfoPrivate
;
48 PROP_GTK_CONFIG_FRAME_CB
,
56 PIDGIN_PLUGIN_UI_DATA_TYPE_FRAME
,
57 PIDGIN_PLUGIN_UI_DATA_TYPE_REQUEST
65 PurplePluginPrefFrame
*pref_frame
;
68 gpointer request_handle
;
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
= PIDGIN_PLUGIN_INFO_GET_PRIVATE(obj
);
96 case PROP_GTK_CONFIG_FRAME_CB
:
97 priv
->config_frame_cb
= g_value_get_pointer(value
);
100 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
105 /* Get method for GObject properties */
107 pidgin_plugin_info_get_property(GObject
*obj
, guint param_id
, GValue
*value
,
110 PidginPluginInfoPrivate
*priv
= PIDGIN_PLUGIN_INFO_GET_PRIVATE(obj
);
113 case PROP_GTK_CONFIG_FRAME_CB
:
114 g_value_set_pointer(value
, priv
->config_frame_cb
);
117 G_OBJECT_WARN_INVALID_PROPERTY_ID(obj
, param_id
, pspec
);
122 /* Class initializer function */
123 static void pidgin_plugin_info_class_init(PidginPluginInfoClass
*klass
)
125 GObjectClass
*obj_class
= G_OBJECT_CLASS(klass
);
127 g_type_class_add_private(klass
, sizeof(PidginPluginInfoPrivate
));
129 /* Setup properties */
130 obj_class
->get_property
= pidgin_plugin_info_get_property
;
131 obj_class
->set_property
= pidgin_plugin_info_set_property
;
133 g_object_class_install_property(obj_class
, PROP_GTK_CONFIG_FRAME_CB
,
134 g_param_spec_pointer("gtk-config-frame-cb",
135 "GTK configuration frame callback",
136 "Callback that returns a GTK configuration frame",
137 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
|
138 G_PARAM_STATIC_STRINGS
));
142 pidgin_plugin_info_get_type(void)
144 static GType type
= 0;
146 if (G_UNLIKELY(type
== 0)) {
147 static const GTypeInfo info
= {
148 .class_size
= sizeof(PidginPluginInfoClass
),
149 .class_init
= (GClassInitFunc
)pidgin_plugin_info_class_init
,
150 .instance_size
= sizeof(PidginPluginInfo
),
153 type
= g_type_register_static(PURPLE_TYPE_PLUGIN_INFO
,
154 "PidginPluginInfo", &info
, 0);
161 pidgin_plugin_info_new(const char *first_property
, ...)
166 /* at least ID is required */
170 va_start(var_args
, first_property
);
171 info
= g_object_new_valist(PIDGIN_TYPE_PLUGIN_INFO
, first_property
,
175 g_object_set(info
, "ui-requirement", PIDGIN_UI
, NULL
);
177 return PIDGIN_PLUGIN_INFO(info
);
181 pidgin_plugin_has_prefs(PurplePlugin
*plugin
)
183 PurplePluginInfo
*info
= purple_plugin_get_info(plugin
);
184 PidginPluginInfoPrivate
*priv
= NULL
;
187 g_return_val_if_fail(plugin
!= NULL
, FALSE
);
189 if (!purple_plugin_is_loaded(plugin
))
192 if (PIDGIN_IS_PLUGIN_INFO(info
))
193 priv
= PIDGIN_PLUGIN_INFO_GET_PRIVATE(info
);
195 ret
= ((priv
&& priv
->config_frame_cb
) ||
196 purple_plugin_info_get_pref_frame_cb(info
) ||
197 purple_plugin_info_get_pref_request_cb(info
));
203 pidgin_plugin_get_config_frame(PurplePlugin
*plugin
,
204 PurplePluginPrefFrame
**purple_pref_frame
)
206 GtkWidget
*config
= NULL
;
207 PurplePluginInfo
*info
;
208 PurplePluginPrefFrameCb pref_frame_cb
= NULL
;
210 g_return_val_if_fail(PURPLE_IS_PLUGIN(plugin
), NULL
);
212 info
= purple_plugin_get_info(plugin
);
213 if(!PURPLE_IS_PLUGIN_INFO(info
))
216 pref_frame_cb
= purple_plugin_info_get_pref_frame_cb(info
);
218 PurplePluginPrefFrame
*frame
= pref_frame_cb(plugin
);
221 config
= pidgin_plugin_pref_create_frame(frame
);
223 *purple_pref_frame
= frame
;
231 pref_dialog_close(PurplePlugin
*plugin
)
233 PurplePluginInfo
*info
;
234 PidginPluginUiData
*ui_data
;
236 g_return_if_fail(plugin
!= NULL
);
238 info
= purple_plugin_get_info(plugin
);
240 ui_data
= purple_plugin_info_get_ui_data(info
);
244 if (ui_data
->type
== PIDGIN_PLUGIN_UI_DATA_TYPE_REQUEST
) {
245 purple_request_close(PURPLE_REQUEST_FIELDS
,
246 ui_data
->u
.request_handle
);
250 g_return_if_fail(ui_data
->type
== PIDGIN_PLUGIN_UI_DATA_TYPE_FRAME
);
252 gtk_widget_destroy(ui_data
->u
.frame
.dialog
);
254 if (ui_data
->u
.frame
.pref_frame
)
255 purple_plugin_pref_frame_destroy(ui_data
->u
.frame
.pref_frame
);
258 purple_plugin_info_set_ui_data(info
, NULL
);
263 pref_dialog_response_cb(GtkWidget
*dialog
, int response
, PurplePlugin
*plugin
)
265 if (response
== GTK_RESPONSE_CLOSE
||
266 response
== GTK_RESPONSE_DELETE_EVENT
)
268 pref_dialog_close(plugin
);
273 pidgin_plugin_open_config(PurplePlugin
*plugin
, GtkWindow
*parent
)
275 PurplePluginInfo
*info
;
276 PidginPluginInfoPrivate
*priv
= NULL
;
277 PidginPluginUiData
*ui_data
;
278 PurplePluginPrefFrameCb pref_frame_cb
;
279 PurplePluginPrefRequestCb pref_request_cb
;
280 PidginPluginConfigFrameCb get_pidgin_frame
= NULL
;
283 g_return_if_fail(plugin
!= NULL
);
285 info
= purple_plugin_get_info(plugin
);
287 if (!pidgin_plugin_has_prefs(plugin
)) {
288 purple_debug_warning("gtkplugin", "Plugin has no prefs");
292 if (purple_plugin_info_get_ui_data(info
))
295 if (PIDGIN_IS_PLUGIN_INFO(info
))
296 priv
= PIDGIN_PLUGIN_INFO_GET_PRIVATE(info
);
298 pref_frame_cb
= purple_plugin_info_get_pref_frame_cb(info
);
299 pref_request_cb
= purple_plugin_info_get_pref_request_cb(info
);
302 get_pidgin_frame
= priv
->config_frame_cb
;
309 if (get_pidgin_frame
)
312 if (prefs_count
> 1) {
313 purple_debug_warning("gtkplugin", "Plugin %s contains more than"
314 " one prefs callback, some will be ignored.",
315 purple_plugin_info_get_name(info
));
317 g_return_if_fail(prefs_count
> 0);
319 ui_data
= g_new0(PidginPluginUiData
, 1);
320 purple_plugin_info_set_ui_data(info
, ui_data
);
322 /* Priority: pidgin frame > purple request > purple frame
323 * Purple frame could be replaced with purple request some day.
325 if (pref_request_cb
&& !get_pidgin_frame
) {
326 ui_data
->type
= PIDGIN_PLUGIN_UI_DATA_TYPE_REQUEST
;
327 ui_data
->u
.request_handle
= pref_request_cb(plugin
);
328 purple_request_add_close_notify(ui_data
->u
.request_handle
,
329 purple_callback_set_zero
, &info
->ui_data
);
330 purple_request_add_close_notify(ui_data
->u
.request_handle
,
333 GtkWidget
*box
, *dialog
;
335 ui_data
->type
= PIDGIN_PLUGIN_UI_DATA_TYPE_FRAME
;
337 box
= pidgin_plugin_get_config_frame(plugin
,
338 &ui_data
->u
.frame
.pref_frame
);
340 purple_debug_error("gtkplugin",
341 "Failed to display prefs frame");
343 purple_plugin_info_set_ui_data(info
, NULL
);
346 gtk_widget_set_vexpand(box
, TRUE
);
348 ui_data
->u
.frame
.dialog
= dialog
= gtk_dialog_new_with_buttons(
349 PIDGIN_ALERT_TITLE
, parent
,
350 GTK_DIALOG_DESTROY_WITH_PARENT
, GTK_STOCK_CLOSE
,
351 GTK_RESPONSE_CLOSE
, NULL
);
353 g_signal_connect(G_OBJECT(dialog
), "response",
354 G_CALLBACK(pref_dialog_response_cb
), plugin
);
356 gtk_container_add(GTK_CONTAINER(
357 gtk_dialog_get_content_area(GTK_DIALOG(dialog
))),
358 pidgin_make_scrollable(box
, GTK_POLICY_NEVER
,
359 GTK_POLICY_AUTOMATIC
, GTK_SHADOW_IN
, 400, 400));
361 gtk_window_set_role(GTK_WINDOW(dialog
), "plugin_config");
362 gtk_window_set_title(GTK_WINDOW(dialog
),
363 _(purple_plugin_info_get_name(info
)));
364 gtk_widget_show_all(dialog
);
369 pidgin_plugins_save(void)
371 purple_plugins_save_loaded(PIDGIN_PREFS_ROOT
"/plugins/loaded");
375 update_plugin_list(void *data
)
377 GtkListStore
*ls
= GTK_LIST_STORE(data
);
381 PurplePluginInfo
*info
;
383 gtk_list_store_clear(ls
);
384 purple_plugins_refresh();
386 plugins
= purple_plugins_find_all();
388 for (l
= plugins
; l
!= NULL
; l
= l
->next
)
394 plug
= PURPLE_PLUGIN(l
->data
);
395 info
= purple_plugin_get_info(plug
);
397 if (purple_plugin_is_internal(plug
))
400 gtk_list_store_append (ls
, &iter
);
402 if (purple_plugin_info_get_name(info
)) {
403 name
= g_markup_escape_text(_(purple_plugin_info_get_name(info
)), -1);
405 char *tmp
= g_path_get_basename(purple_plugin_get_filename(plug
));
406 name
= g_markup_escape_text(tmp
, -1);
409 version
= g_markup_escape_text(purple_plugin_info_get_version(info
), -1);
410 summary
= g_markup_escape_text(purple_plugin_info_get_summary(info
), -1);
412 desc
= g_strdup_printf("<b>%s</b> %s\n%s", name
,
419 gtk_list_store_set(ls
, &iter
,
420 0, purple_plugin_is_loaded(plug
),
423 3, purple_plugin_info_get_error(info
),
428 g_list_free(plugins
);
432 check_if_loaded(GtkTreeModel
*model
, GtkTreePath
*path
, GtkTreeIter
*iter
, gpointer data
)
434 PurplePlugin
*plugin
;
435 gtk_tree_model_get(model
, iter
, 2, &plugin
, -1);
436 gtk_list_store_set(GTK_LIST_STORE(model
), iter
,
437 0, purple_plugin_is_loaded(plugin
),
443 update_loaded_plugins(GtkTreeModel
*model
)
445 gtk_tree_model_foreach(model
, check_if_loaded
, NULL
);
448 static void plugin_loading_common(PurplePlugin
*plugin
, GtkTreeView
*view
, gboolean loaded
)
451 GtkTreeModel
*model
= gtk_tree_view_get_model(view
);
453 if (gtk_tree_model_get_iter_first(model
, &iter
)) {
456 GtkTreeSelection
*sel
;
458 gtk_tree_model_get(model
, &iter
, 2, &plug
, -1);
463 gtk_list_store_set(GTK_LIST_STORE(model
), &iter
, 0, loaded
, -1);
465 /* If the loaded/unloaded plugin is the selected row,
466 * update the pref_button. */
467 sel
= gtk_tree_view_get_selection(view
);
468 if (gtk_tree_selection_get_selected(sel
, &model
, &iter
))
470 gtk_tree_model_get(model
, &iter
, 2, &plug
, -1);
473 gtk_widget_set_sensitive(pref_button
,
474 pidgin_plugin_has_prefs(plug
));
479 } while (gtk_tree_model_iter_next(model
, &iter
));
483 static void plugin_load_cb(PurplePlugin
*plugin
, gpointer data
)
485 GtkTreeView
*view
= (GtkTreeView
*)data
;
486 plugin_loading_common(plugin
, view
, TRUE
);
489 static void plugin_unload_cb(PurplePlugin
*plugin
, gpointer data
)
491 GtkTreeView
*view
= (GtkTreeView
*)data
;
492 plugin_loading_common(plugin
, view
, FALSE
);
495 static void plugin_unload_confirm_cb(gpointer
*data
)
497 PurplePlugin
*plugin
= (PurplePlugin
*)data
[0];
498 GtkTreeModel
*model
= (GtkTreeModel
*)data
[1];
499 GtkTreeIter
*iter
= (GtkTreeIter
*)data
[2];
501 plugin_toggled_stage_two(plugin
, model
, iter
, NULL
, TRUE
);
506 static void plugin_toggled(GtkCellRendererToggle
*cell
, gchar
*pth
, gpointer data
)
508 GtkTreeModel
*model
= (GtkTreeModel
*)data
;
509 GtkTreeIter
*iter
= g_new(GtkTreeIter
, 1);
510 GtkTreePath
*path
= gtk_tree_path_new_from_string(pth
);
512 GError
*error
= NULL
;
514 gtk_tree_model_get_iter(model
, iter
, path
);
515 gtk_tree_path_free(path
);
516 gtk_tree_model_get(model
, iter
, 2, &plug
, -1);
518 if (!purple_plugin_is_loaded(plug
))
520 pidgin_set_cursor(plugin_dialog
, GDK_WATCH
);
522 purple_plugin_load(plug
, &error
);
523 plugin_toggled_stage_two(plug
, model
, iter
, error
, FALSE
);
525 pidgin_clear_cursor(plugin_dialog
);
529 pref_dialog_close(plug
);
531 if (purple_plugin_get_dependent_plugins(plug
) != NULL
)
533 GString
*tmp
= g_string_new(_("The following plugins will be unloaded."));
537 for (l
= purple_plugin_get_dependent_plugins(plug
); l
!= NULL
; l
= l
->next
)
539 const char *dep_name
= (const char *)l
->data
;
540 PurplePlugin
*dep_plugin
= purple_plugins_find_plugin(dep_name
);
541 PurplePluginInfo
*dep_info
;
543 g_return_if_fail(dep_plugin
!= NULL
);
545 dep_info
= purple_plugin_get_info(dep_plugin
);
546 g_string_append_printf(tmp
, "\n\t%s\n", purple_plugin_info_get_name(dep_info
));
549 cb_data
= g_new(gpointer
, 3);
554 purple_request_action(plugin_dialog
, NULL
,
555 _("Multiple plugins will be unloaded."),
556 tmp
->str
, 0, NULL
, cb_data
, 2,
558 G_CALLBACK(plugin_unload_confirm_cb
),
559 _("Cancel"), g_free
);
560 g_string_free(tmp
, TRUE
);
563 plugin_toggled_stage_two(plug
, model
, iter
, NULL
, TRUE
);
567 static void plugin_toggled_stage_two(PurplePlugin
*plug
, GtkTreeModel
*model
, GtkTreeIter
*iter
, GError
*error
, gboolean unload
)
569 PurplePluginInfo
*info
= purple_plugin_get_info(plug
);
573 pidgin_set_cursor(plugin_dialog
, GDK_WATCH
);
575 if (!purple_plugin_unload(plug
, &error
))
577 const char *primary
= _("Could not unload plugin");
578 const char *reload
= _("The plugin could not be unloaded now, but will be disabled at the next startup.");
580 char *tmp
= g_strdup_printf("%s\n\n%s", reload
, error
->message
);
581 purple_notify_warning(NULL
, NULL
, primary
, tmp
, NULL
);
584 purple_plugin_disable(plug
);
587 pidgin_clear_cursor(plugin_dialog
);
589 else if (!unload
&& error
)
591 purple_notify_warning(NULL
, NULL
, _("Could not load plugin"), error
->message
, NULL
);
594 gtk_widget_set_sensitive(pref_button
, pidgin_plugin_has_prefs(plug
));
598 gchar
*name
= g_markup_escape_text(purple_plugin_info_get_name(info
), -1);
600 gchar
*disp_error
= g_markup_escape_text(error
->message
, -1);
603 text
= g_strdup_printf(
604 "<b>%s</b> %s\n<span weight=\"bold\" color=\"red\"%s</span>",
605 purple_plugin_info_get_name(info
),
606 purple_plugin_info_get_version(info
), disp_error
);
607 gtk_list_store_set(GTK_LIST_STORE (model
), iter
,
612 text
= g_strdup_printf(
613 "<span weight=\"bold\" color=\"red\">%s</span>",
615 gtk_label_set_markup(plugin_error
, text
);
624 if ((unload
&& purple_plugin_get_dependent_plugins(plug
)) ||
625 (!unload
&& purple_plugin_info_get_dependencies(info
)))
626 update_loaded_plugins(model
);
628 gtk_list_store_set(GTK_LIST_STORE (model
), iter
,
629 0, purple_plugin_is_loaded(plug
),
633 pidgin_plugins_save();
636 static gboolean
ensure_plugin_visible(void *data
)
638 GtkTreeSelection
*sel
= GTK_TREE_SELECTION(data
);
639 GtkTreeView
*tv
= gtk_tree_selection_get_tree_view(sel
);
640 GtkTreeModel
*model
= gtk_tree_view_get_model(tv
);
643 if (!gtk_tree_selection_get_selected (sel
, &model
, &iter
))
645 path
= gtk_tree_model_get_path(model
, &iter
);
646 gtk_tree_view_scroll_to_cell(gtk_tree_selection_get_tree_view(sel
), path
, NULL
, FALSE
, 0, 0);
647 gtk_tree_path_free(path
);
651 static void prefs_plugin_sel (GtkTreeSelection
*sel
, GtkTreeModel
*model
)
653 gchar
*buf
, *tmp
, *name
, *version
;
654 gchar
*authors
= NULL
;
655 const gchar
* const *authorlist
;
659 PurplePluginInfo
*info
;
661 if (!gtk_tree_selection_get_selected (sel
, &model
, &iter
))
663 gtk_widget_set_sensitive(pref_button
, FALSE
);
665 /* Collapse and disable the expander widget */
666 gtk_expander_set_expanded(GTK_EXPANDER(expander
), FALSE
);
667 gtk_widget_set_sensitive(expander
, FALSE
);
672 gtk_widget_set_sensitive(expander
, TRUE
);
675 gtk_tree_model_get_value (model
, &iter
, 2, &val
);
676 plug
= g_value_get_pointer(&val
);
677 info
= purple_plugin_get_info(plug
);
679 name
= g_markup_escape_text(purple_plugin_info_get_name(info
), -1);
680 version
= g_markup_escape_text(purple_plugin_info_get_version(info
), -1);
681 buf
= g_strdup_printf(
682 "<span size=\"larger\" weight=\"bold\">%s</span> "
683 "<span size=\"smaller\">%s</span>",
685 gtk_label_set_markup(plugin_name
, buf
);
690 gtk_text_buffer_set_text(plugin_desc
, purple_plugin_info_get_description(info
), -1);
692 authorlist
= purple_plugin_info_get_authors(info
);
694 authors
= g_strjoinv(",\n", (gchar
**)authorlist
);
695 gtk_label_set_text(plugin_authors
, authors
);
698 gtk_label_set_text(plugin_filename
, purple_plugin_get_filename(plug
));
700 g_free(plugin_website_uri
);
701 plugin_website_uri
= g_strdup(purple_plugin_info_get_website(info
));
703 if (plugin_website_uri
)
705 tmp
= g_markup_escape_text(plugin_website_uri
, -1);
706 buf
= g_strdup_printf("<span underline=\"single\" "
707 "foreground=\"blue\">%s</span>", tmp
);
708 gtk_label_set_markup(plugin_website
, buf
);
714 gtk_label_set_text(plugin_website
, NULL
);
717 if (purple_plugin_info_get_error(info
) == NULL
)
719 gtk_label_set_text(plugin_error
, NULL
);
723 tmp
= g_markup_escape_text(purple_plugin_info_get_error(info
), -1);
724 buf
= g_strdup_printf(
725 _("<span foreground=\"red\" weight=\"bold\">"
727 "Check the plugin website for an update."
730 gtk_label_set_markup(plugin_error
, buf
);
735 gtk_widget_set_sensitive(pref_button
, pidgin_plugin_has_prefs(plug
));
737 /* Make sure the selected plugin is still visible */
738 g_idle_add(ensure_plugin_visible
, sel
);
743 static void plugin_dialog_response_cb(GtkWidget
*d
, int response
, GtkTreeSelection
*sel
)
745 PurplePlugin
*plugin
;
751 g_return_if_fail(d
!= NULL
);
754 case GTK_RESPONSE_CLOSE
:
755 case GTK_RESPONSE_DELETE_EVENT
:
756 purple_request_close_with_handle(plugin_dialog
);
757 purple_signals_disconnect_by_handle(plugin_dialog
);
758 list
= purple_plugins_find_all();
759 for (it
= list
; it
; it
= g_list_next(it
))
760 pref_dialog_close(it
->data
);
762 gtk_widget_destroy(d
);
763 plugin_dialog
= NULL
;
765 case PIDGIN_RESPONSE_CONFIGURE
:
766 if (! gtk_tree_selection_get_selected (sel
, &model
, &iter
))
769 gtk_tree_model_get_value(model
, &iter
, 2, &val
);
770 plugin
= g_value_get_pointer(&val
);
775 pidgin_plugin_open_config(plugin
, GTK_WINDOW(d
));
782 show_plugin_prefs_cb(GtkTreeView
*view
, GtkTreePath
*path
, GtkTreeViewColumn
*column
, GtkWidget
*dialog
)
784 GtkTreeSelection
*sel
;
786 PurplePlugin
*plugin
;
789 sel
= gtk_tree_view_get_selection(view
);
791 if (!gtk_tree_selection_get_selected(sel
, &model
, &iter
))
794 gtk_tree_model_get(model
, &iter
, 2, &plugin
, -1);
796 if (!purple_plugin_is_loaded(plugin
))
799 /* Now show the pref-dialog for the plugin */
800 plugin_dialog_response_cb(dialog
, PIDGIN_RESPONSE_CONFIGURE
, sel
);
804 pidgin_plugins_paint_tooltip(GtkWidget
*tipwindow
, cairo_t
*cr
, gpointer data
)
806 PangoLayout
*layout
= g_object_get_data(G_OBJECT(tipwindow
), "tooltip-plugin");
807 GtkStyleContext
*context
= gtk_widget_get_style_context(tipwindow
);
808 gtk_style_context_add_class(context
, GTK_STYLE_CLASS_TOOLTIP
);
809 gtk_render_layout(context
, cr
, 6, 6, layout
);
814 pidgin_plugins_create_tooltip(GtkWidget
*tipwindow
, GtkTreePath
*path
,
815 gpointer data
, int *w
, int *h
)
818 GtkTreeView
*treeview
= GTK_TREE_VIEW(data
);
819 PurplePlugin
*plugin
= NULL
;
820 PurplePluginInfo
*info
;
821 GtkTreeModel
*model
= gtk_tree_view_get_model(treeview
);
824 const char * const *authorlist
;
825 char *markup
, *name
, *desc
;
826 char *authors
= NULL
, *pauthors
= NULL
;
828 if (!gtk_tree_model_get_iter(model
, &iter
, path
))
831 gtk_tree_model_get(model
, &iter
, 2, &plugin
, -1);
832 info
= purple_plugin_get_info(plugin
);
833 authorlist
= purple_plugin_info_get_authors(info
);
836 authors
= g_strjoinv(", ", (gchar
**)authorlist
);
838 pauthors
= g_markup_escape_text(authors
, -1);
840 markup
= g_strdup_printf("<span size='x-large' weight='bold'>%s</span>\n<b>%s:</b> %s\n<b>%s:</b> %s",
841 name
= g_markup_escape_text(purple_plugin_info_get_name(info
), -1),
842 _("Description"), desc
= g_markup_escape_text(purple_plugin_info_get_description(info
), -1),
843 (g_strv_length((gchar
**)authorlist
) > 1 ? _("Authors") : _("Author")), pauthors
);
845 layout
= gtk_widget_create_pango_layout(tipwindow
, NULL
);
846 pango_layout_set_markup(layout
, markup
, -1);
847 pango_layout_set_wrap(layout
, PANGO_WRAP_WORD
);
848 pango_layout_set_width(layout
, 600000);
849 pango_layout_get_size(layout
, &width
, &height
);
850 g_object_set_data_full(G_OBJECT(tipwindow
), "tooltip-plugin", layout
, g_object_unref
);
853 *w
= PANGO_PIXELS(width
) + 12;
855 *h
= PANGO_PIXELS(height
) + 12;
867 website_button_motion_cb(GtkWidget
*button
, GdkEventCrossing
*event
,
870 if (plugin_website_uri
) {
871 pidgin_set_cursor(button
, GDK_HAND2
);
878 website_button_clicked_cb(GtkButton
*button
, GdkEventButton
*event
,
881 if (plugin_website_uri
) {
882 purple_notify_uri(NULL
, plugin_website_uri
);
891 GtkBox
*vbox
= GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL
, 3));
892 GtkSizeGroup
*sg
= gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL
);
893 GtkWidget
*label
, *view
, *website_button
;
895 plugin_name
= GTK_LABEL(gtk_label_new(NULL
));
896 gtk_label_set_xalign(plugin_name
, 0);
897 gtk_label_set_yalign(plugin_name
, 0);
898 gtk_label_set_line_wrap(plugin_name
, FALSE
);
899 gtk_label_set_selectable(plugin_name
, TRUE
);
900 gtk_box_pack_start(vbox
, GTK_WIDGET(plugin_name
), FALSE
, FALSE
, 0);
902 view
= gtk_text_view_new();
903 plugin_desc
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(view
));
904 g_object_set(view
, "wrap-mode", GTK_WRAP_WORD
,
906 "left-margin", PIDGIN_HIG_CAT_SPACE
,
907 "right-margin", PIDGIN_HIG_CAT_SPACE
,
909 gtk_box_pack_start(vbox
, view
, TRUE
, TRUE
, 0);
911 plugin_error
= GTK_LABEL(gtk_label_new(NULL
));
912 gtk_label_set_xalign(plugin_error
, 0);
913 gtk_label_set_yalign(plugin_error
, 0);
914 gtk_label_set_line_wrap(plugin_error
, FALSE
);
915 gtk_label_set_selectable(plugin_error
, TRUE
);
916 gtk_box_pack_start(vbox
, GTK_WIDGET(plugin_error
), FALSE
, FALSE
, 0);
918 plugin_authors
= GTK_LABEL(gtk_label_new(NULL
));
919 gtk_label_set_line_wrap(plugin_authors
, FALSE
);
920 gtk_label_set_xalign(plugin_authors
, 0);
921 gtk_label_set_yalign(plugin_authors
, 0);
922 gtk_label_set_selectable(plugin_authors
, TRUE
);
923 pidgin_add_widget_to_vbox(vbox
, "", sg
,
924 GTK_WIDGET(plugin_authors
), TRUE
, &label
);
925 gtk_label_set_markup(GTK_LABEL(label
), _("<b>Written by:</b>"));
926 gtk_label_set_xalign(GTK_LABEL(label
), 0);
927 gtk_label_set_yalign(GTK_LABEL(label
), 0);
929 website_button
= gtk_event_box_new();
930 gtk_event_box_set_visible_window(GTK_EVENT_BOX(website_button
), FALSE
);
932 plugin_website
= GTK_LABEL(gtk_label_new(NULL
));
933 g_object_set(G_OBJECT(plugin_website
),
934 "ellipsize", PANGO_ELLIPSIZE_MIDDLE
, NULL
);
935 gtk_label_set_xalign(plugin_website
, 0);
936 gtk_label_set_yalign(plugin_website
, 0);
937 gtk_container_add(GTK_CONTAINER(website_button
),
938 GTK_WIDGET(plugin_website
));
939 g_signal_connect(website_button
, "button-release-event",
940 G_CALLBACK(website_button_clicked_cb
), NULL
);
941 g_signal_connect(website_button
, "enter-notify-event",
942 G_CALLBACK(website_button_motion_cb
), NULL
);
943 g_signal_connect(website_button
, "leave-notify-event",
944 G_CALLBACK(pidgin_clear_cursor
), NULL
);
946 pidgin_add_widget_to_vbox(vbox
, "", sg
, website_button
, TRUE
, &label
);
947 gtk_label_set_markup(GTK_LABEL(label
), _("<b>Web site:</b>"));
948 gtk_label_set_xalign(GTK_LABEL(label
), 0);
950 plugin_filename
= GTK_LABEL(gtk_label_new(NULL
));
951 gtk_label_set_line_wrap(plugin_filename
, FALSE
);
952 gtk_label_set_xalign(plugin_filename
, 0);
953 gtk_label_set_yalign(plugin_filename
, 0);
954 gtk_label_set_selectable(plugin_filename
, TRUE
);
955 pidgin_add_widget_to_vbox(vbox
, "", sg
,
956 GTK_WIDGET(plugin_filename
), TRUE
, &label
);
957 gtk_label_set_markup(GTK_LABEL(label
), _("<b>Filename:</b>"));
958 gtk_label_set_xalign(GTK_LABEL(label
), 0);
959 gtk_label_set_yalign(GTK_LABEL(label
), 0);
963 return GTK_WIDGET(vbox
);
967 void pidgin_plugin_dialog_show()
969 GtkWidget
*event_view
;
971 GtkCellRenderer
*rend
, *rendt
;
972 GtkTreeViewColumn
*col
;
973 GtkTreeSelection
*sel
;
975 if (plugin_dialog
!= NULL
) {
976 gtk_window_present(GTK_WINDOW(plugin_dialog
));
980 plugin_dialog
= gtk_dialog_new();
981 gtk_window_set_title(GTK_WINDOW(plugin_dialog
), _("Plugins"));
982 pref_button
= gtk_dialog_add_button(GTK_DIALOG(plugin_dialog
),
983 _("Configure Pl_ugin"), PIDGIN_RESPONSE_CONFIGURE
);
984 gtk_dialog_add_button(GTK_DIALOG(plugin_dialog
),
985 GTK_STOCK_CLOSE
, GTK_RESPONSE_CLOSE
);
986 gtk_widget_set_sensitive(pref_button
, FALSE
);
987 gtk_window_set_role(GTK_WINDOW(plugin_dialog
), "plugins");
989 ls
= gtk_list_store_new(4, G_TYPE_BOOLEAN
, G_TYPE_STRING
, G_TYPE_POINTER
, G_TYPE_BOOLEAN
);
990 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(ls
),
991 1, GTK_SORT_ASCENDING
);
993 update_plugin_list(ls
);
995 event_view
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(ls
));
997 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(event_view
), TRUE
);
999 g_signal_connect(G_OBJECT(event_view
), "row-activated",
1000 G_CALLBACK(show_plugin_prefs_cb
), plugin_dialog
);
1002 purple_signal_connect(purple_plugins_get_handle(), "plugin-load", plugin_dialog
,
1003 PURPLE_CALLBACK(plugin_load_cb
), event_view
);
1004 purple_signal_connect(purple_plugins_get_handle(), "plugin-unload", plugin_dialog
,
1005 PURPLE_CALLBACK(plugin_unload_cb
), event_view
);
1007 rend
= gtk_cell_renderer_toggle_new();
1008 sel
= gtk_tree_view_get_selection (GTK_TREE_VIEW (event_view
));
1010 col
= gtk_tree_view_column_new_with_attributes (_("Enabled"),
1014 gtk_tree_view_append_column (GTK_TREE_VIEW(event_view
), col
);
1015 gtk_tree_view_column_set_sort_column_id(col
, 0);
1016 g_signal_connect(G_OBJECT(rend
), "toggled",
1017 G_CALLBACK(plugin_toggled
), ls
);
1019 rendt
= gtk_cell_renderer_text_new();
1021 "foreground", "#c0c0c0",
1023 col
= gtk_tree_view_column_new_with_attributes (_("Name"),
1026 "foreground-set", 3,
1028 gtk_tree_view_column_set_expand (col
, TRUE
);
1029 g_object_set(rendt
, "ellipsize", PANGO_ELLIPSIZE_END
, NULL
);
1030 gtk_tree_view_append_column (GTK_TREE_VIEW(event_view
), col
);
1031 gtk_tree_view_column_set_sort_column_id(col
, 1);
1032 g_object_unref(G_OBJECT(ls
));
1033 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(plugin_dialog
))),
1034 pidgin_make_scrollable(event_view
, GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
, GTK_SHADOW_IN
, -1, -1),
1036 gtk_tree_view_set_search_column(GTK_TREE_VIEW(event_view
), 1);
1037 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(event_view
),
1038 pidgin_tree_view_search_equal_func
, NULL
, NULL
);
1040 pidgin_tooltip_setup_for_treeview(event_view
, event_view
,
1041 pidgin_plugins_create_tooltip
,
1042 pidgin_plugins_paint_tooltip
);
1045 expander
= gtk_expander_new(_("<b>Plugin Details</b>"));
1046 gtk_expander_set_use_markup(GTK_EXPANDER(expander
), TRUE
);
1047 gtk_widget_set_sensitive(expander
, FALSE
);
1048 gtk_container_add(GTK_CONTAINER(expander
), create_details());
1049 gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(plugin_dialog
))),
1050 expander
, FALSE
, FALSE
, 0);
1053 g_signal_connect (G_OBJECT (sel
), "changed", G_CALLBACK (prefs_plugin_sel
), NULL
);
1054 g_signal_connect(G_OBJECT(plugin_dialog
), "response", G_CALLBACK(plugin_dialog_response_cb
), sel
);
1055 gtk_window_set_default_size(GTK_WINDOW(plugin_dialog
), 430, 530);
1057 pidgin_auto_parent_window(plugin_dialog
);
1059 gtk_widget_show_all(plugin_dialog
);