No issues found
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 *
15 *
16 * Authors:
17 * Federico Mena Quintero <federico@ximian.com>
18 * Damon Chaplin <damon@ximian.com>
19 * Rodrigo Moya <rodrigo@ximian.com>
20 *
21 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
22 *
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include "e-cal-component-preview.h"
30
31 #include <string.h>
32 #include <gtk/gtk.h>
33 #include <glib/gi18n.h>
34 #include <camel/camel.h>
35
36 #include <e-util/e-util.h>
37 #include <e-util/e-categories-config.h>
38
39 #define E_CAL_COMPONENT_PREVIEW_GET_PRIVATE(obj) \
40 (G_TYPE_INSTANCE_GET_PRIVATE \
41 ((obj), E_TYPE_CAL_COMPONENT_PREVIEW, ECalComponentPreviewPrivate))
42
43 G_DEFINE_TYPE (
44 ECalComponentPreview,
45 e_cal_component_preview,
46 E_TYPE_WEB_VIEW)
47
48 struct _ECalComponentPreviewPrivate {
49 /* information about currently showing component in a preview;
50 * if it didn't change then the preview is not updated */
51 gchar *cal_uid;
52 gchar *comp_uid;
53 struct icaltimetype comp_last_modified;
54 gint comp_sequence;
55
56 ECalClient *client;
57 ECalComponent *comp;
58 icaltimezone *timezone;
59 gboolean use_24_hour_format;
60 };
61
62 #define HTML_HEADER "<!doctype html public \"-//W3C//DTD HTML 4.0 TRANSITIONAL//EN\">\n<html>\n" \
63 "<head>\n<meta name=\"generator\" content=\"Evolution Calendar Component\">\n" \
64 "<link type=\"text/css\" rel=\"stylesheet\" href=\"evo-file://" EVOLUTION_PRIVDATADIR "/theme/webview.css\">\n" \
65 "<style>\n" \
66 ".description { font-family: monospace; font-size: 1em; }\n" \
67 "</style>\n" \
68 "</head>"
69
70 static void
71 clear_comp_info (ECalComponentPreview *preview)
72 {
73 ECalComponentPreviewPrivate *priv;
74
75 g_return_if_fail (E_IS_CAL_COMPONENT_PREVIEW (preview));
76
77 priv = preview->priv;
78
79 g_free (priv->cal_uid);
80 priv->cal_uid = NULL;
81 g_free (priv->comp_uid);
82 priv->comp_uid = NULL;
83 priv->comp_last_modified = icaltime_null_time ();
84 priv->comp_sequence = -1;
85
86 g_clear_object (&priv->client);
87 g_clear_object (&priv->comp);
88 if (priv->timezone) {
89 icaltimezone_free (priv->timezone, 1);
90 priv->timezone = NULL;
91 }
92 }
93
94 /* Stores information about actually shown component and
95 * returns whether component in the preview changed */
96 static gboolean
97 update_comp_info (ECalComponentPreview *preview,
98 ECalClient *client,
99 ECalComponent *comp,
100 icaltimezone *zone,
101 gboolean use_24_hour_format)
102 {
103 ECalComponentPreviewPrivate *priv;
104 gboolean changed;
105
106 g_return_val_if_fail (preview != NULL, TRUE);
107 g_return_val_if_fail (E_IS_CAL_COMPONENT_PREVIEW (preview), TRUE);
108
109 priv = preview->priv;
110
111 if (!E_IS_CAL_COMPONENT (comp) || !E_IS_CAL_CLIENT (client)) {
112 changed = !priv->cal_uid;
113 clear_comp_info (preview);
114 } else {
115 ESource *source;
116 const gchar *uid;
117 gchar *cal_uid;
118 gchar *comp_uid;
119 struct icaltimetype comp_last_modified, *itm = NULL;
120 gint *sequence = NULL;
121 gint comp_sequence;
122
123 source = e_client_get_source (E_CLIENT (client));
124 cal_uid = g_strdup (e_source_get_uid (source));
125 e_cal_component_get_uid (comp, &uid);
126 comp_uid = g_strdup (uid);
127 e_cal_component_get_last_modified (comp, &itm);
128 if (itm) {
129 comp_last_modified = *itm;
130 e_cal_component_free_icaltimetype (itm);
131 } else
132 comp_last_modified = icaltime_null_time ();
133 e_cal_component_get_sequence (comp, &sequence);
134 if (sequence) {
135 comp_sequence = *sequence;
136 e_cal_component_free_sequence (sequence);
137 } else
138 comp_sequence = 0;
139
140 changed = !priv->cal_uid || !priv->comp_uid || !cal_uid || !comp_uid ||
141 !g_str_equal (priv->cal_uid, cal_uid) ||
142 !g_str_equal (priv->comp_uid, comp_uid) ||
143 priv->comp_sequence != comp_sequence ||
144 icaltime_compare (priv->comp_last_modified, comp_last_modified) != 0;
145
146 clear_comp_info (preview);
147
148 priv->cal_uid = cal_uid;
149 priv->comp_uid = comp_uid;
150 priv->comp_sequence = comp_sequence;
151 priv->comp_last_modified = comp_last_modified;
152
153 priv->comp = g_object_ref (comp);
154 priv->client = g_object_ref (client);
155 priv->timezone = icaltimezone_copy (zone);
156 priv->use_24_hour_format = use_24_hour_format;
157 }
158
159 return changed;
160 }
161
162 /* Converts a time_t to a string, relative to the specified timezone */
163 static gchar *
164 timet_to_str_with_zone (ECalComponentDateTime *dt,
165 ECalClient *client,
166 icaltimezone *default_zone,
167 gboolean use_24_hour_format)
168 {
169 struct icaltimetype itt;
170 icaltimezone *zone;
171 struct tm tm;
172 gchar buf[256];
173
174 if (dt->tzid) {
175 /* If we can't find the zone, we'll guess its "local" */
176 if (!e_cal_client_get_timezone_sync (client, dt->tzid, &zone, NULL, NULL))
177 zone = NULL;
178 } else if (dt->value->is_utc) {
179 zone = icaltimezone_get_utc_timezone ();
180 } else {
181 zone = NULL;
182 }
183
184 itt = *dt->value;
185 if (zone)
186 icaltimezone_convert_time (&itt, zone, default_zone);
187 tm = icaltimetype_to_tm (&itt);
188
189 e_time_format_date_and_time (
190 &tm, use_24_hour_format,
191 FALSE, FALSE, buf, sizeof (buf));
192
193 return g_locale_to_utf8 (buf, -1, NULL, NULL, NULL);
194 }
195
196 static void
197 cal_component_preview_write_html (ECalComponentPreview *preview,
198 GString *buffer)
199 {
200 ECalClient *client;
201 ECalComponent *comp;
202 icaltimezone *default_zone;
203 gboolean use_24_hour_format;
204 ECalComponentText text;
205 ECalComponentDateTime dt;
206 gchar *str;
207 GString *string;
208 GSList *list, *iter;
209 icalcomponent *icalcomp;
210 icalproperty *icalprop;
211 icalproperty_status status;
212 const gchar *location;
213 gint *priority_value;
214 GtkStyle *style;
215 GtkStateType state;
216
217 client = preview->priv->client;
218 comp = preview->priv->comp;
219 default_zone = preview->priv->timezone;
220 use_24_hour_format = preview->priv->use_24_hour_format;
221
222 /* write document header */
223 e_cal_component_get_summary (comp, &text);
224
225 style = gtk_widget_get_style (GTK_WIDGET (preview));
226 state = gtk_widget_get_state (GTK_WIDGET (preview));
227
228 g_string_append (buffer, HTML_HEADER);
229 g_string_append_printf (
230 buffer, "<body bgcolor=\"#%06x\" text=\"#%06x\">",
231 e_color_to_value (&style->base[state]),
232 e_color_to_value (&style->text[state]));
233
234 if (text.value)
235 g_string_append_printf (buffer, "<h2>%s</h2>", text.value);
236 else
237 g_string_append_printf (buffer, "<h2><i>%s</i></h2>",_("Untitled"));
238
239 g_string_append (buffer, "<table border=\"0\" cellspacing=\"5\">");
240
241 /* write icons for the categories */
242 string = g_string_new (NULL);
243 e_cal_component_get_categories_list (comp, &list);
244 if (list != NULL)
245 g_string_append_printf (buffer, "<tr><th>%s</th><td>", _("Categories:"));
246 for (iter = list; iter != NULL; iter = iter->next) {
247 const gchar *category = iter->data;
248 const gchar *icon_file;
249
250 icon_file = e_categories_get_icon_file_for (category);
251 if (icon_file && g_file_test (icon_file, G_FILE_TEST_EXISTS)) {
252 gchar *uri;
253
254 uri = g_filename_to_uri (icon_file, NULL, NULL);
255 g_string_append_printf (
256 buffer, "<img alt=\"%s\" src=\"evo-%s\">",
257 category, uri);
258 g_free (uri);
259 } else {
260 if (iter != list)
261 g_string_append_len (string, ", ", 2);
262 g_string_append (string, category);
263 }
264 }
265 if (string->len > 0)
266 g_string_append_printf (buffer, "%s", string->str);
267 if (list != NULL)
268 g_string_append (buffer, "</td></tr>");
269 e_cal_component_free_categories_list (list);
270 g_string_free (string, TRUE);
271
272 /* write location */
273 e_cal_component_get_location (comp, &location);
274 if (location)
275 g_string_append_printf (
276 buffer, "<tr><th>%s</th><td>%s</td></tr>",
277 _("Summary:"), text.value);
278
279 /* write start date */
280 e_cal_component_get_dtstart (comp, &dt);
281 if (dt.value != NULL) {
282 str = timet_to_str_with_zone (
283 &dt, client, default_zone, use_24_hour_format);
284 g_string_append_printf (
285 buffer, "<tr><th>%s</th><td>%s</td></tr>",
286 _("Start Date:"), str);
287 g_free (str);
288 }
289 e_cal_component_free_datetime (&dt);
290
291 /* write end date */
292 e_cal_component_get_dtend (comp, &dt);
293 if (dt.value != NULL) {
294 str = timet_to_str_with_zone (
295 &dt, client, default_zone, use_24_hour_format);
296 g_string_append_printf (
297 buffer,"<tr><th>%s</th><td>%s</td></tr>",
298 _("End Date:"), str);
299 g_free (str);
300 }
301 e_cal_component_free_datetime (&dt);
302
303 /* write Due Date */
304 e_cal_component_get_due (comp, &dt);
305 if (dt.value != NULL) {
306 str = timet_to_str_with_zone (
307 &dt, client, default_zone, use_24_hour_format);
308 g_string_append_printf (
309 buffer, "<tr><th>%s</th><td>%s</td></tr>",
310 _("Due Date:"), str);
311 g_free (str);
312 }
313 e_cal_component_free_datetime (&dt);
314
315 /* write status */
316 icalcomp = e_cal_component_get_icalcomponent (comp);
317 icalprop = icalcomponent_get_first_property (
318 icalcomp, ICAL_STATUS_PROPERTY);
319 if (icalprop != NULL) {
320 g_string_append_printf (
321 buffer, "<tr><th>%s</th>",
322 _("Status:"));
323 e_cal_component_get_status (comp, &status);
324 switch (status) {
325 case ICAL_STATUS_INPROCESS :
326 str = g_strdup (_("In Progress"));
327 break;
328 case ICAL_STATUS_COMPLETED :
329 str = g_strdup (_("Completed"));
330 break;
331 case ICAL_STATUS_CANCELLED :
332 str = g_strdup (_("Canceled"));
333 break;
334 case ICAL_STATUS_NONE :
335 default :
336 str = g_strdup (_("Not Started"));
337 break;
338 }
339
340 g_string_append_printf (buffer, "<td>%s</td></tr>", str);
341 g_free (str);
342 }
343
344 /* write priority */
345 e_cal_component_get_priority (comp, &priority_value);
346 if (priority_value && *priority_value != 0) {
347 g_string_append_printf (
348 buffer, "<tr><th>%s</th>",
349 _("Priority:"));
350 if (*priority_value <= 4)
351 str = g_strdup (_("High"));
352 else if (*priority_value == 5)
353 str = g_strdup (_("Normal"));
354 else
355 str = g_strdup (_("Low"));
356
357 g_string_append_printf (buffer, "<td>%s</td></tr>", str);
358
359 g_free (str);
360 }
361
362 if (priority_value)
363 e_cal_component_free_priority (priority_value);
364
365 /* write description and URL */
366 g_string_append (buffer, "<tr><td colspan=\"2\"><hr></td></tr>");
367
368 e_cal_component_get_description_list (comp, &list);
369 if (list) {
370 GSList *node;
371
372 g_string_append_printf (
373 buffer, "<tr><th>%s</th>",
374 _("Description:"));
375
376 g_string_append (buffer, "<td class=\"description\">");
377
378 for (node = list; node != NULL; node = node->next) {
379 gchar *html;
380
381 text = * (ECalComponentText *) node->data;
382 html = camel_text_to_html (
383 text.value ? text.value : "",
384 CAMEL_MIME_FILTER_TOHTML_CONVERT_NL |
385 CAMEL_MIME_FILTER_TOHTML_CONVERT_SPACES |
386 CAMEL_MIME_FILTER_TOHTML_CONVERT_URLS |
387 CAMEL_MIME_FILTER_TOHTML_CONVERT_ADDRESSES, 0);
388
389 if (html)
390 g_string_append_printf (buffer, "%s", html);
391
392 g_free (html);
393 }
394
395 g_string_append (buffer, "</td></tr>");
396
397 e_cal_component_free_text_list (list);
398 }
399
400 /* URL */
401 e_cal_component_get_url (comp, (const gchar **) &str);
402 if (str) {
403 g_string_append_printf (
404 buffer, "<tr><th>%s</th><td><a href=\"%s\">%s</a></td></tr>",
405 _("Web Page:"), str, str);
406 }
407
408 g_string_append (buffer, "</table>");
409
410 /* close document */
411 g_string_append (buffer, "</body></html>");
412 }
413
414 static void
415 load_comp (ECalComponentPreview *preview)
416 {
417 GString *buffer;
418
419 if (!preview->priv->comp) {
420 e_cal_component_preview_clear (preview);
421 return;
422 }
423
424 buffer = g_string_sized_new (4096);
425 cal_component_preview_write_html (preview, buffer);
426 e_web_view_load_string (E_WEB_VIEW (preview), buffer->str);
427 g_string_free (buffer, TRUE);
428 }
429
430 static void
431 cal_component_preview_finalize (GObject *object)
432 {
433 clear_comp_info (E_CAL_COMPONENT_PREVIEW (object));
434
435 /* Chain up to parent's finalize() method. */
436 G_OBJECT_CLASS (e_cal_component_preview_parent_class)->finalize (object);
437 }
438
439 static void
440 e_cal_component_preview_class_init (ECalComponentPreviewClass *class)
441 {
442 GObjectClass *object_class;
443
444 g_type_class_add_private (class, sizeof (ECalComponentPreviewPrivate));
445
446 object_class = G_OBJECT_CLASS (class);
447 object_class->finalize = cal_component_preview_finalize;
448 }
449
450 static void
451 e_cal_component_preview_init (ECalComponentPreview *preview)
452 {
453 preview->priv = E_CAL_COMPONENT_PREVIEW_GET_PRIVATE (preview);
454
455 g_signal_connect (
456 preview, "style-set",
457 G_CALLBACK (load_comp), NULL);
458 }
459
460 GtkWidget *
461 e_cal_component_preview_new (void)
462 {
463 return g_object_new (E_TYPE_CAL_COMPONENT_PREVIEW, NULL);
464 }
465
466 void
467 e_cal_component_preview_display (ECalComponentPreview *preview,
468 ECalClient *client,
469 ECalComponent *comp,
470 icaltimezone *zone,
471 gboolean use_24_hour_format)
472 {
473 g_return_if_fail (E_IS_CAL_COMPONENT_PREVIEW (preview));
474 g_return_if_fail (E_IS_CAL_COMPONENT (comp));
475
476 /* do not update preview when setting the same component as last time,
477 * which even didn't change */
478 if (!update_comp_info (preview, client, comp, zone, use_24_hour_format))
479 return;
480
481 load_comp (preview);
482 }
483
484 void
485 e_cal_component_preview_clear (ECalComponentPreview *preview)
486 {
487 g_return_if_fail (E_IS_CAL_COMPONENT_PREVIEW (preview));
488
489 clear_comp_info (preview);
490 e_web_view_clear (E_WEB_VIEW (preview));
491 }