/* Copyright (C) 1999 Red Hat, Inc. */
/* Original work by Michael Fulbright <drmike@redhat.com> */

#include <gnome.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <string.h>

#include <Python.h>

#include "pygtk.h"
#include "gnome-map.h"
#include "gnome-canvas-dot.h"
#include "timezones.h"


/* Command line options */
/* image     - filename of file to use for map (must be PNG for antialias) */
/* mapwidth  - width to scale image to (also size of canvas widget)        */
/* mapheight - height to scale image to (also size of canvas widget)       */
/* aa        - if TRUE use antialiased canvas instead of normal            */

typedef struct MapData_t {
    GnomeMap *map;
    GtkWidget * locationlist;
    GtkWidget * citylist;
    GtkWidget * statusbar;
    GtkWidget * views;
    gint curselection;
    GnomeCanvasItem *selmarker;
    GnomeCanvasItem *curmarker;
    GnomeCanvasItem **markers;
    GPtrArray *Locations;
    GnomeCanvasItem *hiliterubberband;
    gint hiliteindex;
} MapData;

/* if TRUE we do not want to act on the select_row events to locationlist */
/* used when we are manually manipulating rows in locationlist and we     */
/* want to ignore the events going to the callback.                       */
gboolean ignore_locationlist_selectevents=FALSE;

/* Canvas item circle for selected location */
double          oldselx, oldsely; /* old location of selection */
#define SELECTED_COLOR "red"
#define HILITED_COLOR   "limegreen"
#define CITY_COLOR     "yellow"

GnomeMap *WorldMap;

/* view data */
/* Views are defined by a range of either longitude or latitude     */
/* as well as the central value for the other axis.                 */
typedef enum
{
    LONGITUDE_CONSTRAINT,
    LATITUDE_CONSTRAINT
} ViewContraintType;

struct _VIEW_DEF {
    gchar    *name;
    ViewContraintType type;
    double   constraint1, constraint2, constraint3;
};

typedef struct _VIEW_DEF ViewDefinition;

ViewDefinition Views[] = {
    { "World",         LONGITUDE_CONSTRAINT, -180.0, 180.0, 0.0 },
    { "North America", LONGITUDE_CONSTRAINT, -171.0, -21.0, 40.0 },
    { "South America", LATITUDE_CONSTRAINT,   15.0,   -70.0, -70.0 },
    { "Pacific Rim",   LATITUDE_CONSTRAINT,    -47.0, 47.0, 155.0},
    { "Europe",        LONGITUDE_CONSTRAINT,  -25.0, 70.0, 45.0 },
    { "Africa",        LATITUDE_CONSTRAINT,   40.0,   -40.0, 15.0},
    { "Asia",          LONGITUDE_CONSTRAINT,  20.0, 165.0, 40.0}
};

static gint numviews = sizeof(Views)/sizeof(ViewDefinition);

static gint item_event (GnomeCanvasItem *item, GdkEvent *event, gpointer data);
static GtkWidget * create_location_list (MapData *mapdata);

/* give a location name search for match in Locations db */
/* returns index if found, -1 if not                     */
static int
find_location (GPtrArray *Locations, gchar *locname)
{
    TimeZoneLocation  *loc;
    gint i;

    for (i=0; i < Locations->len; i++) {
	loc = g_ptr_array_index (Locations, i);

	if (!strcmp (loc->zone, locname))
	    return i;
    }

    return -1;
}


/* find nearest location to specified map coords - clips to current view */
/* if no match return -1                                                 */
static int
find_nearest (GPtrArray * Locations, double longitude, double latitude )
{
    double mindist;
    double dist;
    double dx, dy;
    int    i, mini;
    gboolean first=TRUE;
    TimeZoneLocation  *loc;

    mindist = 0;

    mini = -1;
    for (i=0; i < Locations->len; i++) {
	loc = g_ptr_array_index (Locations, i);

	if (!gnome_map_is_loc_in_view (WorldMap, 
				       loc->longitude, loc->latitude))
	    continue;

	dx = (loc->longitude-longitude);
	dy = (loc->latitude-latitude);
	dist = dx*dx + dy*dy;

	if ((dist < mindist) || first) {
	    mindist = dist;
	    mini = i;
	    first = FALSE;
	}
    }

    return mini;
}

