1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2012 Colin Leroy <colin@colino.net> and
4  * the Claws Mail team
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #include "claws-features.h"
23 #endif
24 
25 #include <gtk/gtk.h>
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <gdk/gdkkeysyms.h>
29 #include <sys/types.h>
30 #include <dirent.h>
31 
32 #include "manage_window.h"
33 #include "utils.h"
34 #include "mainwindow.h"
35 #include "gtkutils.h"
36 #include "alertpanel.h"
37 #include "textview.h"
38 #include "mimeview.h"
39 #include "prefs_common.h"
40 
41 enum {
42 	URI_OPENER_URL,
43 	URI_OPENER_DATA,
44 	N_URI_OPENER_COLUMNS
45 };
46 
47 
48 static struct URIOpener
49 {
50 	GtkWidget *window;
51 	GtkWidget *hbox_scroll;
52 	GtkWidget *hbox1;
53 	GtkWidget *vbox1;
54 	GtkWidget *label;
55 	GtkWidget *urilist;
56 	GtkWidget *scrolledwin;
57 	GtkWidget *open_btn;
58 	GtkWidget *close_btn;
59 	MessageView *msgview;
60 	GSList    *uris;
61 } opener;
62 
63 static void uri_opener_load_uris (void);
64 static void uri_opener_open_cb		(GtkWidget *widget, gpointer data);
65 static void uri_opener_close_cb		(GtkWidget *widget, gpointer data);
66 static void uri_opener_select_all_cb	(GtkWidget *widget, gpointer data);
67 static gboolean key_pressed		(GtkWidget *widget, GdkEventKey *event,
68 					 gpointer data);
69 static void uri_opener_double_clicked(GtkTreeView		*list_view,
70 				   	GtkTreePath		*path,
71 				   	GtkTreeViewColumn	*column,
72 				   	gpointer		 data);
73 static void uri_opener_create(void);
uri_opener_open(MessageView * msgview,GSList * uris)74 void uri_opener_open(MessageView *msgview, GSList *uris)
75 {
76 	cm_return_if_fail(msgview);
77 	cm_return_if_fail(msgview->mimeview);
78 	cm_return_if_fail(msgview->mimeview->textview);
79 	cm_return_if_fail(msgview);
80 	if (!opener.window)
81 		uri_opener_create();
82 
83 	manage_window_set_transient(GTK_WINDOW(opener.window));
84 	gtk_widget_grab_focus(opener.close_btn);
85 
86 	if (uris == NULL) {
87 		alertpanel_notice(_("There are no URLs in this email."));
88 		return;
89 	}
90 
91 	opener.msgview = msgview;
92 	opener.uris = g_slist_copy(uris);
93 	uri_opener_load_uris();
94 
95 	gtk_widget_show(opener.window);
96 	gtk_widget_grab_focus(opener.urilist);
97 	gtk_window_set_modal(GTK_WINDOW(opener.window), TRUE);
98 }
99 
uri_opener_create_data_store(void)100 static GtkListStore* uri_opener_create_data_store(void)
101 {
102 	return gtk_list_store_new(N_URI_OPENER_COLUMNS,
103 				  G_TYPE_STRING,
104   				  G_TYPE_POINTER,
105 				  -1);
106 }
107 
uri_opener_create_list_view_columns(GtkWidget * list_view)108 static void uri_opener_create_list_view_columns(GtkWidget *list_view)
109 {
110 	GtkTreeViewColumn *column;
111 	GtkCellRenderer *renderer;
112 
113 	renderer = gtk_cell_renderer_text_new();
114 	column = gtk_tree_view_column_new_with_attributes
115 		(_("Available URLs:"),
116 		 renderer,
117 		 "markup", URI_OPENER_URL,
118 		 NULL);
119 	gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), column);
120 }
121 
uri_opener_list_view_create(void)122 static GtkWidget *uri_opener_list_view_create	(void)
123 {
124 	GtkTreeView *list_view;
125 	GtkTreeSelection *selector;
126 	GtkTreeModel *model;
127 
128 	model = GTK_TREE_MODEL(uri_opener_create_data_store());
129 	list_view = GTK_TREE_VIEW(gtk_tree_view_new_with_model(model));
130 	g_object_unref(model);
131 
132 	gtk_tree_view_set_rules_hint(list_view, prefs_common.use_stripes_everywhere);
133 
134 	selector = gtk_tree_view_get_selection(list_view);
135 	gtk_tree_selection_set_mode(selector, GTK_SELECTION_MULTIPLE);
136 
137 	g_signal_connect(G_OBJECT(list_view), "row_activated",
138 	                 G_CALLBACK(uri_opener_double_clicked),
139 			 list_view);
140 
141 	/* create the columns */
142 	uri_opener_create_list_view_columns(GTK_WIDGET(list_view));
143 
144 	return GTK_WIDGET(list_view);
145 
146 }
147 
uri_opener_scrolled_win_create(void)148 static GtkWidget *uri_opener_scrolled_win_create(void)
149 {
150 	GtkWidget *scrolledwin;
151 
152 	scrolledwin = gtk_scrolled_window_new(NULL, NULL);
153 	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
154 					    GTK_SHADOW_ETCHED_IN);
155 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
156 				       GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
157 
158 	gtk_widget_set_size_request(scrolledwin, 200, 250);
159 	gtk_widget_show(scrolledwin);
160 
161 	return scrolledwin;
162 }
163 
uri_opener_size_allocate_cb(GtkWidget * widget,GtkAllocation * allocation)164 static void uri_opener_size_allocate_cb(GtkWidget *widget, GtkAllocation *allocation)
165 {
166 	cm_return_if_fail(allocation != NULL);
167 
168 	prefs_common.uriopenerwin_width = allocation->width;
169 	prefs_common.uriopenerwin_height = allocation->height;
170 }
171 
uri_opener_create(void)172 static void uri_opener_create(void)
173 {
174 	GtkWidget *window;
175 	GtkWidget *hbox_scroll;
176 	GtkWidget *hbox;
177 	GtkWidget *hbox1;
178 	GtkWidget *vbox1;
179 	GtkWidget *label;
180 	GtkWidget *urilist;
181 	GtkWidget *select_all_btn;
182 	GtkWidget *open_btn;
183 	GtkWidget *close_btn;
184 	GtkWidget *scrolledwin;
185 	static GdkGeometry geometry;
186 
187 	window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "uri_opener");
188 	gtk_window_set_title (GTK_WINDOW(window),
189 			      C_("Dialog title", "Open URLs"));
190 
191 	gtk_container_set_border_width (GTK_CONTAINER (window), 8);
192 	gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
193 	gtk_window_set_resizable(GTK_WINDOW (window), TRUE);
194 	gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG);
195 	g_signal_connect(G_OBJECT(window), "delete_event",
196 			 G_CALLBACK(uri_opener_close_cb), NULL);
197 	g_signal_connect (G_OBJECT(window), "size_allocate",
198 			 G_CALLBACK (uri_opener_size_allocate_cb), NULL);
199 	g_signal_connect(G_OBJECT(window), "key_press_event",
200 			 G_CALLBACK(key_pressed), NULL);
201 	MANAGE_WINDOW_SIGNALS_CONNECT (window);
202 
203 	vbox1 = gtk_vbox_new(FALSE, 6);
204 	gtkut_stock_button_set_create(&hbox1,
205 				      &open_btn, GTK_STOCK_OPEN,
206 				      &close_btn, GTK_STOCK_CLOSE,
207 				      NULL, NULL);
208 
209 	g_signal_connect(G_OBJECT(open_btn), "clicked",
210 			 G_CALLBACK(uri_opener_open_cb), NULL);
211 
212 	g_signal_connect(G_OBJECT(close_btn), "clicked",
213 			 G_CALLBACK(uri_opener_close_cb), NULL);
214 
215 	urilist = uri_opener_list_view_create();
216 
217 	label = gtk_label_new(_("Please select the URL to open."));
218 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
219 	gtk_box_pack_start(GTK_BOX(vbox1), label, FALSE, TRUE, 0);
220 
221 	scrolledwin = uri_opener_scrolled_win_create();
222 	hbox_scroll = gtk_hbox_new(FALSE, 0);
223 	gtk_box_pack_start(GTK_BOX(hbox_scroll), scrolledwin, TRUE, TRUE, 0);
224 
225 	select_all_btn = gtk_button_new_with_label(_("Select All"));
226 	g_signal_connect(G_OBJECT(select_all_btn), "clicked",
227 			 G_CALLBACK(uri_opener_select_all_cb), NULL);
228 
229 	hbox = gtk_hbox_new(FALSE, 0);
230 	gtk_box_pack_start(GTK_BOX(hbox), select_all_btn, FALSE, FALSE, 0);
231 	gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(""), TRUE, TRUE, 0);
232 
233 	gtk_container_add(GTK_CONTAINER(scrolledwin), urilist);
234 	gtk_box_pack_start(GTK_BOX(vbox1), hbox_scroll, TRUE, TRUE, 0);
235 	gtk_box_pack_start(GTK_BOX(vbox1), hbox, FALSE, FALSE, 0);
236 	gtk_box_pack_start(GTK_BOX(vbox1), hbox1, FALSE, FALSE, 0);
237 
238 	gtk_widget_show_all(vbox1);
239 	gtk_container_add(GTK_CONTAINER (window), vbox1);
240 
241 	if (!geometry.min_height) {
242 		geometry.min_width = 450;
243 		geometry.min_height = 300;
244 	}
245 
246 	gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL, &geometry,
247 				      GDK_HINT_MIN_SIZE);
248 	gtk_widget_set_size_request(window, prefs_common.uriopenerwin_width,
249 				    prefs_common.uriopenerwin_height);
250 
251 	opener.window = window;
252 	opener.hbox_scroll = hbox_scroll;
253 	opener.hbox1 = hbox1;
254 	opener.vbox1 = vbox1;
255 	opener.label = label;
256 	opener.urilist = urilist;
257 	opener.scrolledwin = scrolledwin;
258 	opener.open_btn = open_btn;
259 	opener.close_btn = close_btn;
260 
261 }
262 
uri_opener_list_view_insert_uri(GtkWidget * list_view,GtkTreeIter * row_iter,ClickableText * uri)263 static void uri_opener_list_view_insert_uri(GtkWidget *list_view,
264 						  GtkTreeIter *row_iter,
265 						  ClickableText *uri)
266 {
267 	GtkTreeIter iter;
268 	GtkListStore *list_store = GTK_LIST_STORE(gtk_tree_view_get_model
269 					(GTK_TREE_VIEW(list_view)));
270 	gchar *visible = textview_get_visible_uri(opener.msgview->mimeview->textview, uri);
271 
272 	gchar *label = NULL;
273 
274 	if (visible && strcmp(visible, uri->uri))
275 		label = g_markup_printf_escaped("<b>%s</b>\n%s", visible, uri->uri);
276 	else
277 		label = g_markup_printf_escaped("\n%s", uri->uri);
278 
279 	if (row_iter == NULL) {
280 		/* append new */
281 		gtk_list_store_append(list_store, &iter);
282 		gtk_list_store_set(list_store, &iter,
283 				   URI_OPENER_URL, label,
284 				   URI_OPENER_DATA, uri,
285 				   -1);
286 	} else {
287 		gtk_list_store_set(list_store, row_iter,
288 				   URI_OPENER_URL, label,
289 				   URI_OPENER_DATA, uri,
290 				   -1);
291 	}
292 	g_free(visible);
293 	g_free(label);
294 }
295 
uri_opener_list_view_clear_uris(GtkWidget * list_view)296 static void uri_opener_list_view_clear_uris(GtkWidget *list_view)
297 {
298 	GtkListStore *list_store = GTK_LIST_STORE(gtk_tree_view_get_model
299 					(GTK_TREE_VIEW(list_view)));
300 	gtk_list_store_clear(list_store);
301 }
302 
uri_opener_load_uris(void)303 static void uri_opener_load_uris (void)
304 {
305 	GSList *cur = opener.uris;
306 	GtkTreeModel *model;
307 	GtkTreeSelection *selection;
308 	GtkTreeIter iter;
309 
310 	uri_opener_list_view_clear_uris(opener.urilist);
311 	for (; cur; cur = cur->next) {
312 		ClickableText *uri = (ClickableText *)cur->data;
313 		uri_opener_list_view_insert_uri(opener.urilist, NULL, uri);
314 	}
315 
316 	g_object_ref(opener.urilist);
317 	gtk_container_remove(GTK_CONTAINER(opener.scrolledwin), opener.urilist);
318 	gtk_widget_destroy(opener.scrolledwin);
319 
320 	opener.scrolledwin = uri_opener_scrolled_win_create();
321 	gtk_container_add(GTK_CONTAINER(opener.scrolledwin), opener.urilist);
322 	gtk_box_pack_start(GTK_BOX(opener.hbox_scroll),
323 			   opener.scrolledwin, TRUE, TRUE, 0);
324 	g_object_unref(opener.urilist);
325 
326 	model = gtk_tree_view_get_model(GTK_TREE_VIEW(opener.urilist));
327 	if (!gtk_tree_model_get_iter_first(model, &iter))
328 		return;
329 	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(opener.urilist));
330 	gtk_tree_selection_select_iter(selection, &iter);
331 }
332 
uri_opener_close(void)333 static void uri_opener_close(void)
334 {
335 	g_slist_free(opener.uris);
336 	opener.uris = NULL;
337 	gtk_window_set_modal(GTK_WINDOW(opener.window), FALSE);
338 	gtk_widget_hide(opener.window);
339 }
340 
uri_opener_close_cb(GtkWidget * widget,gpointer data)341 static void uri_opener_close_cb(GtkWidget *widget,
342 			         gpointer data)
343 {
344 	uri_opener_close();
345 }
346 
key_pressed(GtkWidget * widget,GdkEventKey * event,gpointer data)347 static gboolean key_pressed(GtkWidget *widget, GdkEventKey *event, gpointer data)
348 {
349 	if (event && event->keyval == GDK_KEY_Escape)
350 		uri_opener_close();
351 	return FALSE;
352 }
353 
uri_opener_double_clicked(GtkTreeView * list_view,GtkTreePath * path,GtkTreeViewColumn * column,gpointer data)354 static void uri_opener_double_clicked(GtkTreeView		*list_view,
355 				   	GtkTreePath		*path,
356 				   	GtkTreeViewColumn	*column,
357 				   	gpointer		 data)
358 {
359 	ClickableText *uri;
360 	GtkTreeIter iter;
361 	GtkTreeModel *model = gtk_tree_view_get_model(list_view);
362 
363 	if (!gtk_tree_model_get_iter(model, &iter, path))
364 		return;
365 
366 	gtk_tree_model_get(model, &iter,
367 			   URI_OPENER_DATA, &uri,
368 			   -1);
369 
370 	if (!uri)
371 		return;
372 
373 	if (textview_uri_security_check(opener.msgview->mimeview->textview, uri) == TRUE)
374 		open_uri(uri->uri,
375 			 prefs_common_get_uri_cmd());
376 }
377 
uri_opener_open_cb(GtkWidget * widget,gpointer data)378 static void uri_opener_open_cb(GtkWidget *widget,
379 			        gpointer data)
380 {
381 	ClickableText *uri;
382 	GtkTreeIter sel;
383 	GtkTreeModel *model;
384 	GtkTreeSelection *selection;
385 	GList *selected, *cur;
386 
387 	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(opener.urilist));
388 	selected  = gtk_tree_selection_get_selected_rows(selection, &model);
389 	cm_return_if_fail(selected);
390 
391 	for(cur = selected; cur != NULL; cur = g_list_next(cur))
392 	{
393 		if(!gtk_tree_model_get_iter(model, &sel, (GtkTreePath *)cur->data))
394 			continue;
395 
396 		gtk_tree_model_get(model, &sel,
397 			   URI_OPENER_DATA, &uri,
398 			   -1);
399 		if (!uri)
400 			continue;
401 
402 		if (textview_uri_security_check(opener.msgview->mimeview->textview, uri) == TRUE)
403 			open_uri(uri->uri,
404 				 prefs_common_get_uri_cmd());
405 	}
406 
407 	g_list_foreach(selected, (GFunc)gtk_tree_path_free, NULL);
408 	g_list_free(selected);
409 }
410 
uri_opener_select_all_cb(GtkWidget * widget,gpointer data)411 static void uri_opener_select_all_cb(GtkWidget *widget,
412 				     gpointer data)
413 {
414 	GtkTreeSelection *selection = gtk_tree_view_get_selection(
415 						GTK_TREE_VIEW(opener.urilist));
416 	gtk_tree_selection_select_all(selection);
417 }
418