From 4e60b7d947fc081958acc7b4e17dfdf4e62ec08b Mon Sep 17 00:00:00 2001 From: Iain Holmes Date: Fri, 17 Aug 2001 04:29:11 +0000 Subject: Startup assistant stuff svn path=/trunk/; revision=12145 --- widgets/e-timezone-dialog/e-timezone-dialog.c | 681 ++++++++++++++++++++++++++ 1 file changed, 681 insertions(+) create mode 100644 widgets/e-timezone-dialog/e-timezone-dialog.c (limited to 'widgets/e-timezone-dialog/e-timezone-dialog.c') diff --git a/widgets/e-timezone-dialog/e-timezone-dialog.c b/widgets/e-timezone-dialog/e-timezone-dialog.c new file mode 100644 index 0000000000..31fc7f77e3 --- /dev/null +++ b/widgets/e-timezone-dialog/e-timezone-dialog.c @@ -0,0 +1,681 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* Evolution calendar - Timezone selector dialog + * + * Copyright (C) 2001 Ximian, Inc. + * + * Authors: Damon Chaplin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "e-timezone-dialog.h" + +#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 + +struct _ETimezoneDialogPrivate { + /* The TZID of the timezone. May be NULL for a 'local time' (i.e. when + the displayed name is "") or for builtin timezones which we haven't + loaded yet. */ + char *tzid; + + /* Glade XML data */ + GladeXML *xml; + + EMapPoint *point_selected; + EMapPoint *point_hover; + + EMap *map; + + /* The timeout used to flash the nearest point. */ + guint timeout_id; + + /* Widgets from the Glade file */ + GtkWidget *app; + GtkWidget *table; + GtkWidget *map_window; + GtkWidget *timezone_preview; + GtkWidget *timezone_combo; +}; + + +static void e_timezone_dialog_class_init (ETimezoneDialogClass *class); +static void e_timezone_dialog_init (ETimezoneDialog *etd); +static void e_timezone_dialog_destroy (GtkObject *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, + GdkEventButton *event, + gpointer data); + +static char* get_zone_from_point (ETimezoneDialog *etd, + EMapPoint *point); +static void find_selected_point (ETimezoneDialog *etd); +static void on_combo_changed (GtkEditable *entry, + ETimezoneDialog *etd); + + +static GtkObjectClass *parent_class; + + +GtkType +e_timezone_dialog_get_type (void) +{ + static GtkType e_timezone_dialog_type = 0; + + if (!e_timezone_dialog_type) { + static const GtkTypeInfo e_timezone_dialog_info = { + "ETimezoneDialog", + sizeof (ETimezoneDialog), + sizeof (ETimezoneDialogClass), + (GtkClassInitFunc) e_timezone_dialog_class_init, + (GtkObjectInitFunc) e_timezone_dialog_init, + NULL, /* reserved_1 */ + NULL, /* reserved_2 */ + (GtkClassInitFunc) NULL + }; + + e_timezone_dialog_type = gtk_type_unique (GTK_TYPE_OBJECT, + &e_timezone_dialog_info); + } + + return e_timezone_dialog_type; +} + +/* Class initialization function for the event editor */ +static void +e_timezone_dialog_class_init (ETimezoneDialogClass *class) +{ + GtkObjectClass *object_class; + + object_class = (GtkObjectClass *) class; + + parent_class = gtk_type_class (GTK_TYPE_OBJECT); + + object_class->destroy = e_timezone_dialog_destroy; +} + +/* Object initialization function for the event editor */ +static void +e_timezone_dialog_init (ETimezoneDialog *etd) +{ + ETimezoneDialogPrivate *priv; + + priv = g_new0 (ETimezoneDialogPrivate, 1); + etd->priv = priv; + + priv->tzid = NULL; + priv->point_selected = NULL; + priv->point_hover = NULL; + priv->timeout_id = 0; +} + +/* Destroy handler for the event editor */ +static void +e_timezone_dialog_destroy (GtkObject *object) +{ + ETimezoneDialog *etd; + ETimezoneDialogPrivate *priv; + GtkWidget *dialog; + + g_return_if_fail (object != NULL); + g_return_if_fail (E_IS_TIMEZONE_DIALOG (object)); + + etd = E_TIMEZONE_DIALOG (object); + priv = etd->priv; + + /* Destroy the actual dialog. */ + dialog = e_timezone_dialog_get_toplevel (etd); + gtk_widget_destroy (dialog); + + g_free (priv->tzid); + priv->tzid = NULL; + + if (priv->timeout_id) { + g_source_remove (priv->timeout_id); + priv->timeout_id = 0; + } + + if (priv->xml) { + gtk_object_unref (GTK_OBJECT (priv->xml)); + priv->xml = NULL; + } + + g_free (priv); + etd->priv = NULL; + + if (GTK_OBJECT_CLASS (parent_class)->destroy) + (* GTK_OBJECT_CLASS (parent_class)->destroy) (object); +} + + +static void +e_timezone_dialog_add_timezones (ETimezoneDialog *etd) +{ + ETimezoneDialogPrivate *priv; + icalarray *zones; + GtkWidget *listitem; + GtkCombo *combo; + int i; + + priv = etd->priv; + + combo = GTK_COMBO (priv->timezone_combo); + + /* Clear any existing items in the combo. */ + gtk_list_clear_items (GTK_LIST (combo->list), 0, -1); + + /* Put the "None" and "UTC" entries at the top of the combo's list. + When "None" is selected we want the field to be cleared. */ + listitem = gtk_list_item_new_with_label (_("None")); + gtk_combo_set_item_string (combo, GTK_ITEM (listitem), ""); + gtk_widget_show (listitem); + gtk_container_add (GTK_CONTAINER (combo->list), listitem); + + /* Note: We don't translate timezone names at the moment. */ + listitem = gtk_list_item_new_with_label ("UTC"); + gtk_widget_show (listitem); + gtk_container_add (GTK_CONTAINER (combo->list), listitem); + + /* Get the array of builtin timezones. */ + zones = icaltimezone_get_builtin_timezones (); + + for (i = 0; i < zones->num_elements; i++) { + icaltimezone *zone; + char *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); + + listitem = gtk_list_item_new_with_label (location); + gtk_widget_show (listitem); + gtk_container_add (GTK_CONTAINER (combo->list), listitem); + } +} + + +ETimezoneDialog * +e_timezone_dialog_construct (ETimezoneDialog *etd) +{ + ETimezoneDialogPrivate *priv; + 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 */ + + g_print ("Loading: %s\n", EVOLUTION_GLADEDIR "/e-timezone-dialog.glade"); + priv->xml = glade_xml_new (EVOLUTION_GLADEDIR "/e-timezone-dialog.glade", + NULL); + if (!priv->xml) { + g_message ("e_timezone_dialog_construct(): Could not load the Glade XML file!"); + goto error; + } + + if (!get_widgets (etd)) { + g_message ("e_timezone_dialog_construct(): Could not find all widgets in the XML file!"); + goto error; + } + + map = GTK_WIDGET (e_map_new ()); + priv->map = E_MAP (map); + gtk_widget_set_events (map, gtk_widget_get_events (map) + | GDK_LEAVE_NOTIFY_MASK + | GDK_VISIBILITY_NOTIFY_MASK); + + gtk_entry_set_editable (GTK_ENTRY (GTK_COMBO (priv->timezone_combo)->entry), FALSE); + + e_timezone_dialog_add_timezones (etd); + + gtk_container_add (GTK_CONTAINER (priv->map_window), map); + gtk_widget_show (map); + + gtk_signal_connect (GTK_OBJECT (map), "motion-notify-event", + GTK_SIGNAL_FUNC (on_map_motion), etd); + gtk_signal_connect (GTK_OBJECT (map), "leave-notify-event", + GTK_SIGNAL_FUNC (on_map_leave), etd); + gtk_signal_connect (GTK_OBJECT (map), "visibility-notify-event", + GTK_SIGNAL_FUNC (on_map_visibility_changed), etd); + gtk_signal_connect (GTK_OBJECT (map), "button-press-event", + GTK_SIGNAL_FUNC (on_map_button_pressed), etd); + + gtk_signal_connect (GTK_OBJECT (GTK_COMBO (priv->timezone_combo)->entry), "changed", + GTK_SIGNAL_FUNC (on_combo_changed), etd); + + return etd; + + error: + + gtk_object_unref (GTK_OBJECT (etd)); + return NULL; +} + +/* 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; + + priv = etd->priv; + +#define GW(name) glade_xml_get_widget (priv->xml, name) + + priv->app = GW ("timezone-dialog"); + priv->map_window = GW ("map-window"); + priv->timezone_combo = GW ("timezone-combo"); + priv->timezone_preview = GW ("timezone-preview"); + priv->table = GW ("table1"); + + return (priv->app + && priv->map_window + && priv->timezone_combo + && priv->timezone_preview + && priv->table); +} + + +/** + * 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 (gtk_type_new (E_TYPE_TIMEZONE_DIALOG)); + return e_timezone_dialog_construct (E_TIMEZONE_DIALOG (etd)); +} + + + +/* 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; + double longitude, latitude; + char *old_zone, *new_zone; + + etd = E_TIMEZONE_DIALOG (data); + priv = etd->priv; + + e_map_window_to_world (priv->map, (double) event->x, (double) 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); + + gtk_label_get (GTK_LABEL (priv->timezone_preview), &old_zone); + new_zone = get_zone_from_point (etd, priv->point_hover); + if (strcmp (old_zone, new_zone)) + gtk_label_set_text (GTK_LABEL (priv->timezone_preview), + new_zone); + + return TRUE; +} + + +static gboolean +on_map_leave (GtkWidget *widget, GdkEventCrossing *event, gpointer data) +{ + ETimezoneDialog *etd; + ETimezoneDialogPrivate *priv; + char *old_zone; + + etd = E_TIMEZONE_DIALOG (data); + priv = etd->priv; + + /* We only want to reset the hover point and the preview text 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); + + priv->point_hover = NULL; + + /* Clear the timezone preview label, if it isn't already empty. */ + gtk_label_get (GTK_LABEL (priv->timezone_preview), &old_zone); + if (strcmp (old_zone, "")) + gtk_label_set_text (GTK_LABEL (priv->timezone_preview), ""); + + 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 = gtk_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, GdkEventButton *event, gpointer data) +{ + ETimezoneDialog *etd; + ETimezoneDialogPrivate *priv; + double longitude, latitude; + char *location; + + etd = E_TIMEZONE_DIALOG (data); + priv = etd->priv; + + e_map_window_to_world (priv->map, (double) event->x, (double) event->y, + &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; + + location = get_zone_from_point (etd, priv->point_selected); + gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (priv->timezone_combo)->entry), + location); + + g_free (priv->tzid); + priv->tzid = NULL; + } + + return TRUE; +} + + +static char* +get_zone_from_point (ETimezoneDialog *etd, + EMapPoint *point) +{ + ETimezoneDialogPrivate *priv; + icalarray *zones; + double longitude, latitude; + int i; + + priv = etd->priv; + + if (point == NULL) + return ""; + + 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; + double 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 icaltimezone_get_location (zone); + } + } + + g_assert_not_reached (); + + return NULL; +} + + +/* Returns the TZID of the timezone set, and optionally its displayed name. + The TZID may be NULL, in which case the builtin timezone with the city name + of display_name should be used. If display_name is also NULL or "", then it + is assumed to be a 'local time'. */ +char* +e_timezone_dialog_get_timezone (ETimezoneDialog *etd, + char **display_name) +{ + ETimezoneDialogPrivate *priv; + + g_return_val_if_fail (etd != NULL, NULL); + g_return_val_if_fail (E_IS_TIMEZONE_DIALOG (etd), NULL); + + priv = etd->priv; + + if (display_name) + *display_name = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (priv->timezone_combo)->entry)); + + return priv->tzid; +} + + +/* Sets the TZID and displayed name of the timezone. The TZID may be NULL for + a 'local time' (i.e. display_name is NULL or "") or if it is a builtin + timezone which hasn't been loaded yet. (This is done so we don't load + timezones until we really need them.) */ +void +e_timezone_dialog_set_timezone (ETimezoneDialog *etd, + char *tzid, + char *display_name) +{ + ETimezoneDialogPrivate *priv; + + g_return_if_fail (etd != NULL); + g_return_if_fail (E_IS_TIMEZONE_DIALOG (etd)); + + priv = etd->priv; + + if (priv->tzid) + g_free (priv->tzid); + + priv->tzid = g_strdup (tzid); + + gtk_entry_set_text (GTK_ENTRY (GTK_COMBO (priv->timezone_combo)->entry), + display_name ? display_name : ""); + + find_selected_point (etd); +} + + +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; +} + + +/* This tries to find the timezone corresponding to the text in the combo, + and selects the point so that it flashes. */ +static void +find_selected_point (ETimezoneDialog *etd) +{ + ETimezoneDialogPrivate *priv; + icalarray *zones; + char *current_zone; + EMapPoint *point = NULL; + int i; + + priv = etd->priv; + + current_zone = gtk_entry_get_text (GTK_ENTRY (GTK_COMBO (priv->timezone_combo)->entry)); + + /* Get the array of builtin timezones. */ + zones = icaltimezone_get_builtin_timezones (); + + for (i = 0; i < zones->num_elements; i++) { + icaltimezone *zone; + char *location; + + zone = icalarray_element_at (zones, i); + + location = icaltimezone_get_location (zone); + + if (!strcmp (current_zone, location)) { + double zone_longitude, zone_latitude; + + 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); + break; + } + } + + 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 (GtkEditable *entry, ETimezoneDialog *etd) +{ + ETimezoneDialogPrivate *priv; + + priv = etd->priv; + + find_selected_point (etd); + + g_free (priv->tzid); + priv->tzid = NULL; +} + +/** + * e_timezone_dialog_reparent: + * @etd: #ETimezoneDialog. + * @new_parent: The new parent widget. + * + * Takes the internal widgets out of the dialog and put them into @new_parent + */ +void +e_timezone_dialog_reparent (ETimezoneDialog *etd, + GtkWidget *new_parent) +{ + ETimezoneDialogPrivate *priv; + + priv = etd->priv; + + gtk_widget_reparent (priv->table, new_parent); +} -- cgit v1.2.3