/* attach to signal for canvas items so we can track motion and mouse */
/* events                                                             */
static void
setup_item (GnomeCanvasItem *item, MapData *mapdata)
{
    gtk_signal_connect (GTK_OBJECT (item), "event",
			(GtkSignalFunc) item_event,
			mapdata);
}

/* Moves marker (a 'x' currently) to the specified location */
/* and sets it to the specified color.  The oldx and oldy      */
/* variables are required because the canvas only allows one   */
/* to do relative moves for items.  These are automatically    */
/* set to x and y by this function before it returns.          */
/*                                                             */
/* The first time this function is called for a given marker   */
/* *curmarker should equal NULL.  The marker will be created   */
/* automatically.                                              */

static void
set_flag_marker (MapData *mapdata, gchar *color,
		 double x, double y, double *oldx, double *oldy)
{
    GnomeCanvasGroup  *canvas_root;
    GnomeCanvasItem   *group;

    g_return_if_fail ( color != NULL );

    canvas_root = gnome_canvas_root (GNOME_CANVAS(WorldMap->canvas));
    if (!mapdata->curmarker) {
	group = gnome_canvas_item_new ( canvas_root,
					gnome_canvas_group_get_type(),
					"x", x,
					"y", y,
					NULL);
	
#define MARKER_RADIUS 3.5
#define MARKER_WIDTH_PIX 1

        setup_item (gnome_canvas_item_new (GNOME_CANVAS_GROUP (group),
					   gnome_canvas_text_get_type (),
					   "font", "-adobe-helvetica-bold-r-normal--12-*-*-*-p-*-iso8859-1",
					   "anchor", GTK_ANCHOR_CENTER,
					   "fill_color", "red",
					   "text", "x",
					   NULL), mapdata);

  	mapdata->curmarker = GNOME_CANVAS_ITEM (group); 
    } else {
	gnome_canvas_item_move (mapdata->curmarker, x - *oldx, y - *oldy);
    }

    *oldx = x;
    *oldy = y;
}

/* Given a pointer to a GnomeMap and an index into the Locations db */
/* mark it as the selected location on the canvas                   */
static void
map_mark_location_selected (GnomeMap *map, gint index)
{
    TimeZoneLocation *loc;
    double selx, sely;
    MapData *mapdata = (MapData *) map->data;
    
    g_return_if_fail ( map != NULL );
    g_return_if_fail ( index >= 0 );

    loc = g_ptr_array_index (mapdata->Locations, index);
    
    gnome_map_xlat_map2screen ( map, 
				loc->longitude,	loc->latitude,
				&selx, &sely );
    set_flag_marker (mapdata, SELECTED_COLOR, selx, sely, &oldselx, &oldsely);
    
    if (mapdata->curselection >= 0) {
	gnome_canvas_item_set (mapdata->markers[mapdata->curselection],
			       "fill_color", CITY_COLOR, NULL);
	gnome_canvas_item_show (mapdata->markers[mapdata->curselection]);
    }

    gnome_canvas_item_hide (mapdata->markers[index]);
}

