[gnome-control-center/wip/datetime-panel: 10/10] datetime: add detection of location from map clicks



commit 3ae3e868082d29544a721605c4218b76fb70e57f
Author: Thomas Wood <thomas wood intel com>
Date:   Fri Jun 25 17:36:14 2010 +0100

    datetime: add detection of location from map clicks

 panels/datetime/Makefile.am         |    3 +-
 panels/datetime/cc-datetime-panel.c |   14 ++
 panels/datetime/cc-timezone-map.c   |  157 ++++++++++++++++-
 panels/datetime/cc-timezone-map.h   |    1 +
 panels/datetime/tz.c                |  336 +++++++++++++++++++++++++++++++++++
 panels/datetime/tz.h                |   84 +++++++++
 6 files changed, 587 insertions(+), 8 deletions(-)
---
diff --git a/panels/datetime/Makefile.am b/panels/datetime/Makefile.am
index f297c5d..09893d2 100644
--- a/panels/datetime/Makefile.am
+++ b/panels/datetime/Makefile.am
@@ -61,7 +61,8 @@ libdate_time_la_SOURCES =		\
 	cc-timezone-map.c	\
 	cc-timezone-map.h	\
 	set-timezone.c		\
-	set-timezone.h
+	set-timezone.h		\
+	tz.c tz.h
 
 libdate_time_la_LIBADD = $(PANEL_LIBS)
 libdate_time_la_LDFLAGS = $(PANEL_LDFLAGS)
diff --git a/panels/datetime/cc-datetime-panel.c b/panels/datetime/cc-datetime-panel.c
index eb7d070..fc8598c 100644
--- a/panels/datetime/cc-datetime-panel.c
+++ b/panels/datetime/cc-datetime-panel.c
@@ -179,6 +179,18 @@ apply_button_clicked_cb (GtkButton       *button,
 }
 
 static void
+location_changed_cb (CcTimezoneMap   *map,
+                     TzLocation      *location,
+                     CcDateTimePanel *self)
+{
+  GtkWidget *label;
+
+  label = (GtkWidget *) gtk_builder_get_object (self->priv->builder,
+                                                "label_current_location");
+  gtk_label_set_text (GTK_LABEL (label), location->zone);
+}
+
+static void
 cc_date_time_panel_init (CcDateTimePanel *self)
 {
   CcDateTimePanelPrivate *priv;
@@ -205,6 +217,8 @@ cc_date_time_panel_init (CcDateTimePanel *self)
     }
 
   widget = (GtkWidget *) cc_timezone_map_new ();
