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> </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