2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2024 the Claws Mail team and Colin Leroy
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"
26 #include <glib/gi18n.h>
27 #include <gdk/gdkkeysyms.h>
28 #include <sys/types.h>
30 #include "manage_window.h"
32 #include "mainwindow.h"
34 #include "alertpanel.h"
37 #include "prefs_common.h"
47 static struct URIOpener
50 GtkWidget
*hbox_scroll
;
55 GtkWidget
*scrolledwin
;
62 static void uri_opener_list_copy_cb(gpointer action
, gpointer data
);
64 static GtkActionGroup
*uri_opener_popup_action
= NULL
;
65 static GtkWidget
*uri_opener_popup_menu
= NULL
;
67 static GtkActionEntry uri_opener_popup_entries
[] =
69 {"UriOpenerPopup", NULL
, "UriOpenerPopup", NULL
, NULL
, NULL
},
70 {"UriOpenerPopup/Copy", NULL
, N_("C_opy URL"), NULL
, NULL
, G_CALLBACK(uri_opener_list_copy_cb
) }
73 static void uri_opener_load_uris (void);
74 static void uri_opener_open_cb (GtkWidget
*widget
, gpointer data
);
75 static void uri_opener_close_cb (GtkWidget
*widget
, gpointer data
);
76 static void uri_opener_select_all_cb (GtkWidget
*widget
, gpointer data
);
77 static gboolean
key_pressed (GtkWidget
*widget
, GdkEventKey
*event
,
79 static void uri_opener_double_clicked(GtkTreeView
*list_view
,
81 GtkTreeViewColumn
*column
,
83 static void uri_opener_create(void);
84 static gint
uri_opener_list_btn_pressed(GtkWidget
*widget
, GdkEventButton
*event
,
85 GtkTreeView
*list_view
);
86 static gboolean
uri_opener_list_popup_menu(GtkWidget
*widget
, gpointer data
);
88 void uri_opener_open(MessageView
*msgview
, GSList
*uris
)
90 cm_return_if_fail(msgview
);
91 cm_return_if_fail(msgview
->mimeview
);
92 cm_return_if_fail(msgview
->mimeview
->textview
);
97 manage_window_set_transient(GTK_WINDOW(opener
.window
));
98 gtk_widget_grab_focus(opener
.close_btn
);
101 alertpanel_notice(_("There are no URLs in this email."));
105 opener
.msgview
= msgview
;
106 opener
.uris
= g_slist_copy(uris
);
107 uri_opener_load_uris();
109 gtk_widget_show(opener
.window
);
110 gtk_widget_grab_focus(opener
.urilist
);
111 gtk_window_set_modal(GTK_WINDOW(opener
.window
), TRUE
);
114 static GtkListStore
* uri_opener_create_data_store(void)
116 return gtk_list_store_new(N_URI_OPENER_COLUMNS
,
122 static void uri_opener_create_list_view_columns(GtkWidget
*list_view
)
124 GtkTreeViewColumn
*column
;
125 GtkCellRenderer
*renderer
;
127 renderer
= gtk_cell_renderer_text_new();
128 column
= gtk_tree_view_column_new_with_attributes
129 (_("Included URLs:"),
131 "markup", URI_OPENER_URL
,
133 gtk_tree_view_append_column(GTK_TREE_VIEW(list_view
), column
);
136 static GtkWidget
*uri_opener_list_view_create(void)
138 GtkTreeView
*list_view
;
139 GtkTreeSelection
*selector
;
142 model
= GTK_TREE_MODEL(uri_opener_create_data_store());
143 list_view
= GTK_TREE_VIEW(gtk_tree_view_new_with_model(model
));
144 g_object_unref(model
);
146 selector
= gtk_tree_view_get_selection(list_view
);
147 gtk_tree_selection_set_mode(selector
, GTK_SELECTION_MULTIPLE
);
149 g_signal_connect(G_OBJECT(list_view
), "row_activated",
150 G_CALLBACK(uri_opener_double_clicked
),
152 g_signal_connect(G_OBJECT(list_view
), "popup-menu",
153 G_CALLBACK(uri_opener_list_popup_menu
),
155 g_signal_connect(G_OBJECT(list_view
), "button-press-event",
156 G_CALLBACK(uri_opener_list_btn_pressed
),
159 /* create the columns */
160 uri_opener_create_list_view_columns(GTK_WIDGET(list_view
));
162 return GTK_WIDGET(list_view
);
166 static GtkWidget
*uri_opener_scrolled_win_create(void)
168 GtkWidget
*scrolledwin
;
170 scrolledwin
= gtk_scrolled_window_new(NULL
, NULL
);
171 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin
),
172 GTK_SHADOW_ETCHED_IN
);
173 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin
),
174 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
176 gtk_widget_set_size_request(scrolledwin
, 200, 250);
177 gtk_widget_show(scrolledwin
);
182 static void uri_opener_size_allocate_cb(GtkWidget
*widget
, GtkAllocation
*allocation
)
184 cm_return_if_fail(allocation
!= NULL
);
186 gtk_window_get_size(GTK_WINDOW(widget
),
187 &prefs_common
.uriopenerwin_width
, &prefs_common
.uriopenerwin_height
);
190 static void uri_opener_create(void)
193 GtkWidget
*hbox_scroll
;
199 GtkWidget
*select_all_btn
;
201 GtkWidget
*close_btn
;
202 GtkWidget
*scrolledwin
;
203 static GdkGeometry geometry
;
205 window
= gtkut_window_new(GTK_WINDOW_TOPLEVEL
, "uri_opener");
206 gtk_window_set_title (GTK_WINDOW(window
),
207 C_("Dialog title", "Open URLs"));
209 gtk_container_set_border_width (GTK_CONTAINER (window
), 8);
210 gtk_window_set_position (GTK_WINDOW (window
), GTK_WIN_POS_CENTER
);
211 gtk_window_set_resizable(GTK_WINDOW (window
), TRUE
);
212 gtk_window_set_type_hint(GTK_WINDOW(window
), GDK_WINDOW_TYPE_HINT_DIALOG
);
213 g_signal_connect(G_OBJECT(window
), "delete_event",
214 G_CALLBACK(uri_opener_close_cb
), NULL
);
215 g_signal_connect (G_OBJECT(window
), "size_allocate",
216 G_CALLBACK (uri_opener_size_allocate_cb
), NULL
);
217 g_signal_connect(G_OBJECT(window
), "key_press_event",
218 G_CALLBACK(key_pressed
), NULL
);
219 MANAGE_WINDOW_SIGNALS_CONNECT (window
);
221 vbox1
= gtk_box_new(GTK_ORIENTATION_VERTICAL
, 6);
222 gtkut_stock_button_set_create(&hbox1
,
223 &open_btn
, NULL
, _("Open in browser"),
224 &close_btn
, NULL
, _("Close"),
227 g_signal_connect(G_OBJECT(open_btn
), "clicked",
228 G_CALLBACK(uri_opener_open_cb
), NULL
);
230 g_signal_connect(G_OBJECT(close_btn
), "clicked",
231 G_CALLBACK(uri_opener_close_cb
), NULL
);
233 urilist
= uri_opener_list_view_create();
235 label
= gtk_label_new(_("Any phishing URLs are shown in red, followed by the actual URL."));
236 gtk_label_set_xalign(GTK_LABEL(label
), 0.0);
237 gtk_box_pack_start(GTK_BOX(vbox1
), label
, FALSE
, TRUE
, 0);
239 scrolledwin
= uri_opener_scrolled_win_create();
240 hbox_scroll
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 0);
241 gtk_box_pack_start(GTK_BOX(hbox_scroll
), scrolledwin
, TRUE
, TRUE
, 0);
243 select_all_btn
= gtk_button_new_with_label(_("Select All"));
244 g_signal_connect(G_OBJECT(select_all_btn
), "clicked",
245 G_CALLBACK(uri_opener_select_all_cb
), NULL
);
247 hbox
= gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 0);
248 gtk_box_pack_start(GTK_BOX(hbox
), select_all_btn
, FALSE
, FALSE
, 0);
249 gtk_box_pack_start(GTK_BOX(hbox
), gtk_label_new(""), TRUE
, TRUE
, 0);
251 gtk_container_add(GTK_CONTAINER(scrolledwin
), urilist
);
252 gtk_box_pack_start(GTK_BOX(vbox1
), hbox_scroll
, TRUE
, TRUE
, 0);
253 gtk_box_pack_start(GTK_BOX(vbox1
), hbox
, FALSE
, FALSE
, 0);
254 gtk_box_pack_start(GTK_BOX(vbox1
), hbox1
, FALSE
, FALSE
, 0);
256 gtk_widget_show_all(vbox1
);
257 gtk_container_add(GTK_CONTAINER (window
), vbox1
);
259 if (!geometry
.min_height
) {
260 geometry
.min_width
= 450;
261 geometry
.min_height
= 300;
264 gtk_window_set_geometry_hints(GTK_WINDOW(window
), NULL
, &geometry
,
266 gtk_window_set_default_size(GTK_WINDOW(window
),
267 prefs_common
.uriopenerwin_width
,
268 prefs_common
.uriopenerwin_height
);
270 opener
.window
= window
;
271 opener
.hbox_scroll
= hbox_scroll
;
272 opener
.hbox1
= hbox1
;
273 opener
.vbox1
= vbox1
;
274 opener
.label
= label
;
275 opener
.urilist
= urilist
;
276 opener
.scrolledwin
= scrolledwin
;
277 opener
.open_btn
= open_btn
;
278 opener
.close_btn
= close_btn
;
282 static void uri_opener_list_view_insert_uri(GtkWidget
*list_view
,
283 GtkTreeIter
*row_iter
,
287 GtkListStore
*list_store
= GTK_LIST_STORE(gtk_tree_view_get_model
288 (GTK_TREE_VIEW(list_view
)));
289 gchar
*visible
= textview_get_visible_uri(opener
.msgview
->mimeview
->textview
, uri
);
293 if (visible
&& strcmp(visible
, uri
->uri
)) {
294 gboolean phishing_attempt
= FALSE
;
295 if (strcmp(visible
, uri
->uri
) != 0 && is_uri_string(visible
)) {
297 gchar
*visible_uri_path
;
299 uri_path
= get_uri_path(uri
->uri
);
300 visible_uri_path
= get_uri_path(visible
);
301 if (path_cmp(uri_path
, visible_uri_path
) != 0)
302 phishing_attempt
= TRUE
;
304 if (phishing_attempt
) {
305 label
= g_markup_printf_escaped("<span color=\"%s\"><b>%s</b></span>\n%s",
306 gtkut_gdk_rgba_to_string(&prefs_common
.color
[COL_LOG_ERROR
]), visible
, uri
->uri
);
308 label
= g_markup_printf_escaped("%s", uri
->uri
);
311 label
= g_markup_printf_escaped("%s", uri
->uri
);
313 if (row_iter
== NULL
) {
315 gtk_list_store_append(list_store
, &iter
);
316 gtk_list_store_set(list_store
, &iter
,
317 URI_OPENER_URL
, label
,
318 URI_OPENER_DATA
, uri
,
321 gtk_list_store_set(list_store
, row_iter
,
322 URI_OPENER_URL
, label
,
323 URI_OPENER_DATA
, uri
,
330 static void uri_opener_list_view_clear_uris(GtkWidget
*list_view
)
332 GtkListStore
*list_store
= GTK_LIST_STORE(gtk_tree_view_get_model
333 (GTK_TREE_VIEW(list_view
)));
334 gtk_list_store_clear(list_store
);
337 static void uri_opener_load_uris(void)
339 GSList
*cur
= opener
.uris
;
341 GtkTreeSelection
*selection
;
344 uri_opener_list_view_clear_uris(opener
.urilist
);
345 for (; cur
; cur
= cur
->next
) {
346 ClickableText
*uri
= (ClickableText
*)cur
->data
;
347 uri_opener_list_view_insert_uri(opener
.urilist
, NULL
, uri
);
350 g_object_ref(opener
.urilist
);
351 gtk_container_remove(GTK_CONTAINER(opener
.scrolledwin
), opener
.urilist
);
352 gtk_widget_destroy(opener
.scrolledwin
);
354 opener
.scrolledwin
= uri_opener_scrolled_win_create();
355 gtk_container_add(GTK_CONTAINER(opener
.scrolledwin
), opener
.urilist
);
356 gtk_box_pack_start(GTK_BOX(opener
.hbox_scroll
),
357 opener
.scrolledwin
, TRUE
, TRUE
, 0);
358 g_object_unref(opener
.urilist
);
360 model
= gtk_tree_view_get_model(GTK_TREE_VIEW(opener
.urilist
));
361 if (!gtk_tree_model_get_iter_first(model
, &iter
))
363 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(opener
.urilist
));
364 gtk_tree_selection_select_iter(selection
, &iter
);
367 static void uri_opener_close(void)
369 g_slist_free(opener
.uris
);
371 gtk_window_set_modal(GTK_WINDOW(opener
.window
), FALSE
);
372 gtk_widget_hide(opener
.window
);
375 static void uri_opener_close_cb(GtkWidget
*widget
,
381 static gboolean
key_pressed(GtkWidget
*widget
, GdkEventKey
*event
, gpointer data
)
384 if (event
->keyval
== GDK_KEY_Escape
)
387 if ((event
->keyval
== GDK_KEY_c
|| event
->keyval
== GDK_KEY_x
) &&
388 (event
->state
& GDK_CONTROL_MASK
)) {
389 uri_opener_list_copy_cb(NULL
, NULL
);
397 static void uri_opener_double_clicked(GtkTreeView
*list_view
,
399 GtkTreeViewColumn
*column
,
404 GtkTreeModel
*model
= gtk_tree_view_get_model(list_view
);
406 if (!gtk_tree_model_get_iter(model
, &iter
, path
))
409 gtk_tree_model_get(model
, &iter
,
410 URI_OPENER_DATA
, &uri
,
416 if (textview_uri_security_check(opener
.msgview
->mimeview
->textview
, uri
,
419 prefs_common_get_uri_cmd());
422 static void uri_opener_open_cb(GtkWidget
*widget
,
428 GtkTreeSelection
*selection
;
429 GList
*selected
, *cur
;
431 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(opener
.urilist
));
432 selected
= gtk_tree_selection_get_selected_rows(selection
, &model
);
433 cm_return_if_fail(selected
);
435 for(cur
= selected
; cur
!= NULL
; cur
= g_list_next(cur
))
437 if(!gtk_tree_model_get_iter(model
, &sel
, (GtkTreePath
*)cur
->data
))
440 gtk_tree_model_get(model
, &sel
,
441 URI_OPENER_DATA
, &uri
,
446 if (textview_uri_security_check(opener
.msgview
->mimeview
->textview
, uri
,
449 prefs_common_get_uri_cmd());
452 g_list_foreach(selected
, (GFunc
)gtk_tree_path_free
, NULL
);
453 g_list_free(selected
);
456 static void uri_opener_select_all_cb(GtkWidget
*widget
,
459 GtkTreeSelection
*selection
= gtk_tree_view_get_selection(
460 GTK_TREE_VIEW(opener
.urilist
));
461 gtk_tree_selection_select_all(selection
);
464 static void uri_opener_list_copy_cb(gpointer action
, gpointer data
)
469 GtkTreeSelection
*selection
;
470 GList
*selected
, *cur
;
471 GString
*uri_list_str
= NULL
;
473 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(opener
.urilist
));
474 selected
= gtk_tree_selection_get_selected_rows(selection
, &model
);
475 cm_return_if_fail(selected
);
477 for(cur
= selected
; cur
!= NULL
; cur
= g_list_next(cur
))
479 if(!gtk_tree_model_get_iter(model
, &sel
, (GtkTreePath
*)cur
->data
))
482 gtk_tree_model_get(model
, &sel
,
483 URI_OPENER_DATA
, &uri
,
489 uri_list_str
= g_string_new((const gchar
*)uri
->uri
);
491 g_string_append_printf(uri_list_str
, "\n%s", uri
->uri
);
494 GtkClipboard
*clip
, *clip2
;
496 if (textview_uri_security_check(opener
.msgview
->mimeview
->textview
, uri
,
498 clip
= gtk_widget_get_clipboard (opener
.window
, GDK_SELECTION_PRIMARY
);
499 clip2
= gtk_widget_get_clipboard (opener
.window
, GDK_SELECTION_CLIPBOARD
);
500 gtk_clipboard_set_text (clip
, uri_list_str
->str
, uri_list_str
->len
);
501 gtk_clipboard_set_text (clip2
, uri_list_str
->str
, uri_list_str
->len
);
503 g_string_free(uri_list_str
, TRUE
);
506 g_list_foreach(selected
, (GFunc
)gtk_tree_path_free
, NULL
);
507 g_list_free(selected
);
510 static gint
uri_opener_list_btn_pressed(GtkWidget
*widget
, GdkEventButton
*event
,
511 GtkTreeView
*list_view
)
514 /* right-button click */
515 if (event
->button
== 3) {
516 GtkTreeModel
*model
= gtk_tree_view_get_model(list_view
);
517 GtkTreeSelection
*selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(list_view
));
518 GList
*selected
= gtk_tree_selection_get_selected_rows(selection
, &model
);
520 if (!uri_opener_popup_menu
) {
521 uri_opener_popup_action
= cm_menu_create_action_group("UriOpenerPopup", uri_opener_popup_entries
,
522 G_N_ELEMENTS(uri_opener_popup_entries
), (gpointer
)list_view
);
523 MENUITEM_ADDUI("/Menus", "UriOpenerPopup", "UriOpenerPopup", GTK_UI_MANAGER_MENU
)
524 MENUITEM_ADDUI("/Menus/UriOpenerPopup", "Copy", "UriOpenerPopup/Copy", GTK_UI_MANAGER_MENUITEM
)
525 uri_opener_popup_menu
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(
526 gtk_ui_manager_get_widget(gtkut_ui_manager(), "/Menus/UriOpenerPopup")) );
529 /* grey out some popup menu item if there is no selected row */
530 cm_menu_set_sensitive("UriOpenerPopup/Copy", (selected
!= NULL
));
532 gtk_menu_popup_at_pointer(GTK_MENU(uri_opener_popup_menu
), NULL
);
538 static gboolean
uri_opener_list_popup_menu(GtkWidget
*widget
, gpointer data
)
540 GtkTreeView
*list_view
= (GtkTreeView
*)data
;
541 GdkEventButton event
;
544 event
.time
= gtk_get_current_event_time();
546 uri_opener_list_btn_pressed(NULL
, &event
, list_view
);