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 * Damon Chaplin <damon@ximian.com>
18 *
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20 *
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <time.h>
28 #include <string.h>
29 #include <glib/gi18n.h>
30 #include <misc/e-map.h>
31
32 #include <libecal/libecal.h>
33
34 #include "e-util/e-util.h"
35 #include "e-util/e-util-private.h"
36
37 #include "e-timezone-dialog.h"
38
39 #ifdef G_OS_WIN32
40 #ifdef gmtime_r
41 #undef gmtime_r
42 #endif
43 #ifdef localtime_r
44 #undef localtime_r
45 #endif
46
47 /* The gmtime() and localtime() in Microsoft's C library are MT-safe */
48 #define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0)
49 #define localtime_r(tp,tmp) (localtime(tp)?(*(tmp)=*localtime(tp),(tmp)):0)
50 #endif
51
52 #define E_TIMEZONE_DIALOG_MAP_POINT_NORMAL_RGBA 0xc070a0ff
53 #define E_TIMEZONE_DIALOG_MAP_POINT_HOVER_RGBA 0xffff60ff
54 #define E_TIMEZONE_DIALOG_MAP_POINT_SELECTED_1_RGBA 0xff60e0ff
55 #define E_TIMEZONE_DIALOG_MAP_POINT_SELECTED_2_RGBA 0x000000ff
56
57 #define E_TIMEZONE_DIALOG_GET_PRIVATE(obj) \
58 (G_TYPE_INSTANCE_GET_PRIVATE \
59 ((obj), E_TYPE_TIMEZONE_DIALOG, ETimezoneDialogPrivate))
60
61 struct _ETimezoneDialogPrivate {
62 /* The selected timezone. May be NULL for a 'local time' (i.e. when
63 * the displayed name is ""). */
64 icaltimezone *zone;
65
66 GtkBuilder *builder;
67
68 EMapPoint *point_selected;
69 EMapPoint *point_hover;
70
71 EMap *map;
72
73 /* The timeout used to flash the nearest point. */
74 guint timeout_id;
75
76 /* Widgets from the UI file */
77 GtkWidget *app;
78 GtkWidget *table;
79 GtkWidget *map_window;
80 GtkWidget *timezone_combo;
81 GtkWidget *preview_label;
82 };
83
84 static void e_timezone_dialog_dispose (GObject *object);
85
86 static gboolean get_widgets (ETimezoneDialog *etd);
87 static gboolean on_map_timeout (gpointer data);
88 static gboolean on_map_motion (GtkWidget *widget,
89 GdkEventMotion *event,
90 gpointer data);
91 static gboolean on_map_leave (GtkWidget *widget,
92 GdkEventCrossing *event,
93 gpointer data);
94 static gboolean on_map_visibility_changed (GtkWidget *w,
95 GdkEventVisibility *event,
96 gpointer data);
97 static gboolean on_map_button_pressed (GtkWidget *w,
98 GdkEventButton *event,
99 gpointer data);
100
101 static icaltimezone * get_zone_from_point (ETimezoneDialog *etd,
102 EMapPoint *point);
103 static void set_map_timezone (ETimezoneDialog *etd,
104 icaltimezone *zone);
105 static void on_combo_changed (GtkComboBox *combo,
106 ETimezoneDialog *etd);
107
108 static void timezone_combo_get_active_text (GtkComboBox *combo,
109 gchar **zone_name);
110 static gboolean timezone_combo_set_active_text (GtkComboBox *combo,
111 const gchar *zone_name);
112
113 static void map_destroy_cb (gpointer data,
114 GObject *where_object_was);
115
116 G_DEFINE_TYPE (ETimezoneDialog, e_timezone_dialog, G_TYPE_OBJECT)
117
118 /* Class initialization function for the event editor */
119 static void
120 e_timezone_dialog_class_init (ETimezoneDialogClass *class)
121 {
122 GObjectClass *object_class;
123
124 g_type_class_add_private (class, sizeof (ETimezoneDialogPrivate));
125
126 object_class = G_OBJECT_CLASS (class);
127 object_class->dispose = e_timezone_dialog_dispose;
128 }
129
130 /* Object initialization function for the event editor */
131 static void
132 e_timezone_dialog_init (ETimezoneDialog *etd)
133 {
134 etd->priv = E_TIMEZONE_DIALOG_GET_PRIVATE (etd);
135 }
136
137 /* Dispose handler for the event editor */
138 static void
139 e_timezone_dialog_dispose (GObject *object)
140 {
141 ETimezoneDialogPrivate *priv;
142
143 priv = E_TIMEZONE_DIALOG_GET_PRIVATE (object);
144
145 /* Destroy the actual dialog. */
146 if (priv->app != NULL) {
147 gtk_widget_destroy (priv->app);
148 priv->app = NULL;
149 }
150
151 if (priv->timeout_id) {
152 g_source_remove (priv->timeout_id);
153 priv->timeout_id = 0;
154 }
155
156 if (priv->builder) {
157 g_object_unref (priv->builder);
158 priv->builder = NULL;
159 }
160
161 /* Chain up to parent's dispose() method. */
162 G_OBJECT_CLASS (e_timezone_dialog_parent_class)->dispose (object);
163 }
164
165 static void
166 e_timezone_dialog_add_timezones (ETimezoneDialog *etd)
167 {
168 ETimezoneDialogPrivate *priv;
169 icalarray *zones;
170 GtkComboBox *combo;
171 GList *l, *list_items = NULL;
172 GtkListStore *list_store;
173 GtkTreeIter iter;
174 GtkCellRenderer *cell;
175 GHashTable *index;
176 gint i;
177
178 priv = etd->priv;
179
180 /* Get the array of builtin timezones. */
181 zones = icaltimezone_get_builtin_timezones ();
182
183 for (i = 0; i < zones->num_elements; i++) {
184 icaltimezone *zone;
185 gchar *location;
186
187 zone = icalarray_element_at (zones, i);
188
189 location = _(icaltimezone_get_location (zone));
190
191 e_map_add_point (
192 priv->map, location,
193 icaltimezone_get_longitude (zone),
194 icaltimezone_get_latitude (zone),
195 E_TIMEZONE_DIALOG_MAP_POINT_NORMAL_RGBA);
196
197 list_items = g_list_prepend (list_items, location);
198 }
199
200 list_items = g_list_sort (list_items, (GCompareFunc) g_utf8_collate);
201
202 /* Put the "UTC" entry at the top of the combo's list. */
203 list_items = g_list_prepend (list_items, _("UTC"));
204
205 combo = GTK_COMBO_BOX (priv->timezone_combo);
206
207 cell = gtk_cell_renderer_text_new ();
208 gtk_cell_layout_pack_start ((GtkCellLayout *) combo, cell, TRUE);
209 gtk_cell_layout_set_attributes ((GtkCellLayout *) combo, cell, "text", 0, NULL);
210
211 list_store = gtk_list_store_new (1, G_TYPE_STRING);
212 index = g_hash_table_new (g_str_hash, g_str_equal);
213 for (l = list_items, i = 0; l != NULL; l = l->next, ++i) {
214 gtk_list_store_append (list_store, &iter);
215 gtk_list_store_set (list_store, &iter, 0, (gchar *)(l->data), -1);
216 g_hash_table_insert (index, (gchar *)(l->data), GINT_TO_POINTER (i));
217 }
218
219 g_object_set_data_full (
220 G_OBJECT (list_store), "index", index,
221 (GDestroyNotify) g_hash_table_destroy);
222
223 gtk_combo_box_set_model (combo, (GtkTreeModel *) list_store);
224
225 gtk_rc_parse_string (
226 "style \"e-timezone-combo-style\" {\n"
227 " GtkComboBox::appears-as-list = 1\n"
228 "}\n"
229 "\n"
230 "widget \"*.e-timezone-dialog-combo\" style \"e-timezone-combo-style\"");
231
232 gtk_widget_set_name (priv->timezone_combo, "e-timezone-dialog-combo");
233
234 g_list_free (list_items);
235 }
236
237 ETimezoneDialog *
238 e_timezone_dialog_construct (ETimezoneDialog *etd)
239 {
240 ETimezoneDialogPrivate *priv;
241 GtkWidget *widget;
242 GtkWidget *map;
243
244 g_return_val_if_fail (etd != NULL, NULL);
245 g_return_val_if_fail (E_IS_TIMEZONE_DIALOG (etd), NULL);
246
247 priv = etd->priv;
248
249 /* Load the content widgets */
250
251 priv->builder = gtk_builder_new ();
252 e_load_ui_builder_definition (priv->builder, "e-timezone-dialog.ui");
253
254 if (!get_widgets (etd)) {
255 g_message (
256 "%s(): Could not find all widgets in the XML file!",
257 G_STRFUNC);
258 goto error;
259 }
260
261 widget = gtk_dialog_get_content_area (GTK_DIALOG (priv->app));
262 gtk_container_set_border_width (GTK_CONTAINER (widget), 0);
263
264 widget = gtk_dialog_get_action_area (GTK_DIALOG (priv->app));
265 gtk_container_set_border_width (GTK_CONTAINER (widget), 12);
266
267 priv->map = e_map_new ();
268 map = GTK_WIDGET (priv->map);
269
270 g_object_weak_ref (G_OBJECT (map), map_destroy_cb, priv);
271
272 gtk_widget_set_events (
273 map,
274 gtk_widget_get_events (map) |
275 GDK_LEAVE_NOTIFY_MASK |
276 GDK_VISIBILITY_NOTIFY_MASK);
277
278 e_timezone_dialog_add_timezones (etd);
279
280 gtk_container_add (GTK_CONTAINER (priv->map_window), map);
281 gtk_widget_show (map);
282
283 /* Ensure a reasonable minimum amount of map is visible */
284 gtk_widget_set_size_request (priv->map_window, 200, 200);
285
286 g_signal_connect (
287 map, "motion-notify-event",
288 G_CALLBACK (on_map_motion), etd);
289 g_signal_connect (
290 map, "leave-notify-event",
291 G_CALLBACK (on_map_leave), etd);
292 g_signal_connect (
293 map, "visibility-notify-event",
294 G_CALLBACK (on_map_visibility_changed), etd);
295 g_signal_connect (
296 map, "button-press-event",
297 G_CALLBACK (on_map_button_pressed), etd);
298
299 g_signal_connect (
300 priv->timezone_combo, "changed",
301 G_CALLBACK (on_combo_changed), etd);
302
303 return etd;
304
305 error:
306
307 g_object_unref (etd);
308 return NULL;
309 }
310
311 #if 0
312 static gint
313 get_local_offset (void)
314 {
315 time_t now = time (NULL), t_gmt, t_local;
316 struct tm gmt, local;
317 gint diff;
318
319 gmtime_r (&now, &gmt);
320 localtime_r (&now, &local);
321 t_gmt = mktime (&gmt);
322 t_local = mktime (&local);
323 diff = t_local - t_gmt;
324
325 return diff;
326 }
327 #endif
328
329 static icaltimezone *
330 get_local_timezone (void)
331 {
332 icaltimezone *zone;
333 gchar *location;
334
335 tzset ();
336 location = e_cal_system_timezone_get_location ();
337
338 if (location)
339 zone = icaltimezone_get_builtin_timezone (location);
340 else
341 zone = icaltimezone_get_utc_timezone ();
342
343 g_free (location);
344
345 return zone;
346 }
347
348 /* Gets the widgets from the XML file and returns if they are all available.
349 * For the widgets whose values can be simply set with e-dialog-utils, it does
350 * that as well.
351 */
352 static gboolean
353 get_widgets (ETimezoneDialog *etd)
354 {
355 ETimezoneDialogPrivate *priv;
356 GtkBuilder *builder;
357
358 priv = etd->priv;
359 builder = etd->priv->builder;
360
361 priv->app = e_builder_get_widget (builder, "timezone-dialog");
362 priv->map_window = e_builder_get_widget (builder, "map-window");
363 priv->timezone_combo = e_builder_get_widget (builder, "timezone-combo");
364 priv->table = e_builder_get_widget (builder, "timezone-table");
365 priv->preview_label = e_builder_get_widget (builder, "preview-label");
366
367 return (priv->app
368 && priv->map_window
369 && priv->timezone_combo
370 && priv->table
371 && priv->preview_label);
372 }
373
374 /**
375 * e_timezone_dialog_new:
376 *
377 * Creates a new event editor dialog.
378 *
379 * Return value: A newly-created event editor dialog, or NULL if the event
380 * editor could not be created.
381 **/
382 ETimezoneDialog *
383 e_timezone_dialog_new (void)
384 {
385 ETimezoneDialog *etd;
386
387 etd = E_TIMEZONE_DIALOG (g_object_new (E_TYPE_TIMEZONE_DIALOG, NULL));
388 return e_timezone_dialog_construct (E_TIMEZONE_DIALOG (etd));
389 }
390
391 static void
392 format_utc_offset (gint utc_offset,
393 gchar *buffer)
394 {
395 const gchar *sign = "+";
396 gint hours, minutes, seconds;
397
398 if (utc_offset < 0) {
399 utc_offset = -utc_offset;
400 sign = "-";
401 }
402
403 hours = utc_offset / 3600;
404 minutes = (utc_offset % 3600) / 60;
405 seconds = utc_offset % 60;
406
407 /* Sanity check. Standard timezone offsets shouldn't be much more
408 * than 12 hours, and daylight saving shouldn't change it by more
409 * than a few hours. (The maximum offset is 15 hours 56 minutes
410 * at present.) */
411 if (hours < 0 || hours >= 24 || minutes < 0 || minutes >= 60
412 || seconds < 0 || seconds >= 60) {
413 fprintf (
414 stderr, "Warning: Strange timezone offset: "
415 "H:%i M:%i S:%i\n", hours, minutes, seconds);
416 }
417
418 if (hours == 0 && minutes == 0 && seconds == 0)
419 strcpy (buffer, _("UTC"));
420 else if (seconds == 0)
421 sprintf (
422 buffer, "%s %s%02i:%02i",
423 _("UTC"), sign, hours, minutes);
424 else
425 sprintf (
426 buffer, "%s %s%02i:%02i:%02i",
427 _("UTC"), sign, hours, minutes, seconds);
428 }
429
430 static gchar *
431 zone_display_name_with_offset (icaltimezone *zone)
432 {
433 const gchar *display_name;
434 struct tm local;
435 struct icaltimetype tt;
436 gint offset;
437 gchar buffer[100];
438 time_t now = time (NULL);
439
440 gmtime_r ((const time_t *) &now, &local);
441 tt = tm_to_icaltimetype (&local, TRUE);
442 offset = icaltimezone_get_utc_offset (zone, &tt, NULL);
443
444 format_utc_offset (offset, buffer);
445
446 display_name = icaltimezone_get_display_name (zone);
447 if (icaltimezone_get_builtin_timezone (display_name))
448 display_name = _(display_name);
449
450 return g_strdup_printf ("%s (%s)", display_name, buffer);
451 }
452
453 static const gchar *
454 zone_display_name (icaltimezone *zone)
455 {
456 const gchar *display_name;
457
458 display_name = icaltimezone_get_display_name (zone);
459 if (icaltimezone_get_builtin_timezone (display_name))
460 display_name = _(display_name);
461
462 return display_name;
463 }
464
465 /* This flashes the currently selected timezone in the map. */
466 static gboolean
467 on_map_timeout (gpointer data)
468 {
469 ETimezoneDialog *etd;
470 ETimezoneDialogPrivate *priv;
471
472 etd = E_TIMEZONE_DIALOG (data);
473 priv = etd->priv;
474
475 if (!priv->point_selected)
476 return TRUE;
477
478 if (e_map_point_get_color_rgba (priv->point_selected)
479 == E_TIMEZONE_DIALOG_MAP_POINT_SELECTED_1_RGBA)
480 e_map_point_set_color_rgba (
481 priv->map, priv->point_selected,
482 E_TIMEZONE_DIALOG_MAP_POINT_SELECTED_2_RGBA);
483 else
484 e_map_point_set_color_rgba (
485 priv->map, priv->point_selected,
486 E_TIMEZONE_DIALOG_MAP_POINT_SELECTED_1_RGBA);
487
488 return TRUE;
489 }
490
491 static gboolean
492 on_map_motion (GtkWidget *widget,
493 GdkEventMotion *event,
494 gpointer data)
495 {
496 ETimezoneDialog *etd;
497 ETimezoneDialogPrivate *priv;
498 gdouble longitude, latitude;
499 icaltimezone *new_zone;
500 gchar *display = NULL;
501
502 etd = E_TIMEZONE_DIALOG (data);
503 priv = etd->priv;
504
505 e_map_window_to_world (
506 priv->map, (gdouble) event->x, (gdouble) event->y,
507 &longitude, &latitude);
508
509 if (priv->point_hover && priv->point_hover != priv->point_selected)
510 e_map_point_set_color_rgba (
511 priv->map, priv->point_hover,
512 E_TIMEZONE_DIALOG_MAP_POINT_NORMAL_RGBA);
513
514 priv->point_hover = e_map_get_closest_point (
515 priv->map, longitude,
516 latitude, TRUE);
517
518 if (priv->point_hover != priv->point_selected)
519 e_map_point_set_color_rgba (
520 priv->map, priv->point_hover,
521 E_TIMEZONE_DIALOG_MAP_POINT_HOVER_RGBA);
522
523 new_zone = get_zone_from_point (etd, priv->point_hover);
524
525 display = zone_display_name_with_offset (new_zone);
526 gtk_label_set_text (GTK_LABEL (priv->preview_label), display);
527
528 g_free (display);
529
530 return TRUE;
531 }
532
533 static gboolean
534 on_map_leave (GtkWidget *widget,
535 GdkEventCrossing *event,
536 gpointer data)
537 {
538 ETimezoneDialog *etd;
539 ETimezoneDialogPrivate *priv;
540
541 etd = E_TIMEZONE_DIALOG (data);
542 priv = etd->priv;
543
544 /* We only want to reset the hover point if this is a normal leave
545 * event. For some reason we are getting leave events when the
546 * button is pressed in the map, which causes problems. */
547 if (event->mode != GDK_CROSSING_NORMAL)
548 return FALSE;
549
550 if (priv->point_hover && priv->point_hover != priv->point_selected)
551 e_map_point_set_color_rgba (
552 priv->map, priv->point_hover,
553 E_TIMEZONE_DIALOG_MAP_POINT_NORMAL_RGBA);
554
555 timezone_combo_set_active_text (
556 GTK_COMBO_BOX (priv->timezone_combo),
557 zone_display_name (priv->zone));
558 gtk_label_set_text (GTK_LABEL (priv->preview_label), "");
559
560 priv->point_hover = NULL;
561
562 return FALSE;
563 }
564
565 static gboolean
566 on_map_visibility_changed (GtkWidget *w,
567 GdkEventVisibility *event,
568 gpointer data)
569 {
570 ETimezoneDialog *etd;
571 ETimezoneDialogPrivate *priv;
572
573 etd = E_TIMEZONE_DIALOG (data);
574 priv = etd->priv;
575
576 if (event->state != GDK_VISIBILITY_FULLY_OBSCURED) {
577 /* Map is visible, at least partly, so make sure we flash the
578 * selected point. */
579 if (!priv->timeout_id)
580 priv->timeout_id = g_timeout_add (100, on_map_timeout, etd);
581 } else {
582 /* Map is invisible, so don't waste resources on the timeout.*/
583 if (priv->timeout_id) {
584 g_source_remove (priv->timeout_id);
585 priv->timeout_id = 0;
586 }
587 }
588
589 return FALSE;
590 }
591
592 static gboolean
593 on_map_button_pressed (GtkWidget *w,
594 GdkEventButton *event,
595 gpointer data)
596 {
597 ETimezoneDialog *etd;
598 ETimezoneDialogPrivate *priv;
599 gdouble longitude, latitude;
600
601 etd = E_TIMEZONE_DIALOG (data);
602 priv = etd->priv;
603
604 e_map_window_to_world (
605 priv->map, (gdouble) event->x, (gdouble) event->y,
606 &longitude, &latitude);
607
608 if (event->button != 1) {
609 e_map_zoom_out (priv->map);
610 } else {
611 if (e_map_get_magnification (priv->map) <= 1.0)
612 e_map_zoom_to_location (
613 priv->map, longitude, latitude);
614
615 if (priv->point_selected)
616 e_map_point_set_color_rgba (
617 priv->map,
618 priv->point_selected,
619 E_TIMEZONE_DIALOG_MAP_POINT_NORMAL_RGBA);
620 priv->point_selected = priv->point_hover;
621
622 priv->zone = get_zone_from_point (etd, priv->point_selected);
623 timezone_combo_set_active_text (
624 GTK_COMBO_BOX (priv->timezone_combo),
625 zone_display_name (priv->zone));
626 }
627
628 return TRUE;
629 }
630
631 /* Returns the translated timezone location of the given EMapPoint,
632 * e.g. "Europe/London". */
633 static icaltimezone *
634 get_zone_from_point (ETimezoneDialog *etd,
635 EMapPoint *point)
636 {
637 icalarray *zones;
638 gdouble longitude, latitude;
639 gint i;
640
641 if (point == NULL)
642 return NULL;
643
644 e_map_point_get_location (point, &longitude, &latitude);
645
646 /* Get the array of builtin timezones. */
647 zones = icaltimezone_get_builtin_timezones ();
648
649 for (i = 0; i < zones->num_elements; i++) {
650 icaltimezone *zone;
651 gdouble zone_longitude, zone_latitude;
652
653 zone = icalarray_element_at (zones, i);
654 zone_longitude = icaltimezone_get_longitude (zone);
655 zone_latitude = icaltimezone_get_latitude (zone);
656
657 if (zone_longitude - 0.005 <= longitude &&
658 zone_longitude + 0.005 >= longitude &&
659 zone_latitude - 0.005 <= latitude &&
660 zone_latitude + 0.005 >= latitude)
661 {
662 return zone;
663 }
664 }
665
666 g_return_val_if_reached (NULL);
667 }
668
669 /**
670 * e_timezone_dialog_get_timezone:
671 * @etd: the timezone dialog
672 *
673 * Return value: the currently-selected timezone, or %NULL if no timezone
674 * is selected.
675 **/
676 icaltimezone *
677 e_timezone_dialog_get_timezone (ETimezoneDialog *etd)
678 {
679 ETimezoneDialogPrivate *priv;
680
681 g_return_val_if_fail (E_IS_TIMEZONE_DIALOG (etd), NULL);
682
683 priv = etd->priv;
684
685 return priv->zone;
686 }
687
688 /**
689 * e_timezone_dialog_set_timezone:
690 * @etd: the timezone dialog
691 * @zone: the timezone
692 *
693 * Sets the timezone of @etd to @zone. Updates the display name and
694 * selected location. The caller must ensure that @zone is not freed
695 * before @etd is destroyed.
696 **/
697
698 void
699 e_timezone_dialog_set_timezone (ETimezoneDialog *etd,
700 icaltimezone *zone)
701 {
702 ETimezoneDialogPrivate *priv;
703 gchar *display = NULL;
704
705 g_return_if_fail (E_IS_TIMEZONE_DIALOG (etd));
706
707 if (!zone)
708 zone = get_local_timezone ();
709
710 if (zone)
711 display = zone_display_name_with_offset (zone);
712
713 priv = etd->priv;
714
715 priv->zone = zone;
716
717 gtk_label_set_text (
718 GTK_LABEL (priv->preview_label),
719 zone ? display : "");
720 timezone_combo_set_active_text (
721 GTK_COMBO_BOX (priv->timezone_combo),
722 zone ? zone_display_name (zone) : "");
723
724 set_map_timezone (etd, zone);
725 g_free (display);
726 }
727
728 GtkWidget *
729 e_timezone_dialog_get_toplevel (ETimezoneDialog *etd)
730 {
731 ETimezoneDialogPrivate *priv;
732
733 g_return_val_if_fail (etd != NULL, NULL);
734 g_return_val_if_fail (E_IS_TIMEZONE_DIALOG (etd), NULL);
735
736 priv = etd->priv;
737
738 return priv->app;
739 }
740
741 static void
742 set_map_timezone (ETimezoneDialog *etd,
743 icaltimezone *zone)
744 {
745 ETimezoneDialogPrivate *priv;
746 EMapPoint *point;
747 gdouble zone_longitude, zone_latitude;
748
749 priv = etd->priv;
750
751 if (zone) {
752 zone_longitude = icaltimezone_get_longitude (zone);
753 zone_latitude = icaltimezone_get_latitude (zone);
754 point = e_map_get_closest_point (
755 priv->map,
756 zone_longitude,
757 zone_latitude,
758 FALSE);
759 } else
760 point = NULL;
761
762 if (priv->point_selected)
763 e_map_point_set_color_rgba (
764 priv->map, priv->point_selected,
765 E_TIMEZONE_DIALOG_MAP_POINT_NORMAL_RGBA);
766
767 priv->point_selected = point;
768 }
769
770 static void
771 on_combo_changed (GtkComboBox *combo_box,
772 ETimezoneDialog *etd)
773 {
774 ETimezoneDialogPrivate *priv;
775 gchar *new_zone_name;
776 icalarray *zones;
777 icaltimezone *map_zone = NULL;
778 gchar *location;
779 gint i;
780
781 priv = etd->priv;
782
783 timezone_combo_get_active_text (
784 GTK_COMBO_BOX (priv->timezone_combo), &new_zone_name);
785
786 if (!new_zone_name || !*new_zone_name)
787 priv->zone = NULL;
788 else if (!g_utf8_collate (new_zone_name, _("UTC")))
789 priv->zone = icaltimezone_get_utc_timezone ();
790 else {
791 priv->zone = NULL;
792
793 zones = icaltimezone_get_builtin_timezones ();
794 for (i = 0; i < zones->num_elements; i++) {
795 map_zone = icalarray_element_at (zones, i);
796 location = _(icaltimezone_get_location (map_zone));
797 if (!g_utf8_collate (new_zone_name, location)) {
798 priv->zone = map_zone;
799 break;
800 }
801 }
802 }
803
804 set_map_timezone (etd, map_zone);
805
806 g_free (new_zone_name);
807 }
808
809 static void
810 timezone_combo_get_active_text (GtkComboBox *combo,
811 gchar **zone_name)
812 {
813 GtkTreeModel *list_store;
814 GtkTreeIter iter;
815
816 list_store = gtk_combo_box_get_model (combo);
817
818 /* Get the active iter in the list */
819 if (gtk_combo_box_get_active_iter (combo, &iter))
820 gtk_tree_model_get (list_store, &iter, 0, zone_name, -1);
821 else
822 *zone_name = NULL;
823 }
824
825 static gboolean
826 timezone_combo_set_active_text (GtkComboBox *combo,
827 const gchar *zone_name)
828 {
829 GtkTreeModel *list_store;
830 GHashTable *index;
831 gpointer id = NULL;
832
833 list_store = gtk_combo_box_get_model (combo);
834 index = (GHashTable *) g_object_get_data (G_OBJECT (list_store), "index");
835
836 if (zone_name && *zone_name)
837 id = g_hash_table_lookup (index, zone_name);
838
839 gtk_combo_box_set_active (combo, GPOINTER_TO_INT (id));
840
841 return (id != NULL);
842 }
843
844 static void
845 map_destroy_cb (gpointer data,
846 GObject *where_object_was)
847 {
848
849 ETimezoneDialogPrivate *priv = data;
850 if (priv->timeout_id) {
851 g_source_remove (priv->timeout_id);
852 priv->timeout_id = 0;
853 }
854 return;
855 }