/* Given a pointer to a GnomeMap and an index into the Locations db */
/* mark it as the selected location in the clist of locations       */
/* The jumpto gboolean is used to specify if the clist should be    */
/* forced to scroll to the new location.  Used because when the     */
/* clist is autoscrolling we do not want to force selection to be   */
/* constantly recentered.                                           */
static void
list_mark_location_selected (GnomeMap *map, gint index, gboolean jumpto)
{
    gint newrow;
    MapData * mapdata;

    mapdata = (MapData *) map->data;
    
    /* We're messing with list manually, so let callback know to */
    /* ignore any events till we're done                         */
    ignore_locationlist_selectevents = TRUE;

    /* if current selection is visible then select it again, otherwise */
    /* change clist to not have a current selection                    */
    if (index >= 0) {
	TimeZoneLocation *loc = g_ptr_array_index (mapdata->Locations, index);
	
	if (gnome_map_is_loc_in_view (map,loc->longitude,loc->latitude)) {
	    gtk_clist_set_selection_mode (GTK_CLIST (mapdata->locationlist), 
					  GTK_SELECTION_BROWSE);
	} else {
	    gtk_clist_set_selection_mode (GTK_CLIST (mapdata->locationlist), 
					  GTK_SELECTION_SINGLE);
	}
    }

    /* find in list of locations and set as current */
    newrow = gtk_clist_find_row_from_data (GTK_CLIST (mapdata->locationlist),
					   GINT_TO_POINTER (index));

    if (newrow >= 0 ) {
	gtk_clist_select_row (GTK_CLIST(mapdata->locationlist), newrow, 0);
	if (jumpto && gtk_clist_row_is_visible (GTK_CLIST (mapdata->locationlist), newrow) != GTK_VISIBILITY_FULL) {
	    gtk_clist_moveto (GTK_CLIST (mapdata->locationlist), newrow , 0, 0.5, 0.5 );
	}
    }

    /* We're done mucking with clist, ok to listen to events again */
    ignore_locationlist_selectevents = FALSE;

}
/* handles all canvas drawing for making the selected location # index */
/* in the sorted list in the Locations variable                        */
static void
set_selection (MapData *mapdata, gint index, gboolean jumpto)
{
    g_return_if_fail ( index >= 0 );

    map_mark_location_selected (WorldMap, index);

    list_mark_location_selected (WorldMap, index, jumpto);

    /* NOTE: curselection is global variable. Only place it gets set */
    mapdata->curselection = index;
}

/* Given an index into the Locations db and a position, draw the hilite */
/* marker around it to indicate it is city cursor is pointing at        */
static void
set_hilited (MapData *mapdata, gint index, double item_x, double item_y)
{
    TimeZoneLocation *loc;
    GnomeCanvasPoints *points;
    char *status_text;

    g_return_if_fail ( index >= 0 );

    loc = g_ptr_array_index (mapdata->Locations, index);

    points = gnome_canvas_points_new (2);
    points->coords[0] = item_x;
    points->coords[1] = item_y;
    gnome_map_xlat_map2screen ( WorldMap,
				loc->longitude, loc->latitude,
				&points->coords[2], &points->coords[3] );
    if (mapdata->hiliterubberband) {
	gnome_canvas_item_set (mapdata->hiliterubberband, "points", points, NULL);
	gnome_canvas_item_show (mapdata->hiliterubberband);
    } else {
	GnomeCanvasGroup *canvas_root = gnome_canvas_root (GNOME_CANVAS (WorldMap->canvas));
	
	mapdata->hiliterubberband =
	    gnome_canvas_item_new (canvas_root,
				   gnome_canvas_line_get_type (),
				   "points", points,
				   "fill_color", HILITED_COLOR,
				   "width_pixels", 2,
				   "first_arrowhead", FALSE,
				   "last_arrowhead", TRUE,
				   "arrow_shape_a", 4.0,
				   "arrow_shape_b", 8.0,
				   "arrow_shape_c", 4.0,
				   NULL);
	setup_item (mapdata->hiliterubberband, mapdata);
    }

    /* if hilited city isn't also currently selected city, draw the */
    /* hilite marker as well                                        */
    if (index != mapdata->curselection) {
	
	if (mapdata->hiliteindex >= 0 && mapdata->hiliteindex != index) {
	    if (mapdata->hiliteindex != mapdata->curselection)
		gnome_canvas_item_set (mapdata->markers[mapdata->hiliteindex], 
				       "fill_color", CITY_COLOR, 
				       NULL);
	    else
		gnome_canvas_item_set (mapdata->markers[mapdata->hiliteindex], 
				       "fill_color", SELECTED_COLOR, 
				       NULL);
	    
	}

	mapdata->hiliteindex = index;
    }

    /* keep status bar from flickering */
    gtk_label_get (GTK_LABEL (GTK_STATUSBAR (mapdata->statusbar)->label), &status_text);
    if (strncmp (status_text, loc->zone, strlen (loc->zone)) != 0)
      {
	char *newstr;
	char *sep = " - ";

	gtk_statusbar_pop (GTK_STATUSBAR (mapdata->statusbar), 1);
	newstr = (char *) malloc (strlen (loc->zone) + strlen (sep) + 
				  ((loc->comment) ? strlen (loc->comment) : 0) + 1);
	strcpy (newstr, loc->zone);
	if (loc->comment)
	  {
	    strcat (newstr, sep);
	    strcat (newstr, loc->comment);
	  }
	
	gtk_statusbar_push (GTK_STATUSBAR (mapdata->statusbar), 1, newstr);
	free (newstr);
      }
    
    gnome_canvas_points_free (points);
}

