2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2024 the Claws Mail Team and Hiroyuki Yamamoto
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "claws-features.h"
25 #include <glib/gi18n.h>
29 #include <gdk/gdkkeysyms.h>
35 #include "alertpanel.h"
36 #include "prefs_common.h"
39 #include "manage_window.h"
42 PLUGINWINDOW_NAME
, /*<! plugin name */
43 PLUGINWINDOW_DATA
, /*<! Plugin pointer */
44 PLUGINWINDOW_STYLE
, /*<! italic if error */
45 N_PLUGINWINDOW_COLUMNS
48 typedef struct _PluginWindow
51 GtkWidget
*plugin_list_view
;
52 GtkWidget
*plugin_desc
;
53 GtkWidget
*unload_btn
;
55 Plugin
*selected_plugin
;
60 static GtkListStore
* pluginwindow_create_data_store (void);
61 static GtkWidget
*pluginwindow_list_view_create (PluginWindow
*pluginwindow
);
62 static void pluginwindow_create_list_view_columns (GtkWidget
*list_view
);
63 static gboolean
pluginwindow_selected (GtkTreeSelection
*selector
,
66 gboolean currently_selected
,
69 static void close_cb(GtkButton
*button
, PluginWindow
*pluginwindow
)
71 if (pluginwindow
->loading
)
73 gtk_widget_destroy(pluginwindow
->window
);
79 static gint
pluginwindow_delete_cb(GtkWidget
*widget
, GdkEventAny
*event
,
80 PluginWindow
*pluginwindow
)
82 if (pluginwindow
->loading
)
84 close_cb(NULL
,pluginwindow
);
88 static void set_plugin_list(PluginWindow
*pluginwindow
)
90 GSList
*plugins
, *cur
, *unloaded
;
94 GtkTextBuffer
*textbuf
;
95 GtkTextIter start_iter
, end_iter
;
96 GtkTreeSelection
*selection
;
98 plugins
= plugin_get_list();
99 unloaded
= plugin_get_unloaded_list();
101 store
= GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW
102 (pluginwindow
->plugin_list_view
)));
103 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(store
),
104 0, GTK_SORT_ASCENDING
);
105 gtk_list_store_clear(store
);
107 textbuf
= gtk_text_view_get_buffer(GTK_TEXT_VIEW(pluginwindow
->plugin_desc
));
108 gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(pluginwindow
->plugin_desc
), FALSE
);
109 gtk_text_view_set_editable(GTK_TEXT_VIEW(pluginwindow
->plugin_desc
), FALSE
);
110 gtk_text_buffer_get_start_iter(textbuf
, &start_iter
);
111 gtk_text_buffer_get_end_iter(textbuf
, &end_iter
);
112 gtk_text_buffer_delete(textbuf
, &start_iter
, &end_iter
);
113 gtk_widget_set_sensitive(pluginwindow
->unload_btn
, FALSE
);
115 for(cur
= plugins
; cur
!= NULL
; cur
= g_slist_next(cur
)) {
116 Plugin
*plugin
= (Plugin
*) cur
->data
;
118 gtk_list_store_append(store
, &iter
);
119 text
= plugin_get_name(plugin
);
120 gtk_list_store_set(store
, &iter
,
121 PLUGINWINDOW_NAME
, text
,
122 PLUGINWINDOW_DATA
, plugin
,
123 PLUGINWINDOW_STYLE
, PANGO_STYLE_NORMAL
,
127 for(cur
= unloaded
; cur
!= NULL
; cur
= g_slist_next(cur
)) {
128 Plugin
*plugin
= (Plugin
*) cur
->data
;
130 gtk_list_store_append(store
, &iter
);
131 text
= plugin_get_name(plugin
);
132 gtk_list_store_set(store
, &iter
,
133 PLUGINWINDOW_NAME
, text
,
134 PLUGINWINDOW_DATA
, plugin
,
135 PLUGINWINDOW_STYLE
, PANGO_STYLE_ITALIC
,
139 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(pluginwindow
->plugin_list_view
));
140 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store
), &iter
))
141 gtk_tree_selection_select_iter(selection
, &iter
);
143 g_slist_free(plugins
);
146 static void select_row_cb(Plugin
*plugin
, PluginWindow
*pluginwindow
)
148 GtkTextView
*plugin_desc
= GTK_TEXT_VIEW(pluginwindow
->plugin_desc
);
149 GtkTextBuffer
*textbuf
= gtk_text_view_get_buffer(plugin_desc
);
150 GtkTextIter start_iter
, end_iter
;
153 pluginwindow
->selected_plugin
= plugin
;
155 if (pluginwindow
->selected_plugin
!= NULL
) {
156 const gchar
*desc
= plugin_get_desc(plugin
);
157 const gchar
*err
= plugin_get_error(plugin
);
158 gtk_text_buffer_get_start_iter(textbuf
, &start_iter
);
159 gtk_text_buffer_get_end_iter(textbuf
, &end_iter
);
160 gtk_text_buffer_delete(textbuf
, &start_iter
, &end_iter
);
163 text
= g_strconcat(desc
, _("\n\nVersion: "),
164 plugin_get_version(plugin
), "\n", NULL
);
166 text
= g_strconcat(_("Error: "),
167 err
, "\n", _("Plugin is not functional."),
168 "\n\n", desc
, _("\n\nVersion: "),
169 plugin_get_version(plugin
), "\n", NULL
);
170 gtk_text_buffer_insert(textbuf
, &start_iter
, text
, strlen(text
));
172 gtk_widget_set_sensitive(pluginwindow
->unload_btn
, TRUE
);
174 gtk_widget_set_sensitive(pluginwindow
->unload_btn
, FALSE
);
178 static void unselect_row_cb(Plugin
*plugin
, PluginWindow
*pluginwindow
)
180 gtk_widget_set_sensitive(pluginwindow
->unload_btn
, FALSE
);
183 static void unload_cb(GtkButton
*button
, PluginWindow
*pluginwindow
)
185 Plugin
*plugin
= pluginwindow
->selected_plugin
;
187 cm_return_if_fail(plugin
!= NULL
);
188 pluginwindow
->loading
= TRUE
;
189 plugin_unload(plugin
);
190 pluginwindow
->loading
= FALSE
;
191 pluginwindow
->selected_plugin
= NULL
;
192 set_plugin_list(pluginwindow
);
195 static void load_cb(GtkButton
*button
, PluginWindow
*pluginwindow
)
199 file_list
= filesel_select_multiple_files_open_with_filter(
200 _("Select the Plugins to load"), get_plugin_dir(),
201 "*." G_MODULE_SUFFIX
);
205 pluginwindow
->loading
= TRUE
;
206 for ( tmp
= file_list
; tmp
; tmp
= tmp
->next
) {
207 gchar
*file
, *error
= NULL
;
209 file
= (gchar
*) tmp
->data
;
211 plugin_load(file
, &error
);
213 gchar
*basename
= g_path_get_basename(file
);
215 _("The following error occurred while loading %s:\n\n%s\n"),
221 /* FIXME: globally or atom-ly : ? */
222 set_plugin_list(pluginwindow
);
225 pluginwindow
->loading
= FALSE
;
226 g_list_free(file_list
);
230 static gboolean
pluginwindow_key_pressed(GtkWidget
*widget
, GdkEventKey
*event
,
231 PluginWindow
*pluginwindow
)
234 switch (event
->keyval
) {
235 case GDK_KEY_Escape
:
236 case GDK_KEY_Return
:
237 case GDK_KEY_KP_Enter
:
238 close_cb(NULL
, pluginwindow
);
240 case GDK_KEY_Insert
:
241 case GDK_KEY_KP_Insert
:
242 case GDK_KEY_KP_Add
:
244 load_cb(NULL
, pluginwindow
);
246 case GDK_KEY_Delete
:
247 case GDK_KEY_KP_Delete
:
248 case GDK_KEY_KP_Subtract
:
250 unload_cb(NULL
, pluginwindow
);
260 *\brief Save Gtk object size to prefs dataset
262 static void pluginwindow_size_allocate_cb(GtkWidget
*widget
,
263 GtkAllocation
*allocation
)
265 cm_return_if_fail(allocation
!= NULL
);
267 gtk_window_get_size(GTK_WINDOW(widget
),
268 &prefs_common
.pluginswin_width
, &prefs_common
.pluginswin_height
);
272 void pluginwindow_create()
274 PluginWindow
*pluginwindow
;
278 GtkWidget
*scrolledwindow2
;
279 GtkWidget
*plugin_list_view
;
283 GtkWidget
*scrolledwindow3
;
284 GtkWidget
*plugin_desc
;
285 GtkWidget
*hbuttonbox1
;
286 GtkWidget
*hbuttonbox2
;
289 GtkWidget
*unload_btn
;
290 GtkWidget
*close_btn
;
291 gchar
*markup
, *span
;
294 GtkWidget
*hbox_info
;
295 static GdkGeometry geometry
;
297 debug_print("Creating plugins window...\n");
299 pluginwindow
= g_new0(PluginWindow
, 1);
301 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "pluginwindow");
302 gtk_container_set_border_width(GTK_CONTAINER(window
), 8);
303 gtk_window_set_title(GTK_WINDOW(window
), _("Plugins"));
304 gtk_window_set_modal(GTK_WINDOW(window
), TRUE
);
305 gtk_window_set_resizable(GTK_WINDOW (window
), TRUE
);
306 gtk_window_set_type_hint(GTK_WINDOW(window
), GDK_WINDOW_TYPE_HINT_DIALOG
);
307 manage_window_set_transient(GTK_WINDOW(window
));
309 vbox1
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 4);
310 gtk_widget_show(vbox1
);
311 gtk_container_add(GTK_CONTAINER(window
), vbox1
);
312 gtk_box_set_homogeneous(GTK_BOX(vbox1
), FALSE
);
313 gtk_widget_realize(window
);
315 hbox2
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 8);
316 gtk_widget_show(hbox2
);
317 gtk_box_pack_start(GTK_BOX(vbox1
), hbox2
, TRUE
, TRUE
, 0);
319 vbox3
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 4);
320 gtk_widget_show(vbox3
);
321 gtk_box_pack_start(GTK_BOX(hbox2
), vbox3
, FALSE
, FALSE
, 0);
323 scrolledwindow2
= gtk_scrolled_window_new(NULL
, NULL
);
324 gtk_widget_show(scrolledwindow2
);
325 gtk_box_pack_start(GTK_BOX(vbox3
), scrolledwindow2
, TRUE
, TRUE
, 0);
326 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwindow2
),
327 GTK_SHADOW_ETCHED_IN
);
328 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW
329 (scrolledwindow2
), GTK_POLICY_NEVER
,
330 GTK_POLICY_AUTOMATIC
);
332 plugin_list_view
= pluginwindow_list_view_create(pluginwindow
);
333 gtk_widget_show(plugin_list_view
);
334 gtk_container_add(GTK_CONTAINER(scrolledwindow2
), plugin_list_view
);
335 gtk_widget_grab_focus(GTK_WIDGET(plugin_list_view
));
337 gtkut_stock_button_set_create(&hbuttonbox1
,
338 &load_btn
, NULL
, _("_Load..."),
339 &unload_btn
, NULL
, _("_Unload"),
341 gtk_widget_show(hbuttonbox1
);
342 gtk_box_pack_start(GTK_BOX(vbox3
), hbuttonbox1
, FALSE
, FALSE
, 0);
344 vbox2
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 0);
345 gtk_widget_show(vbox2
);
346 gtk_box_pack_start(GTK_BOX(hbox2
), vbox2
, TRUE
, TRUE
, 0);
348 frame2
= gtk_frame_new(NULL
);
349 gtk_frame_set_shadow_type(GTK_FRAME(frame2
), GTK_SHADOW_OUT
);
350 gtk_widget_show(frame2
);
351 gtk_box_pack_start(GTK_BOX(vbox2
), frame2
, FALSE
, TRUE
, 0);
353 label13
= gtk_label_new(_("Description"));
354 gtk_widget_show(label13
);
355 gtk_container_add(GTK_CONTAINER(frame2
), label13
);
356 gtk_label_set_xalign(GTK_LABEL(label13
), 0.0);
357 gtk_widget_set_margin_start(GTK_WIDGET(label13
), 2);
358 gtk_widget_set_margin_end(GTK_WIDGET(label13
), 2);
360 scrolledwindow3
= gtk_scrolled_window_new(NULL
, NULL
);
361 gtk_widget_show(scrolledwindow3
);
362 gtk_box_pack_start(GTK_BOX(vbox2
), scrolledwindow3
, TRUE
, TRUE
, 0);
363 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwindow3
),
364 GTK_SHADOW_ETCHED_IN
);
365 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW
366 (scrolledwindow3
), GTK_POLICY_NEVER
,
369 plugin_desc
= gtk_text_view_new();
370 gtk_widget_show(plugin_desc
);
371 gtk_container_add(GTK_CONTAINER(scrolledwindow3
), plugin_desc
);
373 hbox_info
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 5);
374 gtk_widget_show(hbox_info
);
376 desc_lbl
= gtk_label_new("");
377 span
= g_strdup_printf("<a href=\"%s\"><span underline=\"none\">", PLUGINS_URI
);
378 markup
= g_strdup_printf(_("For more information about plugins see the "
379 "%sClaws Mail website%s."), span
, "</span></a>");
380 gtk_label_set_markup(GTK_LABEL(desc_lbl
), markup
);
383 gtk_label_set_xalign(GTK_LABEL(desc_lbl
), 0.0);
384 gtk_widget_show(desc_lbl
);
385 gtk_box_pack_start(GTK_BOX(hbox_info
), desc_lbl
, FALSE
, FALSE
, 0);
387 gtk_box_pack_start(GTK_BOX(hbox_info
), gtk_label_new(""), TRUE
, TRUE
, 0);
388 gtk_box_pack_start(GTK_BOX(vbox1
), hbox_info
, FALSE
, FALSE
, 0);
390 gtkut_stock_button_set_create_with_help(&hbuttonbox2
, &help_btn
,
391 &close_btn
, "window-close", _("_Close"),
392 NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
394 gtk_box_set_spacing(GTK_BOX(hbuttonbox2
), 6);
395 gtk_widget_show(hbuttonbox2
);
396 gtk_box_pack_end(GTK_BOX(vbox1
), hbuttonbox2
, FALSE
, FALSE
, 0);
398 gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(plugin_desc
), GTK_WRAP_WORD
);
399 gtk_widget_set_sensitive(GTK_WIDGET(unload_btn
), FALSE
);
401 g_signal_connect(G_OBJECT(help_btn
), "clicked",
402 G_CALLBACK(manual_open_with_anchor_cb
),
403 MANUAL_ANCHOR_PLUGINS
);
404 g_signal_connect(G_OBJECT(load_btn
), "clicked",
405 G_CALLBACK(load_cb
), pluginwindow
);
406 g_signal_connect(G_OBJECT(unload_btn
), "clicked",
407 G_CALLBACK(unload_cb
), pluginwindow
);
408 g_signal_connect(G_OBJECT(close_btn
), "clicked",
409 G_CALLBACK(close_cb
), pluginwindow
);
410 g_signal_connect(G_OBJECT(window
), "size_allocate",
411 G_CALLBACK(pluginwindow_size_allocate_cb
), NULL
);
412 g_signal_connect(G_OBJECT(window
), "key_press_event",
413 G_CALLBACK(pluginwindow_key_pressed
), pluginwindow
);
414 g_signal_connect(G_OBJECT(window
), "delete_event",
415 G_CALLBACK(pluginwindow_delete_cb
), pluginwindow
);
416 MANAGE_WINDOW_SIGNALS_CONNECT(window
);
418 CLAWS_SET_TIP(load_btn
,
419 _("Click here to load one or more plugins"));
421 CLAWS_SET_TIP(unload_btn
,
422 _("Unload the selected plugin"));
426 if (!geometry
.min_height
) {
427 geometry
.min_width
= -1;
428 geometry
.min_height
= 300;
431 gtk_window_set_geometry_hints(GTK_WINDOW(window
), NULL
, &geometry
,
433 gtk_window_set_default_size(GTK_WINDOW(window
), prefs_common
.pluginswin_width
,
434 prefs_common
.pluginswin_height
);
436 gtk_widget_show(window
);
438 pluginwindow
->window
= window
;
439 pluginwindow
->plugin_list_view
= plugin_list_view
;
440 pluginwindow
->plugin_desc
= plugin_desc
;
441 pluginwindow
->unload_btn
= unload_btn
;
442 pluginwindow
->selected_plugin
= NULL
;
444 set_plugin_list(pluginwindow
);
448 static GtkListStore
* pluginwindow_create_data_store(void)
450 return gtk_list_store_new(N_PLUGINWINDOW_COLUMNS
,
457 static GtkWidget
*pluginwindow_list_view_create(PluginWindow
*pluginwindow
)
459 GtkTreeView
*list_view
;
460 GtkTreeSelection
*selector
;
463 model
= GTK_TREE_MODEL(pluginwindow_create_data_store());
464 list_view
= GTK_TREE_VIEW(gtk_tree_view_new_with_model(model
));
465 g_object_unref(model
);
467 gtk_tree_view_set_search_column (list_view
, 0);
469 selector
= gtk_tree_view_get_selection(list_view
);
470 gtk_tree_selection_set_mode(selector
, GTK_SELECTION_BROWSE
);
471 gtk_tree_selection_set_select_function(selector
, pluginwindow_selected
,
474 /* create the columns */
475 pluginwindow_create_list_view_columns(GTK_WIDGET(list_view
));
477 return GTK_WIDGET(list_view
);
480 static void pluginwindow_create_list_view_columns(GtkWidget
*list_view
)
482 GtkTreeViewColumn
*column
;
483 GtkCellRenderer
*renderer
;
485 renderer
= gtk_cell_renderer_text_new();
486 column
= gtk_tree_view_column_new_with_attributes
487 (_("Loaded plugins"),
489 "text", PLUGINWINDOW_NAME
,
490 "style", PLUGINWINDOW_STYLE
,
492 gtk_tree_view_append_column(GTK_TREE_VIEW(list_view
), column
);
495 static gboolean
pluginwindow_selected(GtkTreeSelection
*selector
,
498 gboolean currently_selected
,
504 if (!gtk_tree_model_get_iter(model
, &iter
, path
))
507 gtk_tree_model_get(model
, &iter
,
508 PLUGINWINDOW_DATA
, &plugin
,
511 if (currently_selected
)
512 unselect_row_cb(plugin
, data
);
514 select_row_cb(plugin
, data
);