From d09d8de870b6697c8a8b262e7e077b871a69b315 Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Mon, 10 Dec 2012 08:09:59 -0500 Subject: Consolidate base utility libraries into libeutil. Evolution consists of entirely too many small utility libraries, which increases linking and loading time, places a burden on higher layers of the application (e.g. modules) which has to remember to link to all the small in-tree utility libraries, and makes it difficult to generate API documentation for these utility libraries in one Gtk-Doc module. Merge the following utility libraries under the umbrella of libeutil, and enforce a single-include policy on libeutil so we can reorganize the files as desired without disrupting its pseudo-public API. libemail-utils/libemail-utils.la libevolution-utils/libevolution-utils.la filter/libfilter.la widgets/e-timezone-dialog/libetimezonedialog.la widgets/menus/libmenus.la widgets/misc/libemiscwidgets.la widgets/table/libetable.la widgets/text/libetext.la This also merges libedataserverui from the Evolution-Data-Server module, since Evolution is its only consumer nowadays, and I'd like to make some improvements to those APIs without concern for backward-compatibility. And finally, start a Gtk-Doc module for libeutil. It's going to be a project just getting all the symbols _listed_ much less _documented_. But the skeletal structure is in place and I'm off to a good start. --- e-util/e-timezone-dialog.c | 870 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 870 insertions(+) create mode 100644 e-util/e-timezone-dialog.c (limited to 'e-util/e-timezone-dialog.c') diff --git a/e-util/e-timezone-dialog.c b/e-util/e-timezone-dialog.c new file mode 100644 index 0000000000..431287c2df --- /dev/null +++ b/e-util/e-timezone-dialog.c @@ -0,0 +1,870 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + * + * Authors: + * Damon Chaplin + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "e-timezone-dialog.h" + +#include +#include +#include + +#include + +#include "e-map.h" +#include "e-misc-utils.h" +#include "e-util-private.h" + +#ifdef G_OS_WIN32 +#ifdef gmtime_r +#undef gmtime_r +#endif +#ifdef localtime_r +#undef localtime_r +#endif + +/* The gmtime() and localtime() in Microsoft's C library are MT-safe */ +#define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0) +#define localtime_r(tp,tmp) (localtime(tp)?(*(tmp)=*localtime(tp),(tmp)):0) +#endif + +#define E_TIMEZONE_DIALOG_MAP_POINT_NORMAL_RGBA 0xc070a0ff +#define E_TIMEZONE_DIALOG_MAP_POINT_HOVER_RGBA 0xffff60ff +#define E_TIMEZONE_DIALOG_MAP_POINT_SELECTED_1_RGBA 0xff60e0ff +#define E_TIMEZONE_DIALOG_MAP_POINT_SELECTED_2_RGBA 0x000000ff + +#define E_TIMEZONE_DIALOG_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_TIMEZONE_DIALOG, ETimezoneDialogPrivate)) + +struct _ETimezoneDialogPrivate { + /* The selected timezone. May be NULL for a 'local time' (i.e. when + * the displayed name is ""). */ + icaltimezone *zone; + + GtkBuilder *builder; + + EMapPoint *point_selected; + EMapPoint *point_hover; + + EMap *map; + + /* The timeout used to flash the nearest point. */ + guint timeout_id; + + /* Widgets from the UI file */ + GtkWidget *app; + GtkWidget *table; + GtkWidget *map_window; + GtkWidget *timezone_combo; + GtkWidget *preview_label; +}; + +static void e_timezone_dialog_dispose (GObject *object); + +static gboolean get_widgets (ETimezoneDialog *etd); +static gboolean on_map_timeout (gpointer data); +static gboolean on_map_motion (GtkWidget *widget, + GdkEventMotion *event, + gpointer data); +static gboolean on_map_leave (GtkWidget *widget, + GdkEventCrossing *event, + gpointer data); +static gboolean on_map_visibility_changed (GtkWidget *w, + GdkEventVisibility *event, + gpointer data); +static gboolean on_map_button_pressed (GtkWidget *w, + GdkEvent *button_event, + gpointer data); + +static icaltimezone * get_zone_from_point (ETimezoneDialog *etd, + EMapPoint *point); +static void set_map_timezone (ETimezoneDialog *etd, + icaltimezone *zone); +static void on_combo_changed (GtkComboBox *combo, + ETimezoneDialog *etd); + +static void timezone_combo_get_active_text (GtkComboBox *combo, + gchar **zone_name); +static gboolean timezone_combo_set_active_text (GtkComboBox *combo, + const gchar *zone_name); + +static void map_destroy_cb (gpointer data, + GObject *where_object_was); + +G_DEFINE_TYPE (ETimezoneDialog, e_timezone_dialog, G_TYPE_OBJECT) + +/* Class initialization function for the event editor */ +static void +e_timezone_dialog_class_init (ETimezoneDialogClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (ETimezoneDialogPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->dispose = e_timezone_dialog_dispose; +} + +/* Object initialization function for the event editor */ +static void +e_timezone_dialog_init (ETimezoneDialog *etd) +{ + etd->priv = E_TIMEZONE_DIALOG_GET_PRIVATE (etd); +} + +/* Dispose handler for the event editor */ +static void +e_timezone_dialog_dispose (GObject *object) +{ + ETimezoneDialogPrivate *priv; + + priv = E_TIMEZONE_DIALOG_GET_PRIVATE (object); + + /* Destroy the actual dialog. */ + if (priv->app != NULL) { + gtk_widget_destroy (priv->app); + priv->app = NULL; + } + + if (priv->timeout_id) { + g_source_remove (priv->timeout_id); + priv->timeout_id = 0; + } + + if (priv->builder) { + g_object_unref (priv->builder); + priv->builder = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_timezone_dialog_parent_class)->dispose (object); +} + +static void +e_timezone_dialog_add_timezones (ETimezoneDialog *etd) +{ + ETimezoneDialogPrivate *priv; + icalarray *zones; + GtkComboBox *combo; + GList *l, *list_items = NULL; + GtkListStore *list_store; + GtkTreeIter iter; + GtkCellRenderer *cell; + GtkCssProvider *css_provider; + GtkStyleContext *style_context; + GHashTable *index; + const gchar *css; + gint i; + GError *error = NULL; + + priv = etd->priv; + + /* Get the array of builtin timezones. */ + zones = icaltimezone_get_builtin_timezones (); + + for (i = 0; i < zones->num_elements; i++) { + icaltimezone *zone; + gchar *location; + + zone = icalarray_element_at (zones, i); + + location = _(icaltimezone_get_location (zone)); + + e_map_add_point ( + priv->map, location, + icaltimezone_get_longitude (zone), + icaltimezone_get_latitude (zone), + E_TIMEZONE_DIALOG_MAP_POINT_NORMAL_RGBA); + + list_items = g_list_prepend (list_items, location); + } + + list_items = g_list_sort (list_items, (GCompareFunc) g_utf8_collate); + + /* Put the "UTC" entry at the top of the combo's list. */ + list_items = g_list_prepend (list_items, _("UTC")); + + combo = GTK_COMBO_BOX (priv->timezone_combo); + + cell = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start ((GtkCellLayout *) combo, cell, TRUE); + gtk_cell_layout_set_attributes ((GtkCellLayout *) combo, cell, "text", 0, NULL); + + list_store = gtk_list_store_new (1, G_TYPE_STRING); + index = g_hash_table_new (g_str_hash, g_str_equal); + for (l = list_items, i = 0; l != NULL; l = l->next, ++i) { + gtk_list_store_append (list_store, &iter); + gtk_list_store_set (list_store, &iter, 0, (gchar *)(l->data), -1); + g_hash_table_insert (index, (gchar *)(l->data), GINT_TO_POINTER (i)); + } + + g_object_set_data_full ( + G_OBJECT (list_store), "index", index, + (GDestroyNotify) g_hash_table_destroy); + + gtk_combo_box_set_model (combo, (GtkTreeModel *) list_store); + + css_provider = gtk_css_provider_new (); + css = "GtkComboBox { -GtkComboBox-appears-as-list: 1; }"; + gtk_css_provider_load_from_data (css_provider, css, -1, &error); + style_context = gtk_widget_get_style_context (priv->timezone_combo); + if (error == NULL) { + gtk_style_context_add_provider ( + style_context, + GTK_STYLE_PROVIDER (css_provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + } else { + g_warning ("%s: %s", G_STRFUNC, error->message); + g_clear_error (&error); + } + g_object_unref (css_provider); + + g_list_free (list_items); +} + +ETimezoneDialog * +e_timezone_dialog_construct (ETimezoneDialog *etd) +{ + ETimezoneDialogPrivate *priv; + GtkWidget *widget; + GtkWidget *map; + + g_return_val_if_fail (etd != NULL, NULL); + g_return_val_if_fail (E_IS_TIMEZONE_DIALOG (etd), NULL); + + priv = etd->priv; + + /* Load the content widgets */ + + priv->builder = gtk_builder_new (); + e_load_ui_builder_definition (priv->builder, "e-timezone-dialog.ui"); + + if (!get_widgets (etd)) { + g_message ( + "%s(): Could not find all widgets in the XML file!", + G_STRFUNC); + goto error; + } + + widget = gtk_dialog_get_content_area (GTK_DIALOG (priv->app)); + gtk_container_set_border_width (GTK_CONTAINER (widget), 0); + + widget = gtk_dialog_get_action_area (GTK_DIALOG (priv->app)); + gtk_container_set_border_width (GTK_CONTAINER (widget), 12); + + priv->map = e_map_new (); + map = GTK_WIDGET (priv->map); + + g_object_weak_ref (G_OBJECT (map), map_destroy_cb, priv); + + gtk_widget_set_events ( + map, + gtk_widget_get_events (map) | + GDK_LEAVE_NOTIFY_MASK | + GDK_VISIBILITY_NOTIFY_MASK); + + e_timezone_dialog_add_timezones (etd); + + gtk_container_add (GTK_CONTAINER (priv->map_window), map); + gtk_widget_show (map); + + /* Ensure a reasonable minimum amount of map is visible */ + gtk_widget_set_size_request (priv->map_window, 200, 200); + + g_signal_connect ( + map, "motion-notify-event", + G_CALLBACK (on_map_motion), etd); + g_signal_connect ( + map, "leave-notify-event", + G_CALLBACK (on_map_leave), etd); + g_signal_connect ( + map, "visibility-notify-event", + G_CALLBACK (on_map_visibility_changed), etd); + g_signal_connect ( + map, "button-press-event", + G_CALLBACK (on_map_button_pressed), etd); + + g_signal_connect ( + priv->timezone_combo, "changed", + G_CALLBACK (on_combo_changed), etd); + + return etd; + + error: + + g_object_unref (etd); + return NULL; +} + +#if 0 +static gint +get_local_offset (void) +{ + time_t now = time (NULL), t_gmt, t_local; + struct tm gmt, local; + gint diff; + + gmtime_r (&now, &gmt); + localtime_r (&now, &local); + t_gmt = mktime (&gmt); + t_local = mktime (&local); + diff = t_local - t_gmt; + + return diff; +} +#endif + +static icaltimezone * +get_local_timezone (void) +{ + icaltimezone *zone; + gchar *location; + + tzset (); + location = e_cal_system_timezone_get_location (); + + if (location) + zone = icaltimezone_get_builtin_timezone (location); + else + zone = icaltimezone_get_utc_timezone (); + + g_free (location); + + return zone; +} + +/* Gets the widgets from the XML file and returns if they are all available. + * For the widgets whose values can be simply set with e-dialog-utils, it does + * that as well. + */ +static gboolean +get_widgets (ETimezoneDialog *etd) +{ + ETimezoneDialogPrivate *priv; + GtkBuilder *builder; + + priv = etd->priv; + builder = etd->priv->builder; + + priv->app = e_builder_get_widget (builder, "timezone-dialog"); + priv->map_window = e_builder_get_widget (builder, "map-window"); + priv->timezone_combo = e_builder_get_widget (builder, "timezone-combo"); + priv->table = e_builder_get_widget (builder, "timezone-table"); + priv->preview_label = e_builder_get_widget (builder, "preview-label"); + + return (priv->app + && priv->map_window + && priv->timezone_combo + && priv->table + && priv->preview_label); +} + +/** + * e_timezone_dialog_new: + * + * Creates a new event editor dialog. + * + * Return value: A newly-created event editor dialog, or NULL if the event + * editor could not be created. + **/ +ETimezoneDialog * +e_timezone_dialog_new (void) +{ + ETimezoneDialog *etd; + + etd = E_TIMEZONE_DIALOG (g_object_new (E_TYPE_TIMEZONE_DIALOG, NULL)); + return e_timezone_dialog_construct (E_TIMEZONE_DIALOG (etd)); +} + +static void +format_utc_offset (gint utc_offset, + gchar *buffer) +{ + const gchar *sign = "+"; + gint hours, minutes, seconds; + + if (utc_offset < 0) { + utc_offset = -utc_offset; + sign = "-"; + } + + hours = utc_offset / 3600; + minutes = (utc_offset % 3600) / 60; + seconds = utc_offset % 60; + + /* Sanity check. Standard timezone offsets shouldn't be much more + * than 12 hours, and daylight saving shouldn't change it by more + * than a few hours. (The maximum offset is 15 hours 56 minutes + * at present.) */ + if (hours < 0 || hours >= 24 || minutes < 0 || minutes >= 60 + || seconds < 0 || seconds >= 60) { + fprintf ( + stderr, "Warning: Strange timezone offset: " + "H:%i M:%i S:%i\n", hours, minutes, seconds); + } + + if (hours == 0 && minutes == 0 && seconds == 0) + strcpy (buffer, _("UTC")); + else if (seconds == 0) + sprintf ( + buffer, "%s %s%02i:%02i", + _("UTC"), sign, hours, minutes); + else + sprintf ( + buffer, "%s %s%02i:%02i:%02i", + _("UTC"), sign, hours, minutes, seconds); +} + +static gchar * +zone_display_name_with_offset (icaltimezone *zone) +{ + const gchar *display_name; + struct tm local; + struct icaltimetype tt; + gint offset; + gchar buffer[100]; + time_t now = time (NULL); + + gmtime_r ((const time_t *) &now, &local); + tt = tm_to_icaltimetype (&local, TRUE); + offset = icaltimezone_get_utc_offset (zone, &tt, NULL); + + format_utc_offset (offset, buffer); + + display_name = icaltimezone_get_display_name (zone); + if (icaltimezone_get_builtin_timezone (display_name)) + display_name = _(display_name); + + return g_strdup_printf ("%s (%s)", display_name, buffer); +} + +static const gchar * +zone_display_name (icaltimezone *zone) +{ + const gchar *display_name; + + display_name = icaltimezone_get_display_name (zone); + if (icaltimezone_get_builtin_timezone (display_name)) + display_name = _(display_name); + + return display_name; +} + +/* This flashes the currently selected timezone in the map. */ +static gboolean +on_map_timeout (gpointer data) +{ + ETimezoneDialog *etd; + ETimezoneDialogPrivate *priv; + + etd = E_TIMEZONE_DIALOG (data); + priv = etd->priv; + + if (!priv->point_selected) + return TRUE; + + if (e_map_point_get_color_rgba (priv->point_selected) + == E_TIMEZONE_DIALOG_MAP_POINT_SELECTED_1_RGBA) + e_map_point_set_color_rgba ( + priv->map, priv->point_selected, + E_TIMEZONE_DIALOG_MAP_POINT_SELECTED_2_RGBA); + else + e_map_point_set_color_rgba ( + priv->map, priv->point_selected, + E_TIMEZONE_DIALOG_MAP_POINT_SELECTED_1_RGBA); + + return TRUE; +} + +static gboolean +on_map_motion (GtkWidget *widget, + GdkEventMotion *event, + gpointer data) +{ + ETimezoneDialog *etd; + ETimezoneDialogPrivate *priv; + gdouble longitude, latitude; + icaltimezone *new_zone; + gchar *display = NULL; + + etd = E_TIMEZONE_DIALOG (data); + priv = etd->priv; + + e_map_window_to_world ( + priv->map, (gdouble) event->x, (gdouble) event->y, + &longitude, &latitude); + + if (priv->point_hover && priv->point_hover != priv->point_selected) + e_map_point_set_color_rgba ( + priv->map, priv->point_hover, + E_TIMEZONE_DIALOG_MAP_POINT_NORMAL_RGBA); + + priv->point_hover = e_map_get_closest_point ( + priv->map, longitude, + latitude, TRUE); + + if (priv->point_hover != priv->point_selected) + e_map_point_set_color_rgba ( + priv->map, priv->point_hover, + E_TIMEZONE_DIALOG_MAP_POINT_HOVER_RGBA); + + new_zone = get_zone_from_point (etd, priv->point_hover); + + display = zone_display_name_with_offset (new_zone); + gtk_label_set_text (GTK_LABEL (priv->preview_label), display); + + g_free (display); + + return TRUE; +} + +static gboolean +on_map_leave (GtkWidget *widget, + GdkEventCrossing *event, + gpointer data) +{ + ETimezoneDialog *etd; + ETimezoneDialogPrivate *priv; + + etd = E_TIMEZONE_DIALOG (data); + priv = etd->priv; + + /* We only want to reset the hover point if this is a normal leave + * event. For some reason we are getting leave events when the + * button is pressed in the map, which causes problems. */ + if (event->mode != GDK_CROSSING_NORMAL) + return FALSE; + + if (priv->point_hover && priv->point_hover != priv->point_selected) + e_map_point_set_color_rgba ( + priv->map, priv->point_hover, + E_TIMEZONE_DIALOG_MAP_POINT_NORMAL_RGBA); + + timezone_combo_set_active_text ( + GTK_COMBO_BOX (priv->timezone_combo), + zone_display_name (priv->zone)); + gtk_label_set_text (GTK_LABEL (priv->preview_label), ""); + + priv->point_hover = NULL; + + return FALSE; +} + +static gboolean +on_map_visibility_changed (GtkWidget *w, + GdkEventVisibility *event, + gpointer data) +{ + ETimezoneDialog *etd; + ETimezoneDialogPrivate *priv; + + etd = E_TIMEZONE_DIALOG (data); + priv = etd->priv; + + if (event->state != GDK_VISIBILITY_FULLY_OBSCURED) { + /* Map is visible, at least partly, so make sure we flash the + * selected point. */ + if (!priv->timeout_id) + priv->timeout_id = g_timeout_add (100, on_map_timeout, etd); + } else { + /* Map is invisible, so don't waste resources on the timeout.*/ + if (priv->timeout_id) { + g_source_remove (priv->timeout_id); + priv->timeout_id = 0; + } + } + + return FALSE; +} + +static gboolean +on_map_button_pressed (GtkWidget *w, + GdkEvent *button_event, + gpointer data) +{ + ETimezoneDialog *etd; + ETimezoneDialogPrivate *priv; + guint event_button = 0; + gdouble event_x_win = 0; + gdouble event_y_win = 0; + gdouble longitude, latitude; + + etd = E_TIMEZONE_DIALOG (data); + priv = etd->priv; + + gdk_event_get_button (button_event, &event_button); + gdk_event_get_coords (button_event, &event_x_win, &event_y_win); + + e_map_window_to_world ( + priv->map, event_x_win, event_y_win, &longitude, &latitude); + + if (event_button != 1) { + e_map_zoom_out (priv->map); + } else { + if (e_map_get_magnification (priv->map) <= 1.0) + e_map_zoom_to_location ( + priv->map, longitude, latitude); + + if (priv->point_selected) + e_map_point_set_color_rgba ( + priv->map, + priv->point_selected, + E_TIMEZONE_DIALOG_MAP_POINT_NORMAL_RGBA); + priv->point_selected = priv->point_hover; + + priv->zone = get_zone_from_point (etd, priv->point_selected); + timezone_combo_set_active_text ( + GTK_COMBO_BOX (priv->timezone_combo), + zone_display_name (priv->zone)); + } + + return TRUE; +} + +/* Returns the translated timezone location of the given EMapPoint, + * e.g. "Europe/London". */ +static icaltimezone * +get_zone_from_point (ETimezoneDialog *etd, + EMapPoint *point) +{ + icalarray *zones; + gdouble longitude, latitude; + gint i; + + if (point == NULL) + return NULL; + + e_map_point_get_location (point, &longitude, &latitude); + + /* Get the array of builtin timezones. */ + zones = icaltimezone_get_builtin_timezones (); + + for (i = 0; i < zones->num_elements; i++) { + icaltimezone *zone; + gdouble zone_longitude, zone_latitude; + + zone = icalarray_element_at (zones, i); + zone_longitude = icaltimezone_get_longitude (zone); + zone_latitude = icaltimezone_get_latitude (zone); + + if (zone_longitude - 0.005 <= longitude && + zone_longitude + 0.005 >= longitude && + zone_latitude - 0.005 <= latitude && + zone_latitude + 0.005 >= latitude) + { + return zone; + } + } + + g_return_val_if_reached (NULL); +} + +/** + * e_timezone_dialog_get_timezone: + * @etd: the timezone dialog + * + * Return value: the currently-selected timezone, or %NULL if no timezone + * is selected. + **/ +icaltimezone * +e_timezone_dialog_get_timezone (ETimezoneDialog *etd) +{ + ETimezoneDialogPrivate *priv; + + g_return_val_if_fail (E_IS_TIMEZONE_DIALOG (etd), NULL); + + priv = etd->priv; + + return priv->zone; +} + +/** + * e_timezone_dialog_set_timezone: + * @etd: the timezone dialog + * @zone: the timezone + * + * Sets the timezone of @etd to @zone. Updates the display name and + * selected location. The caller must ensure that @zone is not freed + * before @etd is destroyed. + **/ + +void +e_timezone_dialog_set_timezone (ETimezoneDialog *etd, + icaltimezone *zone) +{ + ETimezoneDialogPrivate *priv; + gchar *display = NULL; + + g_return_if_fail (E_IS_TIMEZONE_DIALOG (etd)); + + if (!zone) + zone = get_local_timezone (); + + if (zone) + display = zone_display_name_with_offset (zone); + + priv = etd->priv; + + priv->zone = zone; + + gtk_label_set_text ( + GTK_LABEL (priv->preview_label), + zone ? display : ""); + timezone_combo_set_active_text ( + GTK_COMBO_BOX (priv->timezone_combo), + zone ? zone_display_name (zone) : ""); + + set_map_timezone (etd, zone); + g_free (display); +} + +GtkWidget * +e_timezone_dialog_get_toplevel (ETimezoneDialog *etd) +{ + ETimezoneDialogPrivate *priv; + + g_return_val_if_fail (etd != NULL, NULL); + g_return_val_if_fail (E_IS_TIMEZONE_DIALOG (etd), NULL); + + priv = etd->priv; + + return priv->app; +} + +static void +set_map_timezone (ETimezoneDialog *etd, + icaltimezone *zone) +{ + ETimezoneDialogPrivate *priv; + EMapPoint *point; + gdouble zone_longitude, zone_latitude; + + priv = etd->priv; + + if (zone) { + zone_longitude = icaltimezone_get_longitude (zone); + zone_latitude = icaltimezone_get_latitude (zone); + point = e_map_get_closest_point ( + priv->map, + zone_longitude, + zone_latitude, + FALSE); + } else + point = NULL; + + if (priv->point_selected) + e_map_point_set_color_rgba ( + priv->map, priv->point_selected, + E_TIMEZONE_DIALOG_MAP_POINT_NORMAL_RGBA); + + priv->point_selected = point; +} + +static void +on_combo_changed (GtkComboBox *combo_box, + ETimezoneDialog *etd) +{ + ETimezoneDialogPrivate *priv; + gchar *new_zone_name; + icalarray *zones; + icaltimezone *map_zone = NULL; + gchar *location; + gint i; + + priv = etd->priv; + + timezone_combo_get_active_text ( + GTK_COMBO_BOX (priv->timezone_combo), &new_zone_name); + + if (!new_zone_name || !*new_zone_name) + priv->zone = NULL; + else if (!g_utf8_collate (new_zone_name, _("UTC"))) + priv->zone = icaltimezone_get_utc_timezone (); + else { + priv->zone = NULL; + + zones = icaltimezone_get_builtin_timezones (); + for (i = 0; i < zones->num_elements; i++) { + map_zone = icalarray_element_at (zones, i); + location = _(icaltimezone_get_location (map_zone)); + if (!g_utf8_collate (new_zone_name, location)) { + priv->zone = map_zone; + break; + } + } + } + + set_map_timezone (etd, map_zone); + + g_free (new_zone_name); +} + +static void +timezone_combo_get_active_text (GtkComboBox *combo, + gchar **zone_name) +{ + GtkTreeModel *list_store; + GtkTreeIter iter; + + list_store = gtk_combo_box_get_model (combo); + + /* Get the active iter in the list */ + if (gtk_combo_box_get_active_iter (combo, &iter)) + gtk_tree_model_get (list_store, &iter, 0, zone_name, -1); + else + *zone_name = NULL; +} + +static gboolean +timezone_combo_set_active_text (GtkComboBox *combo, + const gchar *zone_name) +{ + GtkTreeModel *list_store; + GHashTable *index; + gpointer id = NULL; + + list_store = gtk_combo_box_get_model (combo); + index = (GHashTable *) g_object_get_data (G_OBJECT (list_store), "index"); + + if (zone_name && *zone_name) + id = g_hash_table_lookup (index, zone_name); + + gtk_combo_box_set_active (combo, GPOINTER_TO_INT (id)); + + return (id != NULL); +} + +static void +map_destroy_cb (gpointer data, + GObject *where_object_was) +{ + + ETimezoneDialogPrivate *priv = data; + if (priv->timeout_id) { + g_source_remove (priv->timeout_id); + priv->timeout_id = 0; + } + return; +} -- cgit v1.2.3