/* Handles case where cursor leaves the map */
static int
canvas_event (GtkWidget *canvas, GdkEvent *event, gpointer data)
{
    MapData *mapdata = (MapData *) data;
    
    /* if pointer just left canvas, hide hilite marker(s) */
    if (event->type == GDK_LEAVE_NOTIFY) {
	if (mapdata->hiliterubberband)
	    gnome_canvas_item_hide (mapdata->hiliterubberband);
	if (mapdata->hiliteindex >= 0 &&
	    mapdata->hiliteindex != mapdata->curselection)
	    gnome_canvas_item_set (mapdata->markers[mapdata->hiliteindex], 
				    "fill_color", CITY_COLOR, 
				    NULL);

    }
    return FALSE;
}

/* Handles as motion and mouse button events in the map */
static gint
item_event (GnomeCanvasItem *item, GdkEvent *event, gpointer data)
{
    double longitude, latitude;
    double item_x, item_y;
    int    nearest;
    MapData *mapdata = (MapData *) data;

    if (event->type == GDK_BUTTON_PRESS ||
	event->type == GDK_MOTION_NOTIFY) {
        item_x = event->button.x;
        item_y = event->button.y;
	gnome_canvas_item_w2i (WorldMap->image_item, &item_x, &item_y);
    }

    switch (event->type) {
      case GDK_BUTTON_PRESS:
	switch (event->button.button) {
	  case 1:
	    
	    gnome_map_xlat_screen2map (WorldMap, item_x, item_y,
				       &longitude, &latitude);
	    
	    nearest  = find_nearest(mapdata->Locations, longitude, latitude);

	    set_selection (mapdata, nearest, TRUE);

	    break;
	    
	  default:
	    break;
	}
	
	break;
	
	/* highlight city which a button press will select */
      case GDK_MOTION_NOTIFY:

	gnome_map_xlat_screen2map ( WorldMap, item_x, item_y, 
				    &longitude, &latitude);

	nearest  = find_nearest(mapdata->Locations, longitude, latitude);
	set_hilited (mapdata, nearest, item_x, item_y);
	break;

      default:
	break;
    }
    
    return FALSE;
}

/* Handles events for the clist of locations */
static void
list_select_event ( GtkWidget *clist, gint row, gint column,
		    GdkEventButton *event, gpointer data)
{
    gchar       *text;
    gint        index;
    MapData * mapdata = (MapData *) data;

    /* should we do anything? */
    if (ignore_locationlist_selectevents)
	return;

    /* msf - always read zero because sometimes col == -1 if they select */
    /*       without a mouse click (ie. keyboard navigation )            */
    gtk_clist_get_text(GTK_CLIST(clist), row, 0, &text);
    
    /* Just prints some information about the selected row */
    g_print("You selected row %d. More specifically you clicked in column %d, and the text in this cell is %s\n\n", row, column, text);
    
    index = find_location (mapdata->Locations, text);
    if (index < 0)
	return;

    set_selection (mapdata, index, FALSE);
    return;
}


static GnomeCanvasItem *
draw_city_marker ( GnomeMap *map, double longitude, double latitude)
{
    double  x, y;
    GnomeCanvasItem *item;
    GnomeCanvasGroup *canvas_root;
    MapData *mapdata = (MapData *) map->data;

    canvas_root = gnome_canvas_root (GNOME_CANVAS (map->canvas));

    gnome_map_xlat_map2screen (map, longitude, latitude, &x, &y);

#define RAD 1
#ifdef ELLIPSE
    item = gnome_canvas_item_new (canvas_root,
				  gnome_canvas_ellipse_get_type (),
				  "x1", x-RAD,
				  "y1", y-RAD,
				  "x2", x+RAD,
				  "y2", y+RAD,
				  "fill_color", CITY_COLOR,
				  NULL);
#else
    item = gnome_canvas_item_new (canvas_root,
				  gnome_canvas_dot_get_type (),
				  "x", x,
				  "y", y,
				  "diameter_pixels", RAD,
				  "fill_color", CITY_COLOR,
				  NULL);
#endif
    
    setup_item (item, mapdata);
    return item;
}