+  g_signal_connect (widget, "location-changed",
+                    G_CALLBACK (location_changed_cb), self);
   gtk_widget_show (widget);
 
   gtk_container_add (GTK_CONTAINER (gtk_builder_get_object (priv->builder,
diff --git a/panels/datetime/cc-timezone-map.c b/panels/datetime/cc-timezone-map.c
index 7c55138..75a28ce 100644
--- a/panels/datetime/cc-timezone-map.c
+++ b/panels/datetime/cc-timezone-map.c
@@ -23,6 +23,8 @@
  */
 
 #include "cc-timezone-map.h"
+#include <math.h>
+#include "tz.h"
 
 G_DEFINE_TYPE (CcTimezoneMap, cc_timezone_map, GTK_TYPE_WIDGET)
 
@@ -51,8 +53,19 @@ struct _CcTimezoneMapPrivate
   gint visible_map_rowstride;
 
   gdouble selected_offset;
+
+  TzDB *tzdb;
+  TzLocation *location;
+};
+
+enum
+{
+  LOCATION_CHANGED,
+  LAST_SIGNAL
 };
 
+static guint signals[LAST_SIGNAL];
+
 
 static CcTimezoneMapOffset color_codes[] =
 {
@@ -162,6 +175,15 @@ cc_timezone_map_dispose (GObject *object)
 static void
 cc_timezone_map_finalize (GObject *object)
 {
+  CcTimezoneMapPrivate *priv = CC_TIMEZONE_MAP (object)->priv;
+
+  if (priv->tzdb)
+    {
+      g_free (priv->tzdb);
+      priv->tzdb = NULL;
+    }
+
+
   G_OBJECT_CLASS (cc_timezone_map_parent_class)->finalize (object);
 }
 
@@ -237,6 +259,44 @@ cc_timezone_map_realize (GtkWidget *widget)
   gtk_widget_set_window (widget, window);
 }
 
+
+static gdouble
+convert_longtitude_to_x (gdouble longitude, gint map_width)
+{
+  const gdouble xdeg_offset = -6;
+  gdouble x;
+
+  x = (map_width * (180.0 + longitude) / 360.0)
+    + (map_width * xdeg_offset / 180.0);
+
+  return x;
+}
+
+static gdouble
+radians (gdouble degrees)
+{
+  return (degrees / 360.0) * G_PI * 2;
+}
+
+static gdouble
+convert_latitude_to_y (gdouble latitude, gdouble map_height)
+{
+  gdouble bottom_lat = -59;
+  gdouble top_lat = 81;
+  gdouble top_per, y, full_range, top_offset, map_range;
+
+  top_per = top_lat / 180.0;
+  y = 1.25 * log (tan (G_PI_4 + 0.4 * radians (latitude)));
+  full_range = 4.6068250867599998;
+  top_offset = full_range * top_per;
+  map_range = fabs (1.25 * log (tan (G_PI_4 + 0.4 * radians (bottom_lat))) - top_offset);
+  y = fabs (y - top_offset);
+  y = y / map_range;
+  y = y * map_height;
+  return y;
+}
+
+
 static gboolean
 cc_timezone_map_expose_event (GtkWidget      *widget,
                               GdkEventExpose *event)
@@ -247,6 +307,7 @@ cc_timezone_map_expose_event (GtkWidget      *widget,
   GtkAllocation alloc;
   gchar *file;
   GError *err = NULL;
+  gdouble pointx, pointy;
 
   cr = gdk_cairo_create (gtk_widget_get_window (widget));
 
@@ -267,17 +328,30 @@ cc_timezone_map_expose_event (GtkWidget      *widget,
       if (err)
         g_clear_error (&err);
     }
+  else
+    {
 
-  hilight = gdk_pixbuf_scale_simple (orig_hilight, alloc.width, alloc.height,
-                                     GDK_INTERP_BILINEAR);
-  gdk_cairo_set_source_pixbuf (cr, hilight, 0, 0);
+      hilight = gdk_pixbuf_scale_simple (orig_hilight, alloc.width,
+                                         alloc.height, GDK_INTERP_BILINEAR);
+      gdk_cairo_set_source_pixbuf (cr, hilight, 0, 0);
 
-  cairo_paint (cr);
+      cairo_paint (cr);
+      g_object_unref (hilight);
+      g_object_unref (orig_hilight);
+    }
+
+  if (priv->location)
+    {
+      pointx = convert_longtitude_to_x (priv->location->longitude, alloc.width);
+      pointy = convert_latitude_to_y (priv->location->latitude, alloc.height);
+
+      cairo_set_source_rgb (cr, 0, 0, 0);
+      cairo_arc (cr, pointx, pointy, 3.0, 0, 2 * G_PI);
+      cairo_fill (cr);
+    }
 
   cairo_destroy (cr);
 
-  g_object_unref (hilight);
-  g_object_unref (orig_hilight);
 
   return TRUE;
 }
@@ -300,9 +374,33 @@ cc_timezone_map_class_init (CcTimezoneMapClass *klass)
   widget_class->size_allocate = cc_timezone_map_size_allocate;
   widget_class->realize = cc_timezone_map_realize;
   widget_class->expose_event = cc_timezone_map_expose_event;
+
+  signals[LOCATION_CHANGED] = g_signal_new ("location-changed",
+                                            CC_TYPE_TIMEZONE_MAP,
+                                            G_SIGNAL_RUN_FIRST,
+                                            0,
+                                            NULL,
+                                            NULL,
+                                            g_cclosure_marshal_VOID__POINTER,
+                                            G_TYPE_NONE, 1,
+                                            G_TYPE_POINTER);
 }
 
-gboolean
+
+static gint
+sort_locations (TzLocation *a,
+                TzLocation *b)
+{
+  if (a->dist > b->dist)
+    return 1;
+
+  if (a->dist < b->dist)
+    return -1;
+
+  return 0;
+}
+
+static gboolean
 button_press_event (GtkWidget      *widget,
                     GdkEventButton *event)
 {
@@ -313,6 +411,12 @@ button_press_event (GtkWidget      *widget,
   gint rowstride;
   gint i;
 
+  const GPtrArray *array;
+  gint width, height;
+  GList *distances = NULL;
+  GtkAllocation alloc;
+  TzInfo *info;
+
   x = event->x;
   y = event->y;
 
@@ -337,6 +441,44 @@ button_press_event (GtkWidget      *widget,
 
   gtk_widget_queue_draw (widget);
 
+  /* work out the co-ordinates */
+
+  array = tz_get_locations (priv->tzdb);
+
+  gtk_widget_get_allocation (widget, &alloc);
+  width = alloc.width;
+  height = alloc.height;
+
+  for (i = 0; i < array->len; i++)
+    {
+      gdouble pointx, pointy, dx, dy;
+      TzLocation *loc = array->pdata[i];
+
+      pointx = convert_longtitude_to_x (loc->longitude, width);
+      pointy = convert_latitude_to_y (loc->latitude, height);
+
+      dx = pointx - x;
+      dy = pointy - y;
+
+      loc->dist = dx * dx + dy * dy;
+      distances = g_list_prepend (distances, loc);
+
+    }
+  distances = g_list_sort (distances, (GCompareFunc) sort_locations);
+
+
+  priv->location = (TzLocation*) distances->data;
+  g_list_free (distances);
+
+  info = tz_info_from_location (priv->location);
+
+  priv->selected_offset = tz_location_get_utc_offset (priv->location)
+    / (60.0*60.0) + ((info->daylight) ? -1.0 : 0.0);
+
+  g_signal_emit (widget, signals[LOCATION_CHANGED], 0, priv->location);
+
+  tz_info_free (info);
+
   return TRUE;
 }
 
@@ -367,6 +509,7 @@ cc_timezone_map_init (CcTimezoneMap *self)
       g_clear_error (&err);
     }
 
+  priv->tzdb = tz_load_db ();
 
   g_signal_connect (self, "button-press-event", G_CALLBACK (button_press_event),
                     NULL);
diff --git a/panels/datetime/cc-timezone-map.h b/panels/datetime/cc-timezone-map.h
index 8b34161..2a62855 100644
--- a/panels/datetime/cc-timezone-map.h
+++ b/panels/datetime/cc-timezone-map.h
@@ -24,6 +24,7 @@
 #define _CC_TIMEZONE_MAP_H
 
 #include <gtk/gtk.h>
+#include "tz.h"
 
 G_BEGIN_DECLS
 
diff --git a/panels/datetime/tz.c b/panels/datetime/tz.c
new file mode 100644
index 0000000..2b4f342
--- /dev/null
+++ b/panels/datetime/tz.c
@@ -0,0 +1,336 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* Generic timezone utilities.
+ *
+ * Copyright (C) 2000-2001 Ximian, Inc.
+ *
+ * Authors: Hans Petter Jansson <hpj ximian com>
+ * 
+ * Largely based on Michael Fulbright's work on Anaconda.
+ *
+ * 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 <glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <math.h>
+#include <string.h>
+#include "tz.h"
+
+
+/* Forward declarations for private functions */
+
+static float convert_pos (gchar *pos, int digits);
+static int compare_country_names (const void *a, const void *b);
+static void sort_locations_by_country (GPtrArray *locations);
+static gchar * tz_data_file_get (void);
+
+
+/* ---------------- *
+ * Public interface *
+ * ---------------- */
+TzDB *
+tz_load_db (void)
+{
+	gchar *tz_data_file;
+	TzDB *tz_db;
+	FILE *tzfile;
+	char buf[4096];
+
+	tz_data_file = tz_data_file_get ();
+	if (!tz_data_file) {
+		g_warning ("Could not get the TimeZone data file name");
+		return NULL;
+	}
+	tzfile = fopen (tz_data_file, "r");
+	if (!tzfile) {
+		g_warning ("Could not open *%s*\n", tz_data_file);
+		g_free (tz_data_file);
+		return NULL;
+	}
+
+	tz_db = g_new0 (TzDB, 1);
+	tz_db->locations = g_ptr_array_new ();
+
+	while (fgets (buf, sizeof(buf), tzfile))
+	{
+		gchar **tmpstrarr;
+		gchar *latstr, *lngstr, *p;
+		TzLocation *loc;
+
+		if (*buf == '#') continue;
+
+		g_strchomp(buf);
+		tmpstrarr = g_strsplit(buf,"\t", 6);
+		
+		latstr = g_strdup (tmpstrarr[1]);
+		p = latstr + 1;
+		while (*p != '-' && *p != '+') p++;
+		lngstr = g_strdup (p);
+		*p = '\0';
+		
+		loc = g_new0 (TzLocation, 1);
+		loc->country = g_strdup (tmpstrarr[0]);
+		loc->zone = g_strdup (tmpstrarr[2]);
+		loc->latitude  = convert_pos (latstr, 2);
+		loc->longitude = convert_pos (lngstr, 3);
+		
+#ifdef __sun
+		if (tmpstrarr[3] && *tmpstrarr[3] == '-' && tmpstrarr[4])
+			loc->comment = g_strdup (tmpstrarr[4]);
+
+		if (tmpstrarr[3] && *tmpstrarr[3] != '-' && !islower(loc->zone)) {
+			TzLocation *locgrp;
+
+			/* duplicate entry */
+			locgrp = g_new0 (TzLocation, 1);
+			locgrp->country = g_strdup (tmpstrarr[0]);
+			locgrp->zone = g_strdup (tmpstrarr[3]);
+			locgrp->latitude  = convert_pos (latstr, 2);
+			locgrp->longitude = convert_pos (lngstr, 3);
+			locgrp->comment = (tmpstrarr[4]) ? g_strdup (tmpstrarr[4]) : NULL;
+
+			g_ptr_array_add (tz_db->locations, (gpointer) locgrp);
+		}
+#else
+		loc->comment = (tmpstrarr[3]) ? g_strdup(tmpstrarr[3]) : NULL;
+#endif
+
+		g_ptr_array_add (tz_db->locations, (gpointer) loc);
+
+		g_free (latstr);
+		g_free (lngstr);
+		g_strfreev (tmpstrarr);
+	}
+	
+	fclose (tzfile);
+	
+	/* now sort by country */
+	sort_locations_by_country (tz_db->locations);
+	
+	g_free (tz_data_file);
+	
+	return tz_db;
+}    
+
+GPtrArray *
+tz_get_locations (TzDB *db)
+{
+	return db->locations;
+}
+
+
+gchar *
+tz_location_get_country (TzLocation *loc)
+{
+	return loc->country;
+}
+
+
+gchar *
+tz_location_get_zone (TzLocation *loc)
+{
+	return loc->zone;
+}
+
+
+gchar *
+tz_location_get_comment (TzLocation *loc)
+{
+	return loc->comment;
+}
+
+
+void
+tz_location_get_position (TzLocation *loc, double *longitude, double *latitude)
+{
+	*longitude = loc->longitude;
+	*latitude = loc->latitude;
+}
+
+glong
+tz_location_get_utc_offset (TzLocation *loc)
+{
+	TzInfo *tz_info;
+	glong offset;
+
+	tz_info = tz_info_from_location (loc);
+	offset = tz_info->utc_offset;
+	tz_info_free (tz_info);
+	return offset;
+}
+
+gint
+tz_location_set_locally (TzLocation *loc)
+{
+	gchar *str;
+	time_t curtime;
+	struct tm *curzone;
+	gboolean is_dst = FALSE;
+	gint correction = 0;
+
+	g_return_val_if_fail (loc != NULL, 0);
+	g_return_val_if_fail (loc->zone != NULL, 0);
+	
+	curtime = time (NULL);
+	curzone = localtime (&curtime);
+	is_dst = curzone->tm_isdst;
+
+	str = g_strdup_printf ("TZ=%s", loc->zone);
+	putenv (str);
+#if 0
+	curtime = time (NULL);
+	curzone = localtime (&curtime);
+
+	if (!is_dst && curzone->tm_isdst) {
+		correction = (60 * 60);
+	}
+	else if (is_dst && !curzone->tm_isdst) {
+		correction = 0;
+	}
+#endif
+
+	return correction;
+}
+
+TzInfo *
+tz_info_from_location (TzLocation *loc)
+{
+	TzInfo *tzinfo;
+	gchar *str;
+	time_t curtime;
+	struct tm *curzone;
+	
+	g_return_val_if_fail (loc != NULL, NULL);
+	g_return_val_if_fail (loc->zone != NULL, NULL);
+	
+	str = g_strdup_printf ("TZ=%s", loc->zone);
+	putenv (str);
+	
+#if 0
+	tzset ();
+#endif
+	tzinfo = g_new0 (TzInfo, 1);
+
+	curtime = time (NULL);
+	curzone = localtime (&curtime);
+
+#ifndef __sun
+	/* Currently this solution doesnt seem to work - I get that */
+	/* America/Phoenix uses daylight savings, which is wrong    */
+	tzinfo->tzname_normal = g_strdup (curzone->tm_zone);
+	if (curzone->tm_isdst) 
+		tzinfo->tzname_daylight =
+			g_strdup (&curzone->tm_zone[curzone->tm_isdst]);
+	else
+		tzinfo->tzname_daylight = NULL;
+
+	tzinfo->utc_offset = curzone->tm_gmtoff;
+#else
+	tzinfo->tzname_normal = NULL;
+	tzinfo->tzname_daylight = NULL;
+	tzinfo->utc_offset = 0;
+#endif
+
+	tzinfo->daylight = curzone->tm_isdst;
+	
+	return tzinfo;
+}
+
+
+void
+tz_info_free (TzInfo *tzinfo)
+{
+	g_return_if_fail (tzinfo != NULL);
+	
+	if (tzinfo->tzname_normal) g_free (tzinfo->tzname_normal);
+	if (tzinfo->tzname_daylight) g_free (tzinfo->tzname_daylight);
+	g_free (tzinfo);
+}
+
+/* ----------------- *
+ * Private functions *
+ * ----------------- */
+
+static gchar *
+tz_data_file_get (void)
+{
+	gchar *file;
+
+	file = g_strdup (TZ_DATA_FILE);
+
+	return file;
+}
+
+static float
+convert_pos (gchar *pos, int digits)
+{
+	gchar whole[10];
+	gchar *fraction;
+	gint i;
+	float t1, t2;
+	
+	if (!pos || strlen(pos) < 4 || digits > 9) return 0.0;
+	
+	for (i = 0; i < digits + 1; i++) whole[i] = pos[i];
+	whole[i] = '\0';
+	fraction = pos + digits + 1;
+
+	t1 = g_strtod (whole, NULL);
+	t2 = g_strtod (fraction, NULL);
+
+	if (t1 >= 0.0) return t1 + t2/pow (10.0, strlen(fraction));
+	else return t1 - t2/pow (10.0, strlen(fraction));
+}
+
+
+#if 0
+
+/* Currently not working */
+static void
+free_tzdata (TzLocation *tz)
+{
+	
+	if (tz->country)
+	  g_free(tz->country);
+	if (tz->zone)
+	  g_free(tz->zone);
+	if (tz->comment)
+	  g_free(tz->comment);
+	
+	g_free(tz);
+}
+#endif
+
+
+static int
+compare_country_names (const void *a, const void *b)
+{
+	const TzLocation *tza = * (TzLocation **) a;
+	const TzLocation *tzb = * (TzLocation **) b;
+	
+	return strcmp (tza->zone, tzb->zone);
+}
+
+
+static void
+sort_locations_by_country (GPtrArray *locations)
+{
+	qsort (locations->pdata, locations->len, sizeof (gpointer),
+	       compare_country_names);
+}
diff --git a/panels/datetime/tz.h b/panels/datetime/tz.h
new file mode 100644
index 0000000..885a5e6
--- /dev/null
+++ b/panels/datetime/tz.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/* Generic timezone utilities.
+ *
+ * Copyright (C) 2000-2001 Ximian, Inc.
+ *
+ * Authors: Hans Petter Jansson <hpj ximian com>
+ * 
+ * Largely based on Michael Fulbright's work on Anaconda.
+ *
+ * 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.
+ */
+
+
+#ifndef _E_TZ_H
+#define _E_TZ_H
+
+
+#ifndef __sun
+#  define TZ_DATA_FILE "/usr/share/zoneinfo/zone.tab"
+#else
+#  define TZ_DATA_FILE "/usr/share/lib/zoneinfo/tab/zone_sun.tab"
+#endif
+
+typedef struct _TzDB TzDB;
+typedef struct _TzLocation TzLocation;
+typedef struct _TzInfo TzInfo;
+
+
+struct _TzDB
+{
+	GPtrArray *locations;
+};
+
+struct _TzLocation
+{
+	gchar *country;
+	gdouble latitude;
+	gdouble longitude;
+	gchar *zone;
+	gchar *comment;
+
+	gdouble dist; /* distance to clicked point for comparison */
+};
+
+/* see the glibc info page information on time zone information */
+/*  tzname_normal    is the default name for the timezone */
+/*  tzname_daylight  is the name of the zone when in daylight savings */
+/*  utc_offset       is offset in seconds from utc */
+/*  daylight         if non-zero then location obeys daylight savings */
+
+struct _TzInfo
+{
+	gchar *tzname_normal;
+	gchar *tzname_daylight;
+	glong utc_offset;
+	gint daylight;
+};
+
+
+TzDB      *tz_load_db                 (void);
+GPtrArray *tz_get_locations           (TzDB *db);
+void       tz_location_get_position   (TzLocation *loc,
+				       double *longitude, double *latitude);
+char      *tz_location_get_country    (TzLocation *loc);
+gchar     *tz_location_get_zone       (TzLocation *loc);
+gchar     *tz_location_get_comment    (TzLocation *loc);
+glong      tz_location_get_utc_offset (TzLocation *loc);
+gint       tz_location_set_locally    (TzLocation *loc);
+TzInfo    *tz_info_from_location      (TzLocation *loc);
+void       tz_info_free               (TzInfo *tz_info);
+
+#endif



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]