1 /*
2  * e-web-view-preview.c
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
11  * for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; if not, see <http://www.gnu.org/licenses/>.
15  *
16  *
17  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
18  *
19  */
20 
21 #include "evolution-config.h"
22 
23 #include "e-misc-utils.h"
24 #include "e-web-view-preview.h"
25 
26 #include <string.h>
27 #include <glib/gi18n-lib.h>
28 
29 #define E_WEB_VIEW_PREVIEW_GET_PRIVATE(obj) \
30 	(G_TYPE_INSTANCE_GET_PRIVATE \
31 	((obj), E_TYPE_WEB_VIEW_PREVIEW, EWebViewPreviewPrivate))
32 
33 struct _EWebViewPreviewPrivate {
34 	gboolean escape_values;
35 	GString *updating_content; /* is NULL when not between begin_update/end_update */
36 };
37 
38 enum {
39 	PROP_0,
40 	PROP_TREE_VIEW,
41 	PROP_PREVIEW_WIDGET,
42 	PROP_ESCAPE_VALUES
43 };
44 
45 G_DEFINE_TYPE (
46 	EWebViewPreview,
47 	e_web_view_preview,
48 	GTK_TYPE_PANED);
49 
50 static void
web_view_preview_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)51 web_view_preview_set_property (GObject *object,
52                                guint property_id,
53                                const GValue *value,
54                                GParamSpec *pspec)
55 {
56 	switch (property_id) {
57 		case PROP_ESCAPE_VALUES:
58 			e_web_view_preview_set_escape_values (
59 				E_WEB_VIEW_PREVIEW (object),
60 				g_value_get_boolean (value));
61 			return;
62 	}
63 
64 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
65 }
66 
67 static void
web_view_preview_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)68 web_view_preview_get_property (GObject *object,
69                                guint property_id,
70                                GValue *value,
71                                GParamSpec *pspec)
72 {
73 	switch (property_id) {
74 		case PROP_TREE_VIEW:
75 			g_value_set_object (
76 				value, e_web_view_preview_get_tree_view (
77 				E_WEB_VIEW_PREVIEW (object)));
78 			return;
79 
80 		case PROP_PREVIEW_WIDGET:
81 			g_value_set_object (
82 				value, e_web_view_preview_get_preview (
83 				E_WEB_VIEW_PREVIEW (object)));
84 			return;
85 
86 		case PROP_ESCAPE_VALUES:
87 			g_value_set_boolean (
88 				value, e_web_view_preview_get_escape_values (
89 				E_WEB_VIEW_PREVIEW (object)));
90 			return;
91 	}
92 
93 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
94 }
95 
96 static void
web_view_preview_dispose(GObject * object)97 web_view_preview_dispose (GObject *object)
98 {
99 	EWebViewPreviewPrivate *priv;
100 
101 	priv = E_WEB_VIEW_PREVIEW_GET_PRIVATE (object);
102 
103 	if (priv->updating_content != NULL) {
104 		g_string_free (priv->updating_content, TRUE);
105 		priv->updating_content = NULL;
106 	}
107 
108 	/* Chain up to parent's dispose() method. */
109 	G_OBJECT_CLASS (e_web_view_preview_parent_class)->dispose (object);
110 }
111 
112 static void
e_web_view_preview_class_init(EWebViewPreviewClass * class)113 e_web_view_preview_class_init (EWebViewPreviewClass *class)
114 {
115 	GObjectClass *object_class;
116 
117 	g_type_class_add_private (class, sizeof (EWebViewPreviewPrivate));
118 
119 	object_class = G_OBJECT_CLASS (class);
120 	object_class->set_property = web_view_preview_set_property;
121 	object_class->get_property = web_view_preview_get_property;
122 	object_class->dispose = web_view_preview_dispose;
123 
124 	g_object_class_install_property (
125 		object_class,
126 		PROP_TREE_VIEW,
127 		g_param_spec_object (
128 			"tree-view",
129 			"Tree View",
130 			NULL,
131 			GTK_TYPE_TREE_VIEW,
132 			G_PARAM_READABLE));
133 
134 	g_object_class_install_property (
135 		object_class,
136 		PROP_PREVIEW_WIDGET,
137 		g_param_spec_object (
138 			"preview-widget",
139 			"Preview Widget",
140 			NULL,
141 			GTK_TYPE_WIDGET,
142 			G_PARAM_READABLE));
143 
144 	g_object_class_install_property (
145 		object_class,
146 		PROP_ESCAPE_VALUES,
147 		g_param_spec_boolean (
148 			"escape-values",
149 			"Whether escaping values automatically, when inserting",
150 			NULL,
151 			TRUE,
152 			G_PARAM_READWRITE));
153 }
154 
155 static GtkWidget *
in_scrolled_window(GtkWidget * widget)156 in_scrolled_window (GtkWidget *widget)
157 {
158 	GtkWidget *sw;
159 
160 	g_return_val_if_fail (widget != NULL, NULL);
161 
162 	sw = gtk_scrolled_window_new (NULL, NULL);
163 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
164 	gtk_container_add (GTK_CONTAINER (sw), widget);
165 
166 	gtk_widget_show (widget);
167 	gtk_widget_show (sw);
168 
169 	return sw;
170 }
171 
172 static void
e_web_view_preview_init(EWebViewPreview * preview)173 e_web_view_preview_init (EWebViewPreview *preview)
174 {
175 	GtkWidget *tree_view_sw, *web_view;
176 
177 	preview->priv = E_WEB_VIEW_PREVIEW_GET_PRIVATE (preview);
178 	preview->priv->escape_values = TRUE;
179 
180 	gtk_orientable_set_orientation (GTK_ORIENTABLE (preview), GTK_ORIENTATION_VERTICAL);
181 
182 	tree_view_sw = in_scrolled_window (gtk_tree_view_new ());
183 	web_view = e_web_view_new ();
184 
185 	gtk_widget_hide (tree_view_sw);
186 	gtk_widget_show (web_view);
187 
188 	gtk_paned_pack1 (GTK_PANED (preview), tree_view_sw, FALSE, TRUE);
189 	gtk_paned_pack2 (GTK_PANED (preview), web_view, TRUE, TRUE);
190 
191 	/* rawly 3 lines of a text plus a little bit more */
192 	if (gtk_paned_get_position (GTK_PANED (preview)) < 85)
193 		gtk_paned_set_position (GTK_PANED (preview), 85);
194 }
195 
196 GtkWidget *
e_web_view_preview_new(void)197 e_web_view_preview_new (void)
198 {
199 	return g_object_new (E_TYPE_WEB_VIEW_PREVIEW, NULL);
200 }
201 
202 GtkTreeView *
e_web_view_preview_get_tree_view(EWebViewPreview * preview)203 e_web_view_preview_get_tree_view (EWebViewPreview *preview)
204 {
205 	g_return_val_if_fail (E_IS_WEB_VIEW_PREVIEW (preview), NULL);
206 
207 	return GTK_TREE_VIEW (gtk_bin_get_child (GTK_BIN (gtk_paned_get_child1 (GTK_PANED (preview)))));
208 }
209 
210 GtkWidget *
e_web_view_preview_get_preview(EWebViewPreview * preview)211 e_web_view_preview_get_preview (EWebViewPreview *preview)
212 {
213 	g_return_val_if_fail (E_IS_WEB_VIEW_PREVIEW (preview), NULL);
214 
215 	return gtk_paned_get_child2 (GTK_PANED (preview));
216 }
217 
218 void
e_web_view_preview_set_preview(EWebViewPreview * preview,GtkWidget * preview_widget)219 e_web_view_preview_set_preview (EWebViewPreview *preview,
220                                 GtkWidget *preview_widget)
221 {
222 	GtkWidget *old_child;
223 
224 	g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
225 	g_return_if_fail (GTK_IS_WIDGET (preview_widget));
226 
227 	old_child = gtk_paned_get_child2 (GTK_PANED (preview));
228 	if (old_child) {
229 		g_return_if_fail (old_child != preview_widget);
230 		gtk_widget_destroy (old_child);
231 	}
232 
233 	gtk_paned_pack2 (GTK_PANED (preview), preview_widget, TRUE, TRUE);
234 }
235 
236 void
e_web_view_preview_show_tree_view(EWebViewPreview * preview)237 e_web_view_preview_show_tree_view (EWebViewPreview *preview)
238 {
239 	g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
240 
241 	gtk_widget_show (gtk_paned_get_child1 (GTK_PANED (preview)));
242 }
243 
244 void
e_web_view_preview_hide_tree_view(EWebViewPreview * preview)245 e_web_view_preview_hide_tree_view (EWebViewPreview *preview)
246 {
247 	g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
248 
249 	gtk_widget_hide (gtk_paned_get_child1 (GTK_PANED (preview)));
250 }
251 
252 gboolean
e_web_view_preview_get_escape_values(EWebViewPreview * preview)253 e_web_view_preview_get_escape_values (EWebViewPreview *preview)
254 {
255 	g_return_val_if_fail (E_IS_WEB_VIEW_PREVIEW (preview), FALSE);
256 
257 	return preview->priv->escape_values;
258 }
259 
260 void
e_web_view_preview_set_escape_values(EWebViewPreview * preview,gboolean escape)261 e_web_view_preview_set_escape_values (EWebViewPreview *preview,
262                                       gboolean escape)
263 {
264 	g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
265 
266 	preview->priv->escape_values = escape;
267 }
268 
269 void
e_web_view_preview_begin_update(EWebViewPreview * preview)270 e_web_view_preview_begin_update (EWebViewPreview *preview)
271 {
272 	GtkStyleContext *style_context;
273 	GdkRGBA color;
274 	gchar *color_value;
275 
276 	g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
277 
278 	if (preview->priv->updating_content) {
279 		g_warning ("%s: Previous content update isn't finished with e_web_view_preview_end_update()", G_STRFUNC);
280 		g_string_free (preview->priv->updating_content, TRUE);
281 	}
282 
283 	style_context = gtk_widget_get_style_context (GTK_WIDGET (preview));
284 
285 	if (gtk_style_context_lookup_color (style_context, "theme_fg_color", &color))
286 		color_value = g_strdup_printf ("#%06x", e_rgba_to_value (&color));
287 	else
288 		color_value = g_strdup (E_UTILS_DEFAULT_THEME_FG_COLOR);
289 
290 	preview->priv->updating_content = g_string_sized_new (1024);
291 	g_string_append_printf (preview->priv->updating_content, "<BODY class=\"-e-web-view-background-color -e-web-view-text-color\" text=\"%s\">", color_value);
292 	g_string_append (preview->priv->updating_content, "<TABLE width=\"100%\" border=\"0\" cols=\"2\">");
293 
294 	g_free (color_value);
295 }
296 
297 void
e_web_view_preview_end_update(EWebViewPreview * preview)298 e_web_view_preview_end_update (EWebViewPreview *preview)
299 {
300 	GtkWidget *web_view;
301 
302 	g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
303 	g_return_if_fail (preview->priv->updating_content != NULL);
304 
305 	g_string_append (preview->priv->updating_content, "</TABLE></BODY>");
306 
307 	web_view = e_web_view_preview_get_preview (preview);
308 	if (E_IS_WEB_VIEW (web_view))
309 		e_web_view_load_string (E_WEB_VIEW (web_view), preview->priv->updating_content->str);
310 
311 	g_string_free (preview->priv->updating_content, TRUE);
312 	preview->priv->updating_content = NULL;
313 }
314 
315 static gchar *
replace_string(const gchar * text,const gchar * find,const gchar * replace)316 replace_string (const gchar *text,
317                 const gchar *find,
318                 const gchar *replace)
319 {
320 	const gchar *p, *next;
321 	GString *str;
322 	gint find_len;
323 
324 	g_return_val_if_fail (text != NULL, NULL);
325 	g_return_val_if_fail (find != NULL, NULL);
326 	g_return_val_if_fail (*find, NULL);
327 
328 	find_len = strlen (find);
329 	str = g_string_new ("");
330 
331 	p = text;
332 	while (next = strstr (p, find), next) {
333 		if (p + 1 < next)
334 			g_string_append_len (str, p, next - p);
335 
336 		if (replace && *replace)
337 			g_string_append (str, replace);
338 
339 		p = next + find_len;
340 	}
341 
342 	g_string_append (str, p);
343 
344 	return g_string_free (str, FALSE);
345 }
346 
347 static gchar *
web_view_preview_escape_text(EWebViewPreview * preview,const gchar * text)348 web_view_preview_escape_text (EWebViewPreview *preview,
349                               const gchar *text)
350 {
351 	gchar *utf8_valid, *res, *end;
352 
353 	if (!e_web_view_preview_get_escape_values (preview))
354 		return NULL;
355 
356 	g_return_val_if_fail (text != NULL, NULL);
357 
358 	if (g_utf8_validate (text, -1, NULL)) {
359 		res = g_markup_escape_text (text, -1);
360 	} else {
361 		utf8_valid = g_strdup (text);
362 		while (end = NULL, !g_utf8_validate (utf8_valid, -1, (const gchar **) &end) && end && *end)
363 			*end = '?';
364 
365 		res = g_markup_escape_text (utf8_valid, -1);
366 
367 		g_free (utf8_valid);
368 	}
369 
370 	if (res && strchr (res, '\n')) {
371 		/* replace line breaks with <BR> */
372 		if (strchr (res, '\r')) {
373 			end = replace_string (res, "\r", "");
374 			g_free (res);
375 			res = end;
376 		}
377 
378 		end = replace_string (res, "\n", "<BR>");
379 		g_free (res);
380 		res = end;
381 	}
382 
383 	return res;
384 }
385 
386 void
e_web_view_preview_add_header(EWebViewPreview * preview,gint index,const gchar * header)387 e_web_view_preview_add_header (EWebViewPreview *preview,
388                                gint index,
389                                const gchar *header)
390 {
391 	gchar *escaped;
392 
393 	g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
394 	g_return_if_fail (preview->priv->updating_content != NULL);
395 	g_return_if_fail (header != NULL);
396 
397 	if (index < 1)
398 		index = 1;
399 	else if (index > 6)
400 		index = 6;
401 
402 	escaped = web_view_preview_escape_text (preview, header);
403 	if (escaped)
404 		header = escaped;
405 
406 	g_string_append_printf (preview->priv->updating_content, "<TR><TD colspan=2><H%d>%s</H%d></TD></TR>", index, header, index);
407 
408 	g_free (escaped);
409 }
410 
411 void
e_web_view_preview_add_text(EWebViewPreview * preview,const gchar * text)412 e_web_view_preview_add_text (EWebViewPreview *preview,
413                              const gchar *text)
414 {
415 	gchar *escaped;
416 
417 	g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
418 	g_return_if_fail (preview->priv->updating_content != NULL);
419 	g_return_if_fail (text != NULL);
420 
421 	escaped = web_view_preview_escape_text (preview, text);
422 	if (escaped)
423 		text = escaped;
424 
425 	g_string_append_printf (preview->priv->updating_content, "<TR><TD colspan=2><FONT size=\"3\">%s</FONT></TD></TR>", text);
426 
427 	g_free (escaped);
428 }
429 
430 void
e_web_view_preview_add_raw_html(EWebViewPreview * preview,const gchar * raw_html)431 e_web_view_preview_add_raw_html (EWebViewPreview *preview,
432                                  const gchar *raw_html)
433 {
434 	g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
435 	g_return_if_fail (preview->priv->updating_content != NULL);
436 	g_return_if_fail (raw_html != NULL);
437 
438 	g_string_append_printf (preview->priv->updating_content, "<TR><TD colspan=2>%s</TD></TR>", raw_html);
439 }
440 
441 void
e_web_view_preview_add_separator(EWebViewPreview * preview)442 e_web_view_preview_add_separator (EWebViewPreview *preview)
443 {
444 	g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
445 	g_return_if_fail (preview->priv->updating_content != NULL);
446 
447 	g_string_append (preview->priv->updating_content, "<TR><TD colspan=2><HR></TD></TR>");
448 }
449 
450 void
e_web_view_preview_add_empty_line(EWebViewPreview * preview)451 e_web_view_preview_add_empty_line (EWebViewPreview *preview)
452 {
453 	g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
454 	g_return_if_fail (preview->priv->updating_content != NULL);
455 
456 	g_string_append (preview->priv->updating_content, "<TR><TD colspan=2>&nbsp;</TD></TR>");
457 }
458 
459 /* section can be NULL, but value cannot */
460 void
e_web_view_preview_add_section(EWebViewPreview * preview,const gchar * section,const gchar * value)461 e_web_view_preview_add_section (EWebViewPreview *preview,
462                                 const gchar *section,
463                                 const gchar *value)
464 {
465 	gchar *escaped_value;
466 
467 	g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
468 	g_return_if_fail (preview->priv->updating_content != NULL);
469 	g_return_if_fail (value != NULL);
470 
471 	escaped_value = web_view_preview_escape_text (preview, value);
472 	if (escaped_value)
473 		value = escaped_value;
474 
475 	e_web_view_preview_add_section_html (preview, section, value);
476 
477 	g_free (escaped_value);
478 }
479 
480 /* section can be NULL, but html cannot */
481 void
e_web_view_preview_add_section_html(EWebViewPreview * preview,const gchar * section,const gchar * html)482 e_web_view_preview_add_section_html (EWebViewPreview *preview,
483 				     const gchar *section,
484 				     const gchar *html)
485 {
486 	gchar *escaped_section = NULL;
487 
488 	g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
489 	g_return_if_fail (preview->priv->updating_content != NULL);
490 	g_return_if_fail (html != NULL);
491 
492 	if (section) {
493 		escaped_section = web_view_preview_escape_text (preview, section);
494 		if (escaped_section)
495 			section = escaped_section;
496 	}
497 
498 	g_string_append_printf (preview->priv->updating_content,
499 		"<TR>"
500 			"<TD width=\"10%%\" valign=\"top\" nowrap><FONT size=\"3\"><B>%s</B></FONT></TD>"
501 			"<TD width=\"90%%\"><FONT size=\"3\">%s</FONT></TD>"
502 		"</TR>", section ? section : "", html);
503 
504 	g_free (escaped_section);
505 }
506