static void
draw_cities (GnomeMap *map)
{
    gint i;
    MapData * mapdata = (MapData *) map->data;
    
    if (mapdata->markers)
	g_free(mapdata->markers);

    mapdata->markers = g_new( GnomeCanvasItem *, mapdata->Locations->len);
    for (i=0; i < mapdata->Locations->len; i++) {
	TimeZoneLocation  *loc = g_ptr_array_index (mapdata->Locations, i);
    
	mapdata->markers[i] = draw_city_marker (map, loc->longitude, loc->latitude);
    }
}

static void
view_menu_activate (GtkWidget *widget, void *data)
{
    static gint curitem = -1;
    gint   item = GPOINTER_TO_INT (data);
    MapData *mapdata = gtk_object_get_data (GTK_OBJECT (widget), "mapdata");
    
    double lat1, long1, lat2, long2;
    double dlat, dlong;

    if ( item == curitem )
	return;

    curitem = item; 

    /* compute aspect correct view and set canvas to it */
    /* we may have to shift view if it extends outside of map */
    if (Views[item].type == LONGITUDE_CONSTRAINT) {
	long1 = Views[item].constraint1;
	long2 = Views[item].constraint2;
	dlong = fabs(long2 - long1);
	dlat  = dlong/2.0;
	lat1  = Views[item].constraint3 - dlat/2.0;
	lat2  = Views[item].constraint3 + dlat/2.0;

	if (lat1 < -90.0) {
	    lat2 -= (lat1-90.0);
	    lat1 = -90.0;
	} else if (lat2 > 90.0) {
	    lat1 -= (lat2-90.0);
	    lat2 = 90.0;
	}
    } else if (Views[item].type == LATITUDE_CONSTRAINT) {
	lat1 = Views[item].constraint1;
	lat2 = Views[item].constraint2;
	dlat = fabs(lat2 - lat1);
	dlong = 2.0*dlat;
	long1 = Views[item].constraint3 - dlong/2.0;
	long2 = Views[item].constraint3 + dlong/2.0;

	if (long1 < -180.0) {
	    long2 -= (long1-180.0);
	    long1 = -180.0;
	} else if (long2 > 180.0) {
	    long1 -= (long2-180.0);
	    long2 = 180.0;
	}
    } else {
	g_warning ("Bad contraint type %d in Views structure item # %d.\n", 
		   item, Views[item].type);
	return;
    }

    gnome_map_set_view (WorldMap, long1, lat1, long2, lat2);

    /* make locationlist clist entries reflect those visible*/
    create_location_list (mapdata);
}

GtkWidget *
create_view_menu (MapData *mapdata)
{
    GtkWidget *omenu;
    GtkWidget *menu;
    GtkWidget *menu_item;
    gint   i;

    omenu = gtk_option_menu_new ();

    menu = gtk_menu_new ();
    
    for  (i=0; i < numviews; i++) {
	menu_item = gtk_menu_item_new_with_label (Views[i].name);
	gtk_menu_append (GTK_MENU (menu), menu_item);

	gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
			    (GtkSignalFunc) view_menu_activate, 
			    GINT_TO_POINTER (i));

	gtk_object_set_data (GTK_OBJECT (menu_item), "mapdata", mapdata);
    
	gtk_widget_show (menu_item);
    }
    gtk_option_menu_set_menu (GTK_OPTION_MENU (omenu), menu);
    gtk_option_menu_set_history (GTK_OPTION_MENU (omenu), 0);

    gtk_widget_show (omenu);

    return omenu;
}

/* returns pointer to the scrolled window containing the clist          */
/* pointer to clist is returned via the passed argument if it doesnt    */
/* already exist.  The list is clipped to the current world view        */
static GtkWidget *
create_location_list (MapData *mapdata)
{
    TimeZoneLocation   *loc;
    GtkWidget *scrolledwin;
    /* gchar *titles[] = { "Current Selection", NULL }; */
    gchar *row[2];
    gint i;

    ignore_locationlist_selectevents = TRUE;

    if (! mapdata->locationlist) {
	scrolledwin = gtk_scrolled_window_new (NULL, NULL);
    
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwin),
					GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	
	gtk_widget_show (scrolledwin);
	
	mapdata->locationlist = gtk_clist_new (2);
	
	gtk_clist_set_selection_mode (GTK_CLIST(mapdata->locationlist), 
				      GTK_SELECTION_BROWSE);
	gtk_clist_column_title_passive (GTK_CLIST(mapdata->locationlist), 0);
	gtk_signal_connect(GTK_OBJECT(mapdata->locationlist), "select_row",
			   GTK_SIGNAL_FUNC(list_select_event),
			   mapdata);

	gtk_container_add (GTK_CONTAINER (scrolledwin), mapdata->locationlist);
    } else {
	gtk_clist_clear (GTK_CLIST (mapdata->locationlist));
	scrolledwin = NULL;
    }

    for (i=0; i < mapdata->Locations->len; i++) {
	gint  newrow;

	loc = g_ptr_array_index (mapdata->Locations, i);	
	if (!gnome_map_is_loc_in_view (WorldMap, loc->longitude, loc->latitude))
	    continue;

	row[0] = loc->zone;
	row[1] = loc->comment;
	newrow = gtk_clist_append (GTK_CLIST (mapdata->locationlist), row);
	gtk_clist_set_row_data (GTK_CLIST (mapdata->locationlist), newrow, 
				GINT_TO_POINTER (i));
    }
    gtk_clist_columns_autosize (GTK_CLIST (mapdata->locationlist));

    /* restore selection of location in list now we've recreated it */
    list_mark_location_selected(WorldMap, mapdata->curselection, TRUE);

    ignore_locationlist_selectevents = FALSE;
    return scrolledwin;
}

MapData * new_mapdata (char * path)
{
    MapData * mapdata;
    mapdata = g_new0 (MapData, 1);

    /* make a new map view */
    WorldMap = gnome_map_new (path, 390, 180, FALSE);
    if (!WorldMap) {
	g_warning ("Could not create map view.");
	return NULL;
    }
    WorldMap->data = mapdata;
    mapdata->map = WorldMap;

    setup_item (WorldMap->image_item, mapdata);
    gtk_signal_connect (GTK_OBJECT (WorldMap->canvas), "event",
			(GtkSignalFunc) canvas_event,
			mapdata);

    /* load timezone data */
    mapdata->Locations = loadTZDB ();
    if (!mapdata->Locations) {
	g_warning (_("Cannot load timezone data"));
	return NULL;
    }

    mapdata->citylist = create_location_list (mapdata);
    draw_cities (WorldMap);

    mapdata->statusbar = gtk_statusbar_new ();
    mapdata->views = create_view_menu (mapdata);
    
    return mapdata;
}

#if 0
int
main (int argc, char **argv)
{
    GtkWidget *frame;
    GtkWidget *hbox1, *hbox2;
    GtkWidget *vbox1, *vbox2;
    GtkWidget *viewcombo;
    GtkWidget *statusbar;
    GtkWidget *aframe;
    GtkWidget *mainwindow;
    MapData *mapdata;
    GtkWidget *cbutton;

    gnome_init ("gglobe", "0.1", argc, argv);

    mapdata = new_mapdata ();
    
    mainwindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_position (GTK_WINDOW (mainwindow), GTK_WIN_POS_CENTER);
    gtk_window_set_title (GTK_WINDOW (mainwindow), _("gglobe-canvas"));

    gtk_container_add (GTK_CONTAINER (mainwindow), WorldMap->canvas);

    gtk_widget_show (statusbar);

    /* add View combo box */
    aframe = gtk_frame_new (NULL);
    gtk_box_pack_start (GTK_BOX (hbox1), frame, FALSE, FALSE, 2);

    vbox2 = gtk_vbox_new (FALSE, 2);
    gtk_container_add (GTK_CONTAINER (frame), vbox2);

    hbox2 = gtk_hbox_new (FALSE, 2);
    gtk_box_pack_start (GTK_BOX (vbox2), hbox2, FALSE, FALSE, 2);
     
    viewcombo = create_view_menu (mapdata);
    gtk_box_pack_start (GTK_BOX (hbox2), gtk_label_new ("View: "),
			FALSE, FALSE, 0);
    gtk_box_pack_start (GTK_BOX (hbox2), viewcombo, FALSE, FALSE, 2);
    cbutton = gtk_check_button_new_with_label ("System clock uses UTC");
    gtk_box_pack_start (GTK_BOX (hbox2), cbutton, FALSE, FALSE, 5);

    /* put cities on the world map */

    /* add list of all timezones */
    frame = gtk_frame_new (NULL);
    gtk_box_pack_start (GTK_BOX (vbox2), 
			create_location_list (mapdata), 
			TRUE, TRUE, 2);
    
    /* display and wait */
    gtk_widget_show_all (mainwindow);

    /* pick New York City as default */
    set_selection (mapdata,
		   find_location (mapdata->Locations, "America/New_York"),
		   TRUE);

    gtk_main ();
    
    return 0;
}

#endif

typedef struct tzObject_t {
    PyObject_HEAD;
    MapData * mapdata;
} tzObject;

static PyObject * setcurrent (tzObject * o, PyObject * args);

static PyMethodDef tzObjectMethods[] = {
    { "setcurrent", (PyCFunction) setcurrent, METH_VARARGS, NULL },
    { NULL }
};

/*  typedef struct tzObject_t tzObject; */

static PyObject * tzGetAttr(tzObject * o, char * name);
static void tzDealloc (tzObject * o);

static PyTypeObject tzType = {
	PyObject_HEAD_INIT(&PyType_Type)
	0,				/* ob_size */
	"timezonemap",			/* tp_name */
	sizeof(tzObject),		/* tp_size */
	0,				/* tp_itemsize */
	(destructor) tzDealloc, 	/* tp_dealloc */
	0,				/* tp_print */
	(getattrfunc) tzGetAttr, 	/* tp_getattr */
	0,				/* tp_setattr */
	0,				/* tp_compare */
	0,				/* tp_repr */
	0,				/* tp_as_number */
	0,				/* tp_as_sequence */
	0,				/* tp_as_mapping */
};


static PyObject * setcurrent (tzObject * o, PyObject * args) {
    char * loc;
    int index;
    
    if (!PyArg_ParseTuple(args, "s", &loc))
	return NULL;

    index = find_location (o->mapdata->Locations, loc);

    if (index == -1)
	return NULL;
    
    set_selection (o->mapdata, index, TRUE);

    return Py_BuildValue("i", index);
}



static PyObject * tzGetAttr(tzObject * o, char * name) {
    if (!strncmp (name, "map", 3)) {
	return PyGtk_New((GtkObject *) o->mapdata->map->canvas);
    }
    if (!strncmp (name, "citylist", 8)) {
	return PyGtk_New((GtkObject *) o->mapdata->citylist);
    }
    if (!strncmp (name, "statusbar", 9)) {
	return PyGtk_New((GtkObject *) o->mapdata->statusbar);
    }
    if (!strncmp (name, "views", 5)) {
	return PyGtk_New((GtkObject *) o->mapdata->views);
    }
    return Py_FindMethod(tzObjectMethods, (PyObject *) o, name);
};

static void tzDealloc (tzObject * o)
{
    return;
}

static tzObject * doNewTZ (PyObject * s, PyObject * args);

static PyMethodDef timezoneMethods[] = {
    { "new", (PyCFunction) doNewTZ, METH_VARARGS, NULL },
    { NULL },
};

static tzObject * doNewTZ (PyObject * s, PyObject * args) {
    tzObject *o;
    char * path;
    
    if (!PyArg_ParseTuple(args, "s", &path))
	return NULL;
        
    o = (tzObject *) PyObject_NEW(tzObject, &tzType);
    
    o->mapdata = new_mapdata (path);
    
    if (!WorldMap || !o->mapdata) {
	PyErr_SetString(PyExc_TypeError, "Could not create map view.");
	return NULL;
    }

    return o;
}

#include <gdk_imlib.h>

void inittimezonemap (void) {
    init_pygtk();
    
    Py_InitModule("timezonemap", timezoneMethods);
}