/*----------------------------------------------------------------------*\
 | Graphical Sea Ice retrieval program -- Unix X11 version				|
 |																		|
 | Author: Peter N. Schweitzer (MS 955, USGS, Reston VA 22092)			|
 | Language: Standard C (Developed on GNU gcc under DG/UX 4.31, X11R4)	|
 |																		|
 | Input argument: Name of file containing a list of sea ice files.		|
 | The maximum number of data files that can be simultaneously used		|
 | is twelve; ordinarily one would choose monthly files.				|
\*----------------------------------------------------------------------*/

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/keysym.h>
#include <X11/Xresource.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include <pwd.h>

#ifdef __hpux
#undef FILENAME_MAX
#define FILENAME_MAX 1024
/* typedef XPointer caddr_t; */
#endif

#define see_ice_width 40
#define see_ice_height 40
static unsigned char see_ice_bits[] = {
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x84,
   0x10, 0x42, 0x08, 0x52, 0x4a, 0x29, 0xa5, 0x94, 0x8c, 0x31, 0xc6, 0x18,
   0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x84, 0x10, 0x42, 0x08, 0x52,
   0x4a, 0x29, 0xa5, 0x94, 0x8c, 0x31, 0xc6, 0x18, 0x63, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x21, 0x84, 0x10, 0x42, 0x08, 0x52, 0x4a, 0x00, 0x00, 0x94,
   0x8c, 0x31, 0xfe, 0x7f, 0x62, 0x00, 0x80, 0x01, 0x80, 0x00, 0x21, 0x60,
   0x00, 0x80, 0x08, 0x52, 0x18, 0x00, 0x80, 0x94, 0x8c, 0x04, 0x00, 0x00,
   0x61, 0x00, 0x02, 0x00, 0x00, 0x01, 0x21, 0x01, 0x00, 0x00, 0x09, 0x52,
   0x01, 0x00, 0x00, 0x95, 0x0c, 0x01, 0x00, 0x00, 0x61, 0x00, 0x03, 0x00,
   0x00, 0x01, 0x21, 0x07, 0x00, 0x00, 0x09, 0x52, 0x0e, 0x00, 0x80, 0x95,
   0x8c, 0x1c, 0x00, 0xc0, 0x61, 0x00, 0x38, 0x00, 0xf0, 0x00, 0x21, 0x70,
   0x00, 0x7f, 0x08, 0x52, 0xe2, 0xff, 0x3f, 0x94, 0x8c, 0xc1, 0xff, 0x01,
   0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x84, 0x10, 0x42, 0x08, 0x52,
   0x4a, 0x29, 0xa5, 0x94, 0x8c, 0x31, 0xc6, 0x18, 0x63, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x21, 0x84, 0x10, 0x42, 0x08, 0x52, 0x4a, 0x29, 0xa5, 0x94,
   0x8c, 0x31, 0xc6, 0x18, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

#define MAXLEN 256

#define WIDTH	320
#define HEIGHT	448

#define MISSING_CODE	157
#define LAND_CODE		168
#define GRID_CODE		255

/* Instead of including hdf.h, only the two routines used are declared. */

extern int DFR8getdims (char *name, long *width, long *height, int *haspalette);
extern int DFR8getimage (char *name, unsigned char *dst, long w, long h, unsigned char *pal);

extern void mapll (double latitude, double longitude, double *x, double *y);
extern void mapxy (double x, double y, char hemisphere, double *latitude, double *longitude);

extern char *search_up (char *path, char *target);

/*----------------------------------------------------------------------*\
 | Static data															|
\*----------------------------------------------------------------------*/

static Display *display;
static int screen;
static Colormap cmap = (Colormap)0;
static GC gc;
static GC rgc;
static GC xgc;
static XFontStruct *font_info;
static int font_height,font_width;
static unsigned g_width,g_height;
static unsigned t_width,t_height;
static unsigned p_width,p_height;
static Window t_win;
static Window g_win;
static Window p_win;
static XSizeHints size_hints;
static XrmDatabase commandlineDB, rDB;
static unsigned border_width;
static char window_name[64];
static char program[64];
static char name_of_file[128] = "";

static int tabstop = 4;
static char tab_character = 0x1F;
static char tab_fill_character = 0x20;

static unsigned long color[256];
static unsigned long foreground,background;
static unsigned long white_pixel,black_pixel;
static unsigned long missing_color,land_color,grid_color;

#define MAX_ICE_FILES	160

static int mouse_x = 0, mouse_y = 0;
static unsigned char profile [MAX_ICE_FILES];

struct ice_file {
	char name[64];
	char label[16];
	unsigned char *data;
	XImage *image;
	};

static struct ice_file ice[MAX_ICE_FILES];
static int current = 0;
static int nfiles = 0;

static char hemisphere = 0;

short *topo = NULL;

static int grid = 0;
static int fine_grid = 0;
static int marker = 0;

static int mx0 = 0, my0 = 0;

static char *month_name[] = {
	"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
	};

static int p1x,p1y,p2x,p2y;
static double upa = 0, upb = 0, upc = 0, upd = 0;

struct rect {
	int x,y,w,h;
	};

static struct rect list_rect = {0,0,0,0};

#define nint(x) ((x - floor(x) < 0.5) ? (int)(x) : (int)(x + 0.5))

static char *help[] = {
	"To View Images:     ",
	"  PgUp  = Previous  ",
	"  PgDn  = Next      ",
	"  Home  = First     ",
	"  End   = Last      ",
	"  F2    = Cycle     ",
	"                    ",
	"To Query Points:    ",
	"  Select with Mouse ",
	"  Modify with Arrows",
	"                    ",
	"Helpers (toggle)    ",
	"  G = lat-lon grid  ",
	"  M = mark query pt ",
	"                    ",
	"Exit: Alt-X or Alt-Q",
	NULL
	};

static int opTableEntries = 22;
static XrmOptionDescRec opTable[] = {
{"-background",		"*background",					XrmoptionSepArg,	(caddr_t) NULL},
{"-bd",				"*borderColor",					XrmoptionSepArg,	(caddr_t) NULL},
{"-bg",				"*background",					XrmoptionSepArg,	(caddr_t) NULL},
{"-borderwidth",	"*TopLevelShell.borderWidth",	XrmoptionSepArg,	(caddr_t) NULL},
{"-bordercolor",	"*borderColor",					XrmoptionSepArg,	(caddr_t) NULL},
{"-bw",				"*TopLevelShell.borderWidth",	XrmoptionSepArg,	(caddr_t) NULL},
{"-display",		".display",						XrmoptionSepArg,	(caddr_t) NULL},
{"-fg",				"*foreground",					XrmoptionSepArg,	(caddr_t) NULL},
{"-fn",				"*font",						XrmoptionSepArg,	(caddr_t) NULL},
{"-font",			"*font",						XrmoptionSepArg,	(caddr_t) NULL},
{"-foreground",		"*foreground",					XrmoptionSepArg,	(caddr_t) NULL},
{"-geometry",		".TopLevelShell.geometry",		XrmoptionSepArg,	(caddr_t) NULL},
{"-iconic",			".TopLevelShell.iconic",		XrmoptionNoArg,		(caddr_t) "on"},
{"-name",			".name",						XrmoptionSepArg,	(caddr_t) NULL},
{"-reverse",		"*reverseVideo",				XrmoptionNoArg,		(caddr_t) "on"},
{"-rv",				"*reverseVideo",				XrmoptionNoArg,		(caddr_t) "on"},
{"-title",			".TopLevelShell.title",			XrmoptionSepArg,	(caddr_t) NULL},
{"-land",			"*land",						XrmoptionSepArg,	(caddr_t) NULL},
{"-missing",		"*missing",						XrmoptionSepArg,	(caddr_t) NULL},
{"-grid",			"*grid",						XrmoptionSepArg,	(caddr_t) NULL},
{"-force",			"*forceColors",					XrmoptionSepArg,	(caddr_t) NULL},
{"-xrm",			NULL,							XrmoptionResArg,	(caddr_t) NULL}
};

/*----------------------------------------------------------------------*\
 | Procedures. All are static except main().							|
\*----------------------------------------------------------------------*/

static int byte_order;

#ifndef LSBFirst
#define LSBFirst	0
#endif

#ifndef MSBFirst
#define MSBFirst	1
#endif

static void get_byte_order (void) {
	short w = 255;
	unsigned char *s = (unsigned char *) &w;
	if (*s) byte_order = LSBFirst;
	else byte_order = MSBFirst;
	}

int memicmp (const char *s, const char *t, int n) {
	int d = 0;
	while (n && !(d = toupper (*s++) - toupper (*t++))) n--;
	return (d);
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

static void create_colormap (void) {
	int i;
	XColor cell;

	if (!cmap) cmap = XDefaultColormap (display,screen);

	if (cmap != XDefaultColormap (display,screen))
		for (i=0; i < 128; i++) {
			cell.pixel = i;
			cell.flags = DoRed | DoGreen | DoBlue;
			XQueryColor (display,XDefaultColormap (display,screen),&cell);
			XAllocColor (display,cmap,&cell);
			}

	for (i=0; i <= 100; i++) {
		cell.pixel = i+128;
		cell.flags = DoRed | DoGreen | DoBlue;
		cell.red   = (unsigned short) ((i * 0xFFFF) / 100);
		cell.green = cell.red;
		cell.blue  = (40 + i * 60/100) * 0xFFFF/100;
		if (XAllocColor (display,cmap,&cell)) color[i] = cell.pixel;
		else {
			fprintf (stderr,"Warning: could not allocate color %d\n",i);
			color[i] = i;
			}
		}

	cell.pixel = i++;
	cell.flags = DoRed | DoGreen | DoBlue;
	cell.red   = 0x0000;
	cell.green = 0x0000;
	cell.blue  = 0x0000;
	if (XAllocColor (display,cmap,&cell)) color[254] = cell.pixel;
	else color[254] = BlackPixel(display,screen);
	black_pixel = color[254];

	cell.pixel = i++;
	cell.flags = DoRed | DoGreen | DoBlue;
	cell.red   = 0xFFFF;
	cell.green = 0xFFFF;
	cell.blue  = 0xFFFF;
	if (XAllocColor (display,cmap,&cell)) color[252] = cell.pixel;
	else color[252] = WhitePixel(display,screen);
	white_pixel = color[252];

	}

static void parseOpenDisp (int *argc, char *argv[]) {
	XrmValue value;
	char *str_type[20];
	char myDisplayName[128];
	char str_name[128];

	myDisplayName[0] = 0;

	XrmParseCommand (&commandlineDB,opTable,opTableEntries,argv[0],argc,argv);

	if (*argc > 1) strcpy (name_of_file,argv[1]);

	strcpy (str_name,program);
	strcat (str_name,".display");

	if (XrmGetResource (commandlineDB,str_name,"See_ice.Display",str_type,&value) == True)
		strncpy (myDisplayName,value.addr,(int)value.size);

	if (!(display = XOpenDisplay (myDisplayName))) {
		fprintf (stderr,"%s: Can't open display '%s'\n",argv[0],XDisplayName (myDisplayName));
		exit (1);
		}

	screen = DefaultScreen (display);
	}

static char *GetHomeDir (char *dest) {
	uid_t uid;
	extern uid_t getuid (void);
	struct passwd *pw;
	char *ptr;

	if (ptr = getenv ("HOME")) strcpy (dest,ptr);
	else {
		if (ptr = getenv ("USER")) pw = getpwnam (ptr);
		else {
			uid = getuid();
			pw = getpwuid (uid);
			}
		if (pw) strcpy (dest,pw->pw_dir);
		else strcpy (dest," ");
		}
	return (dest);
	}

static void getUsersDatabase (void) {
	XrmDatabase homeDB, serverDB, applicationDB;

	char filenamebuf[1024];
	char *filename = filenamebuf;
	char *environment;
	char *classname = "See_ice";
	char name[255];
	char *string;

	strcpy (name,"/usr/lib/X11/app-defaults/");
	strcat (name,classname);
	applicationDB = XrmGetFileDatabase (name);
	XrmMergeDatabases (applicationDB,&rDB);

	if (string = XResourceManagerString (display))
		serverDB = XrmGetStringDatabase (string);
	else {
		GetHomeDir (filename);
		strcat (filename,".Xdefaults");
		serverDB = XrmGetFileDatabase (filename);
		}
	XrmMergeDatabases (serverDB,&rDB);

	if ((environment = getenv ("XENVIRONMENT")) == NULL) {
		int len;
		environment = GetHomeDir (filename);
		strcat (environment,".Xdefaults");
		len = strlen (environment);
		gethostname (environment+len,1024-len);
		}
	homeDB = XrmGetFileDatabase (environment);
	XrmMergeDatabases (homeDB,&rDB);
	}

static void mergeOpts (void) {
	char str_name[128];
	char *str_type[20];
	char buffer[20];
	long flags;
	XrmValue value;
	int x,y;
	XColor screen_def;
	char fontname[128];

	XrmMergeDatabases (commandlineDB, &rDB);

	/*------------------------------------------------------------------*\
	 | Determine whether a new colormap should be used.					|
	\*------------------------------------------------------------------*/

	strcpy (str_name,program);
	strcat (str_name,".forceColors");

	if (XrmGetResource (rDB,str_name,"See_ice.ForceColors",str_type,&value) == True)
		strncpy (buffer,value.addr,(int)value.size);
	else
		strcpy (buffer,"False");

	if (memcmp (buffer,"True",4) == 0)
		cmap = XCreateColormap (display,RootWindow(display,screen),DefaultVisual(display,screen),AllocNone);
	else
		cmap = XDefaultColormap (display,screen);

	create_colormap();

	/*------------------------------------------------------------------*\
	 | Get font info from user preference database and load the font.	|
	\*------------------------------------------------------------------*/

	strcpy (str_name,program);
	strcat (str_name,".font");

	if (XrmGetResource (rDB,str_name,"See_ice.Font",str_type,&value) == True)
		strncpy (fontname,value.addr,(int)value.size);
	else
		strcpy (fontname,"9x15bold");

	if ((font_info = XLoadQueryFont (display,fontname)) == NULL) {
		fprintf (stderr,"see_ice: cannot open font %s\n",fontname);
		exit (-1);
		}
	font_height = font_info->max_bounds.ascent + font_info->max_bounds.descent;
	font_width = XTextWidth (font_info," ",1);

	x = 0;
	y = 0;
	t_width  = 640;
	t_height = 768;

	/*------------------------------------------------------------------*\
	 | Get geometry info from user preference database. Assume that the |
	 | width and height figures are specified in characters, so multiply|
	 | the given values by font_width and font_height, respectively.	|
	\*------------------------------------------------------------------*/

	strcpy (str_name,program);
	strcat (str_name,".geometry");

	if (XrmGetResource (rDB,str_name,"See.Geometry",str_type,&value) == True)
		strncpy (buffer,value.addr,(int)value.size);
	else
		buffer[0] = 0;

	if (*buffer) {
		flags = XParseGeometry (buffer,&x,&y,&t_width,&t_height);
		if (XValue & flags)
			if (XNegative & flags)
				x = DisplayWidth (display,screen) + x - t_width;
		if (YValue & flags)
			if (YNegative & flags)
				y = DisplayHeight (display,screen) + y - t_height;
		}

	size_hints.x = x;
	size_hints.y = y;
	size_hints.width = t_width;
	size_hints.height = t_height;

	/*------------------------------------------------------------------*\
	 | Get foreground color info from user database.					|
	\*------------------------------------------------------------------*/

	strcpy (str_name,program);
	strcat (str_name,".foreground");

	if (XrmGetResource (rDB,str_name,"See_ice.Foreground",str_type,&value) == True) {
		strncpy (buffer,value.addr,(int)value.size);
		if (XParseColor (display,cmap,buffer,&screen_def) == 0)
			foreground = black_pixel;
		else {
			XAllocColor (display,cmap,&screen_def);
			foreground = screen_def.pixel;
			}
		}
	else
		foreground = black_pixel;

	/*------------------------------------------------------------------*\
	 | Get background color info from user database.					|
	\*------------------------------------------------------------------*/

	strcpy (str_name,program);
	strcat (str_name,".background");

	if (XrmGetResource (rDB,str_name,"See_ice.Background",str_type,&value) == True) {
		strncpy (buffer,value.addr,(int)value.size);
		if (XParseColor (display,cmap,buffer,&screen_def) == 0)
			background = white_pixel;
		else {
			XAllocColor (display,cmap,&screen_def);
			background = screen_def.pixel;
			}
		}
	else
		background = white_pixel;

	/*------------------------------------------------------------------*\
	 | Get border width info from user database.						|
	\*------------------------------------------------------------------*/

	strcpy (str_name,program);
	strcat (str_name,".borderWidth");

	if (XrmGetResource (rDB,str_name,"See_ice.BorderWidth",str_type,&value) == True) {
		strncpy (buffer,value.addr,(int)value.size);
		border_width = (unsigned) strtoul (buffer,0,0);
		}
	else
		border_width = 4;

	/*------------------------------------------------------------------*\
	 | Get window name from user database.								|
	\*------------------------------------------------------------------*/

	strcpy (str_name,program);
	strcat (str_name,".name");

	if (XrmGetResource (rDB,str_name,"See_ice.Name",str_type,&value) == True)
		strncpy (window_name,value.addr,(int)value.size);
	else
		strcpy (window_name,"Sea Ice Concentration");

	/*------------------------------------------------------------------*\
	 | Get land color info from user database.							|
	\*------------------------------------------------------------------*/

	strcpy (str_name,program);
	strcat (str_name,".land");

	if (XrmGetResource (rDB,str_name,"See_ice.Land",str_type,&value) == True) {
		strncpy (buffer,value.addr,(int)value.size);
		if (XParseColor (display,cmap,buffer,&screen_def) == 0) {
			screen_def.red   = 0x2200;
			screen_def.green = 0x8B00;
			screen_def.blue  = 0x2200;
			XAllocColor (display,cmap,&screen_def);
			land_color = screen_def.pixel;
			}
		else {
			XAllocColor (display,cmap,&screen_def);
			land_color = screen_def.pixel;
			}
		}
	else {
		screen_def.red   = 0x2200;
		screen_def.green = 0x8B00;
		screen_def.blue  = 0x2200;
		XAllocColor (display,cmap,&screen_def);
		land_color = screen_def.pixel;
		}
	color[LAND_CODE] = land_color;

	/*------------------------------------------------------------------*\
	 | Get missing color info from user database.						|
	\*------------------------------------------------------------------*/

	strcpy (str_name,program);
	strcat (str_name,".missing");

	if (XrmGetResource (rDB,str_name,"See_ice.Missing",str_type,&value) == True) {
		strncpy (buffer,value.addr,(int)value.size);
		if (XParseColor (display,cmap,buffer,&screen_def) == 0) {
			missing_color = black_pixel;
			}
		else {
			XAllocColor (display,cmap,&screen_def);
			missing_color = screen_def.pixel;
			}
		}
	else
		missing_color = black_pixel;
	color[MISSING_CODE] = missing_color;

	/*------------------------------------------------------------------*\
	 | Get grid color info from user database.							|
	\*------------------------------------------------------------------*/

	strcpy (str_name,program);
	strcat (str_name,".grid");

	if (XrmGetResource (rDB,str_name,"See_ice.Grid",str_type,&value) == True) {
		strncpy (buffer,value.addr,(int)value.size);
		if (XParseColor (display,cmap,buffer,&screen_def) == 0) {
			screen_def.red   = 0xFFFF;
			screen_def.green = 0x0000;
			screen_def.blue  = 0x0000;
			XAllocColor (display,cmap,&screen_def);
			grid_color = screen_def.pixel;
			}
		else {
			XAllocColor (display,cmap,&screen_def);
			grid_color = screen_def.pixel;
			}
		}
	else {
		screen_def.red   = 0xFFFF;
		screen_def.green = 0x0000;
		screen_def.blue  = 0x0000;
		XAllocColor (display,cmap,&screen_def);
		grid_color = screen_def.pixel;
		}
	color[GRID_CODE] = grid_color;
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

static void user_exit (void) {
	XUnloadFont (display,font_info->fid);
	XFreeGC (display,gc);
	XFreeGC (display,rgc);
	XFreeGC (display,xgc);
	XCloseDisplay (display);
	exit (1);
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

static int io_error_handler (Display *display) {
	user_exit();
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

static void allocate_colors (void);
static void refresh_graphics (XExposeEvent *event);
static void refresh_plot (XExposeEvent *event);
static void refresh_text (XExposeEvent *event);
static void process_key (XKeyEvent *event);
static void process_button (XButtonEvent *e);

void main (int argc, char *argv[]) {
	int i,j,k;
	long w,h;
	long n;
	int x = 0, y = 0;
	char *icon_name = "see_ice";
	Pixmap icon_pixmap;
	XEvent report;
	unsigned long valuemask = 0;
	XGCValues values;
	XWMHints wmhints;
	FILE *in,*list;
	char *s;
	char filename [FILENAME_MAX];
	int ispalette;
	char path [FILENAME_MAX];
	char depth_file [FILENAME_MAX];
	char string[32];

	strcpy (program,argv[0]);

	XrmInitialize();
	parseOpenDisp (&argc,argv);
	getUsersDatabase();
	mergeOpts();

	get_byte_order();

	for (i=0; i < MAX_ICE_FILES; i++) profile[i] = MISSING_CODE;

	current = 0;
	g_width = g_height = 0;

	if (*name_of_file) {

		/*--------------------------------------------------------------*\
		 | Use the input file as a list of file names to read.			|
		\*--------------------------------------------------------------*/

		if (list = fopen (name_of_file,"r")) {
			while (fgets (filename,FILENAME_MAX,list)) {
				if (s = strrchr (filename,'\n')) *s = 0;
				if (s = strrchr (filename,'\r')) *s = 0;

				/*------------------------------------------------------*\
				 | If the file list entry consists of name\tlabel, then	|
				 | use the name part as the file name, and the label	|
				 | part as the file's label, which appears in the list	|
				 | and on the horizontal axis of the plot.				|
				\*------------------------------------------------------*/

				if (s = strtok (filename," \t")) {
					strcpy (ice[current].name,s);
					s = strtok (NULL," \t");
					if (s) strcpy (ice[current].label,s);
					else *ice[current].label = 0;
					}

				/*------------------------------------------------------*\
				 | Read the file, checking its image dimensions against	|
				 | the images that have already been read.				|
				\*------------------------------------------------------*/

				if (DFR8getdims (ice[current].name,&w,&h,&ispalette) == 0) {
					if (g_width == 0 && g_height == 0) {
						g_width  = w;
						g_height = h;
						}
					else
						if (w != g_width || h != g_height) {
							fprintf (stderr,"Error: expecting %ld x %ld, got %ld x %ld image in %s\n",g_width,g_height,w,h,ice[current].name);
							exit (1);
							}
					if (!(ice[current].data = (unsigned char *) malloc (g_width*g_height))) {
						printf ("Error: unable to allocate memory for ice data for file %s\n",ice[current].name);
						exit (1);
						}
					if (DFR8getimage (ice[current].name,ice[current].data,w,h,NULL) != 0) {
						fprintf (stderr,"Error: unable to read image from %s\n",ice[current].name);
						exit (1);
						}
					ice[current].image = NULL;
					}
				else {
					fprintf (stderr,"Error: unable to get dimensions of file %s\n",filename);
					exit (1);
					}
				current++;
				}
			fclose (list);
			}
		else {
			fprintf (stderr,"Error: The file list \"%s\" could not be opened.\n",name_of_file);
			exit (1);
			}
		}
	else {
		fprintf (stderr,"Error: You must specify a list file on the command line\n");
		exit (1);
		}

	nfiles = current;
	current = 0;

	/*------------------------------------------------------------------*\
	 | Try to decide what hemisphere we're looking at.					|
	\*------------------------------------------------------------------*/

	switch (g_height) {
		case 332:
			hemisphere = 'S';
			break;
		case 448:
			hemisphere = 'N';
			break;
		default:
			hemisphere = 0;
			while (hemisphere == 0) {
				printf ("Are these files from the North or South pole? (N/S): ");
				gets (string);
				*string = toupper (*string);
				if (*string == 'N' || *string == 'S') hemisphere = *string;
				if ((memicmp (string,"Quit",4) == 0) ||
					(memicmp (string,"Exit",4) == 0) ||
					(memicmp (string,"Stop",4) == 0)) exit (1);
				}
			break;
		}

	/*------------------------------------------------------------------*\
	 | Try to read the appropriate depth data file.						|
	 |																	|
	 | Assume that the file is called etopo5.n or etopo5.s and resides	|
	 | either in the same directory as the data files, its parent, or	|
	 | somewhere up the tree towards the root.							|
	\*------------------------------------------------------------------*/

	strcpy (path,ice[current].name);
	if (s = strrchr (path,'/')) *(s+1) = 0;
	else *path = 0;
	strcpy (depth_file,"etopo5.");
	n = strlen (depth_file);
	depth_file[n++] = tolower (hemisphere);
	depth_file[n] = 0;
	if (s = search_up (path,depth_file)) {
		strcpy (depth_file,s);
		if (in = fopen (depth_file,"rb")) {
			if (topo = (short *) calloc (g_width * g_height, sizeof(short))) {
				n = fread (topo,sizeof(short),g_width*g_height,in);
				if (n < g_width * g_height)
					fprintf (stderr,"Warning: expected %d, got %d words from %s\n",
						g_width * g_height,n,depth_file);
				if (byte_order == LSBFirst) {
					char *scratch;
					n = g_width * sizeof (short);
					if (scratch = (char *) malloc (n)) {
						s = (char *)topo;
						for (i=0; i < g_height; i++) {
							swab (s,scratch,n);
							memcpy (s,scratch,n);
							s += n;
							}
						free (scratch);
						}
					}
				}
			else
				fprintf (stderr,"Warning: could not allocate space for topographic data\n");
			fclose (in);
			}
		else
			fprintf (stderr,"Warning: could not open topographic data file %s\n",depth_file);
		}
	else
		fprintf (stderr,"Warning: could not locate topographic data file %s\n",depth_file);

	/*------------------------------------------------------------------*\
	 | Set up GUI														|
	\*------------------------------------------------------------------*/

	/* create window */
	t_win = XCreateSimpleWindow (display,
								 RootWindow(display,screen),
								 x,y,t_width,t_height,
								 border_width,
								 foreground,
								 background);

	/* create pixmap of depth 1 (bitmap) for icon */
	icon_pixmap = XCreateBitmapFromData (display,t_win,(char *)see_ice_bits,see_ice_width,see_ice_height);

	/* initialize size hint property for window manager */
	size_hints.flags = PPosition | PSize | PMinSize;
	size_hints.min_width = 0;
	size_hints.min_height = 0;

	/* set properties for window manager (always before mapping) */
	XSetStandardProperties (display,
							t_win,
							window_name,
							icon_name,
							icon_pixmap,
							argv,
							argc,
							&size_hints);

	/* Select event types wanted */
	XSelectInput (display,t_win,
				  ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask);

	/* Create graphics contexts */

	gc = XCreateGC (display,t_win,valuemask,&values);
	XSetFont (display,gc,font_info->fid);
	XSetBackground (display,gc,background);
	XSetForeground (display,gc,foreground);
	XSetWindowColormap (display,t_win,cmap);

	rgc = XCreateGC (display,t_win,valuemask,&values);
	XSetFont (display,rgc,font_info->fid);
	XSetBackground (display,rgc,foreground);
	XSetForeground (display,rgc,background);

	xgc = XCreateGC (display,t_win,valuemask,&values);
	XSetBackground (display,xgc,0);
	if (background == 0) XSetForeground (display,xgc,foreground);
	else				 XSetForeground (display,xgc,background);
	XSetFunction (display,xgc,GXxor);

	wmhints.flags = InputHint | IconPixmapHint;
	wmhints.input = True;
	wmhints.icon_pixmap = icon_pixmap;
	XSetWMHints (display,t_win,&wmhints);

	/* create graphics window */
	x = font_width;
	y = 5 * font_height/2;
	g_win = XCreateSimpleWindow (display,t_win,x,y,g_width,g_height,1,foreground,background);
	XSetStandardProperties (display,g_win,window_name,icon_name,icon_pixmap,argv,argc,&size_hints);
	XSelectInput (display,g_win, ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask);

	/* create plot window */
	if (nfiles <= 15) {
		x = font_width;
		for (i=0; help[i]; i++);
		y = (2 + nfiles + i) * font_height + font_height/2;
		if (y < 3 * font_height + g_height) y = 3 * font_height + g_height;
		p_width = 512;
		p_height = 224;
		}
	else {
		x = font_width;
		y = 3 * font_height + g_height;
		p_width = g_width;
		p_height = g_width/2;
		}
	p_win = XCreateSimpleWindow (display,t_win,x,y,p_width,p_height,1,foreground,background);
	XSetStandardProperties (display,p_win,window_name,icon_name,icon_pixmap,argv,argc,&size_hints);
	XSelectInput (display,p_win, ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask);

	/* Display the windows */
	XMapWindow (display,t_win);
	XMapWindow (display,g_win);
	XMapWindow (display,p_win);

	/*------------------------------------------------------------------*\
	 | Set a fatal IO error handler to exit gracefully when the user	|
	 | closes the window through the window manager.					|
	\*------------------------------------------------------------------*/

	XSetIOErrorHandler (io_error_handler);

	/*------------------------------------------------------------------*\
	 | Event loop														|
	\*------------------------------------------------------------------*/

	while (1) {
		XNextEvent (display,&report);
		switch (report.type) {

			case Expose:
				if (report.xexpose.window == t_win) refresh_text (&report.xexpose);
				if (report.xexpose.window == g_win) refresh_graphics (&report.xexpose);
				if (report.xexpose.window == p_win) refresh_plot (&report.xexpose);
				break;

			case ConfigureNotify:
				if (report.xconfigure.window == t_win) {
					t_width = report.xconfigure.width;
					t_height = report.xconfigure.height;
					}
				else
					if (report.xconfigure.window == g_win) {
						g_width = report.xconfigure.width;
						g_height = report.xconfigure.height;
						}
					else
						if (report.xconfigure.window == p_win) {
							p_width = report.xconfigure.width;
							p_height = report.xconfigure.height;
							}
				break;

			case ButtonPress:
				process_button ((XButtonEvent *)&report);
				break;

			case KeyPress:
				process_key (&report.xkey);
				break;

			case MappingNotify:
				XRefreshKeyboardMapping (&report.xmapping);
				break;

			default:
				break;
			}
		}
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

static void xytoll (int x, int y, double *lat, double *lon) {
	double ax,ay;

	ax = ((double) x) * 25.0 - (((hemisphere == 'N') ? 3850.0 : 3950.0) - 12.5);
	ay = ((double) g_height - y) * 25.0 - (((hemisphere == 'N') ? 5350.0 : 3950.0) - 12.5);
	mapxy (ax,ay,hemisphere,lat,lon);
	}

static void lltoxy (double lat, double lon, int *x, int *y) {
	double ax,ay;

	lon += (hemisphere == 'N') ? 45.0 : 180.0;
	if (lon > 360.0) lon -= 360.0;
	mapll (lat,lon,&ax,&ay);
	*x = nint ((ax + ((hemisphere == 'N') ? 3850.0 : 3950.0) - 12.5)/25.0);
	*y = nint ((ay + ((hemisphere == 'N') ? 5350.0 : 3950.0) - 12.5)/25.0);
	*y = g_height - *y;
	}

static void draw_2degree_grid (void) {
	int i,j,w,h;
	int px,py,qx,qy;
	double sign;

	if (grid == 0) return;

	XSetForeground (display,gc,grid_color);
	sign = ((hemisphere == 'N') ? 1.0 : -1.0);
	for (i=0; i < 360; i+=2) {
		lltoxy (sign*30.0,(double)i,&px,&py);
		lltoxy (sign*89.0,(double)i,&qx,&qy);
		XDrawLine (display,g_win,gc,px,py,qx,qy);
		}
	lltoxy (90.0,0.0,&px,&py);
	for (j=88; j > 40; j-=2) {
		lltoxy (sign*(double)j,0.0,&qx,&qy);
		w = (px-qx)*(px-qx) + (py-qy)*(py-qy);
		w = h = (int) sqrt ((double)w);
		XDrawArc (display,g_win,gc,px-w,py-h,2*w,2*h,0,360*64);
		}
	XSetForeground (display,gc,foreground);
	}

static void draw_5degree_grid (void) {
	int i,j,w,h;
	int px,py,qx,qy;
	double sign;

	if (grid == 0) return;

	XSetForeground (display,gc,grid_color);
	sign = ((hemisphere == 'N') ? 1.0 : -1.0);
	for (i=0; i < 360; i+=5) {
		lltoxy (sign*30.0,(double)i,&px,&py);
		lltoxy (sign*89.0,(double)i,&qx,&qy);
		XDrawLine (display,g_win,gc,px,py,qx,qy);
		}
	lltoxy (90.0,0.0,&px,&py);
	for (j=85; j > 40; j-=5) {
		lltoxy (sign*(double)j,0.0,&qx,&qy);
		w = (px-qx)*(px-qx) + (py-qy)*(py-qy);
		w = h = (int) sqrt ((double)w);
		XDrawArc (display,g_win,gc,px-w,py-h,2*w,2*h,0,360*64);
		}
	XSetForeground (display,gc,foreground);
	}

static void draw_grid (void) {
	int i,j,w,h;
	int px,py,qx,qy;
	double sign;

	if (grid == 0) return;

	if (fine_grid) {
		draw_5degree_grid();
		return;
		}

	XSetForeground (display,gc,grid_color);
	sign = ((hemisphere == 'N') ? 1.0 : -1.0);

	for (i=0; i < 360; i+=30) {
		lltoxy (sign*30.0,(double)i,&px,&py);
		lltoxy (sign*89.0,(double)i,&qx,&qy);
		XDrawLine (display,g_win,gc,px,py,qx,qy);
		}
	for (i=0; i < 360; i+=10) {
		lltoxy (sign*66.7,(double)i,    &px,&py);
		lltoxy (sign*66.7,(double)(i+5),&qx,&qy);
		XDrawLine (display,g_win,gc,px,py,qx,qy);
		}
	lltoxy (90.0,0.0,&px,&py);
	for (j=8; j > 3; j--) {
		lltoxy (sign*(double)j*10,0.0,&qx,&qy);
		w = (px-qx)*(px-qx) + (py-qy)*(py-qy);
		w = h = (int) sqrt ((double)w);
		XDrawArc (display,g_win,gc,px-w,py-h,2*w,2*h,0,360*64);
		}
	XSetForeground (display,gc,foreground);
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

static struct rect mark = {0,0,0,0};

static void hide_marker (void) {
	XPutImage (display,g_win,gc,ice[current].image,mark.x,mark.y,mark.x,mark.y,mark.w,mark.h);
	}

static void show_marker (void) {
	mark.x = ((mouse_x > 7) ? mouse_x - 8 : 0);
	mark.y = ((mouse_y > 7) ? mouse_y - 8 : 0);
	mark.w = 16;
	mark.h = 16;
	XSetForeground (display,gc,foreground);
	XDrawRectangle (display,g_win,gc,mouse_x-3,mouse_y-3,6,6);
	XSetForeground (display,gc,background);
	XDrawRectangle (display,g_win,gc,mouse_x-4,mouse_y-4,8,8);
	XSetForeground (display,gc,foreground);
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

static void hexdump (unsigned char *b, int n) {
	int i = 0;

	if (!n) return;
	do {
		if (i && !(i & 0xF)) printf ("\n");
		printf ("%02X ",b[i++]);
		} while (i < n);
	printf ("\n");
	}

static void describe_image (XImage *image) {
	int i;
	char *format, *byte_order_tag, *bit_order_tag;
	unsigned char *s;

	printf ("XImage at 0x%X = {\n",image);
	printf ("\twidth            = %d\n",image->width);
	printf ("\theight           = %d\n",image->height);
	printf ("\txoffset          = %d\n",image->xoffset);

	if (image->format == XYBitmap) format = "XYBitmap";
	if (image->format == XYPixmap) format = "XYPixmap";
	if (image->format ==  ZPixmap) format =  "ZPixmap";
	printf ("\tformat           = %s\n",format);
	printf ("\tdata address     = 0x%X\n",image->data);

	if (image->byte_order == LSBFirst) byte_order_tag = "LSBFirst";
	if (image->byte_order == MSBFirst) byte_order_tag = "MSBFirst";
	printf ("\tbyte_order       = %s\n",byte_order_tag);
	printf ("\tbitmap_unit      = %d\n",image->bitmap_unit);

	if (image->bitmap_bit_order == LSBFirst) bit_order_tag = "LSBFirst";
	if (image->bitmap_bit_order == MSBFirst) bit_order_tag = "MSBFirst";
	printf ("\tbitmap_bit_order = %s\n",bit_order_tag);
	printf ("\tbitmap_pad       = %d\n",image->bitmap_pad);
	printf ("\tdepth            = %d\n",image->depth);
	printf ("\tbytes_per_line   = %d\n",image->bytes_per_line);
	printf ("\tbits_per_pixel   = %d\n",image->bits_per_pixel);
	printf ("\tred_mask         = 0x%X\n",image->red_mask);
	printf ("\tgreen_mask       = 0x%X\n",image->green_mask);
	printf ("\tblue_mask        = 0x%X\n",image->blue_mask);
	printf ("\tobdata           = 0x%X\n",image->obdata);
	printf ("\tcreate_image()   = 0x%X\n",image->f.create_image);
	printf ("\tdestroy_image()  = 0x%X\n",image->f.destroy_image);
	printf ("\tget_pixel()      = 0x%X\n",image->f.get_pixel);
	printf ("\tput_pixel()      = 0x%X\n",image->f.put_pixel);
	printf ("\tsub_image()      = 0x%X\n",image->f.sub_image);
	printf ("\tadd_pixel()      = 0x%X\n",image->f.add_pixel);
	printf ("\t};\n");

	printf ("image->data at 0x%X = {\n",image->data);
	s = (unsigned char *) image->data;
	for (i=0; i < image->depth; i++) {
		printf ("bit plane %d:\n",i);
		hexdump (s,image->bytes_per_line);
		s += image->height * image->bytes_per_line;
		}
	printf ("\n};\n");
	}

static void msb_scramble8 (unsigned char *s) {
	unsigned char d[8];

	d[0] = (s[0] & 0x80) >> 0 | (s[1] & 0x80) >> 1 | (s[2] & 0x80) >> 2 | (s[3] & 0x80) >> 3 | (s[4] & 0x80) >> 4 | (s[5] & 0x80) >> 5 | (s[6] & 0x80) >> 6 | (s[7] & 0x80) >> 7;
	d[1] = (s[0] & 0x40) << 1 | (s[1] & 0x40) >> 0 | (s[2] & 0x40) >> 1 | (s[3] & 0x40) >> 2 | (s[4] & 0x40) >> 3 | (s[5] & 0x40) >> 4 | (s[6] & 0x40) >> 5 | (s[7] & 0x40) >> 6;
	d[2] = (s[0] & 0x20) << 2 | (s[1] & 0x20) << 1 | (s[2] & 0x20) >> 0 | (s[3] & 0x20) >> 1 | (s[4] & 0x20) >> 2 | (s[5] & 0x20) >> 3 | (s[6] & 0x20) >> 4 | (s[7] & 0x20) >> 5;
	d[3] = (s[0] & 0x10) << 3 | (s[1] & 0x10) << 2 | (s[2] & 0x10) << 1 | (s[3] & 0x10) >> 0 | (s[4] & 0x10) >> 1 | (s[5] & 0x10) >> 2 | (s[6] & 0x10) >> 3 | (s[7] & 0x10) >> 4;
	d[4] = (s[0] & 0x08) << 4 | (s[1] & 0x08) << 3 | (s[2] & 0x08) << 2 | (s[3] & 0x08) << 1 | (s[4] & 0x08) >> 0 | (s[5] & 0x08) >> 1 | (s[6] & 0x08) >> 2 | (s[7] & 0x08) >> 3;
	d[5] = (s[0] & 0x04) << 5 | (s[1] & 0x04) << 4 | (s[2] & 0x04) << 3 | (s[3] & 0x04) << 2 | (s[4] & 0x04) << 1 | (s[5] & 0x04) >> 0 | (s[6] & 0x04) >> 1 | (s[7] & 0x04) >> 2;
	d[6] = (s[0] & 0x02) << 6 | (s[1] & 0x02) << 5 | (s[2] & 0x02) << 4 | (s[3] & 0x02) << 3 | (s[4] & 0x02) << 2 | (s[5] & 0x02) << 1 | (s[6] & 0x02) >> 0 | (s[7] & 0x02) >> 1;
	d[7] = (s[0] & 0x01) << 7 | (s[1] & 0x01) << 6 | (s[2] & 0x01) << 5 | (s[3] & 0x01) << 4 | (s[4] & 0x01) << 3 | (s[5] & 0x01) << 2 | (s[6] & 0x01) << 1 | (s[7] & 0x01) << 0;
	s[0] = d[0]; s[1] = d[1]; s[2] = d[2]; s[3] = d[3]; s[4] = d[4]; s[5] = d[5]; s[6] = d[6]; s[7] = d[7];
	}

static void lsb_scramble8 (unsigned char *s) {
	unsigned char d[8];

	d[0] = (s[7] & 0x80) >> 0 | (s[6] & 0x80) >> 1 | (s[5] & 0x80) >> 2 | (s[4] & 0x80) >> 3 | (s[3] & 0x80) >> 4 | (s[2] & 0x80) >> 5 | (s[1] & 0x80) >> 6 | (s[0] & 0x80) >> 7;
	d[1] = (s[7] & 0x40) << 1 | (s[6] & 0x40) >> 0 | (s[5] & 0x40) >> 1 | (s[4] & 0x40) >> 2 | (s[3] & 0x40) >> 3 | (s[2] & 0x40) >> 4 | (s[1] & 0x40) >> 5 | (s[0] & 0x40) >> 6;
	d[2] = (s[7] & 0x20) << 2 | (s[6] & 0x20) << 1 | (s[5] & 0x20) >> 0 | (s[4] & 0x20) >> 1 | (s[3] & 0x20) >> 2 | (s[2] & 0x20) >> 3 | (s[1] & 0x20) >> 4 | (s[0] & 0x20) >> 5;
	d[3] = (s[7] & 0x10) << 3 | (s[6] & 0x10) << 2 | (s[5] & 0x10) << 1 | (s[4] & 0x10) >> 0 | (s[3] & 0x10) >> 1 | (s[2] & 0x10) >> 2 | (s[1] & 0x10) >> 3 | (s[0] & 0x10) >> 4;
	d[4] = (s[7] & 0x08) << 4 | (s[6] & 0x08) << 3 | (s[5] & 0x08) << 2 | (s[4] & 0x08) << 1 | (s[3] & 0x08) >> 0 | (s[2] & 0x08) >> 1 | (s[1] & 0x08) >> 2 | (s[0] & 0x08) >> 3;
	d[5] = (s[7] & 0x04) << 5 | (s[6] & 0x04) << 4 | (s[5] & 0x04) << 3 | (s[4] & 0x04) << 2 | (s[3] & 0x04) << 1 | (s[2] & 0x04) >> 0 | (s[1] & 0x04) >> 1 | (s[0] & 0x04) >> 2;
	d[6] = (s[7] & 0x02) << 6 | (s[6] & 0x02) << 5 | (s[5] & 0x02) << 4 | (s[4] & 0x02) << 3 | (s[3] & 0x02) << 2 | (s[2] & 0x02) << 1 | (s[1] & 0x02) >> 0 | (s[0] & 0x02) >> 1;
	d[7] = (s[7] & 0x01) << 7 | (s[6] & 0x01) << 6 | (s[5] & 0x01) << 5 | (s[4] & 0x01) << 4 | (s[3] & 0x01) << 3 | (s[2] & 0x01) << 2 | (s[1] & 0x01) << 1 | (s[0] & 0x01) << 0;
	s[0] = d[0]; s[1] = d[1]; s[2] = d[2]; s[3] = d[3]; s[4] = d[4]; s[5] = d[5]; s[6] = d[6]; s[7] = d[7];
	}

static void (*scramble8)(unsigned char *s);

static void convert_image (unsigned char *dst, unsigned char *src) {
	int x,y,k;
	unsigned char b[8];
	unsigned char *s,*d;

	s = src;
	d = dst;
	if (!s || !d) return;

	if (g_width % 8 == 0)
		for (y=0; y < g_height; y++)
			for (x=0; x < g_width; x += 8) {
				memcpy (b,s,8);
				for (k=0; k < 8; k++) b[k] = color[b[k]];
				scramble8 (b);
				for (k=0; k < 8; k++) *(d + k * g_width * g_height / 8) = b[k];
				s += 8;
				d++;
				}
	else {
		int m,w;
		int bytes_per_line;

		w = 8 * (g_width/8);
		m = g_width - w;
		bytes_per_line = 1 + g_width/8;

		for (y=0; y < g_height; y++) {
			for (x=0; x < w; x += 8) {
				memcpy (b,s,8);
				for (k=0; k < 8; k++) b[k] = color[b[k]];
				scramble8 (b);
				for (k=0; k < 8; k++) *(d + k * bytes_per_line * g_height) = b[k];
				s += 8;
				d++;
				}
			if (m) {
				memset (b,0,8);
				memcpy (b,s,m);
				for (k=0; k < m; k++) b[k] = color[b[k]];
				scramble8 (b);
				for (k=0; k < 8; k++) *(d + k * bytes_per_line * g_height) = b[k];
				s += m;
				d++;
				}
			}
		}
	}

static XImage *create_image (unsigned char *image_data, unsigned width, unsigned height) {
	Visual *p_visual;
	XImage *image;
	unsigned depth;
	int format;
	int offset;
	int bitmap_pad;
	int bytes_per_line;

	depth = 8;
	p_visual = DefaultVisual (display,screen);
	format = XYPixmap;
	offset = 0;
	bitmap_pad = 32;
	bytes_per_line = width/depth;
	if (width % depth) bytes_per_line++;

	image = XCreateImage (display,p_visual,depth,format,offset,(char *)image_data,width,height,bitmap_pad,bytes_per_line);

	if (image->bitmap_bit_order == MSBFirst)
		scramble8 = msb_scramble8;
	else
		scramble8 = lsb_scramble8;

	return (image);
	}

#define FAST_IMAGE		1

static void refresh_graphics (XExposeEvent *event) {
	int x,y;
	unsigned w,h;
	unsigned char *s,c;

	if (!ice[current].image) {
		#ifdef FAST_IMAGE
			h = g_height;
			w = 8 * (g_width/8);
			if (g_width % 8) w += 8;
			if (!(s = (unsigned char *) malloc (w*h))) {
				printf ("Error: unable to allocate space for image %s\n",ice[current].name);
				return;
				}
			if (ice[current].image = create_image (s,g_width,g_height)) {
				convert_image (s,ice[current].data);
				/* describe_image (ice[current].image); */
				}
			else {
				printf ("Error: unable to create XImage for %s\n",ice[current].name);
				return;
				}
		#else
			s = ice[current].data;
			for (y=0; y < g_height; y++)
				for (x=0; x < g_width; x++) {
					XSetForeground (display,gc,color[*s++]);
					XDrawPoint (display,g_win,gc,x,y);
					}
			ice[current].image = XGetImage (display,g_win,0,0,g_width,g_height,0xFF,XYPixmap);
			/* describe_image (ice[current].image); */
		#endif
		}
	if (event) {
		x = event->x;
		y = event->y;
		w = (unsigned) event->width;
		h = (unsigned) event->height;
		}
	else {
		x = 0;
		y = 0;
		w = g_width;
		h = g_height;
		}
	XPutImage (display,g_win,gc,ice[current].image,x,y,x,y,w,h);
	draw_grid();
	if (marker) show_marker();
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/

static void get_new_profile (void) {
	int i;
	unsigned long k;

	if (mouse_x != mx0 || mouse_y != my0) {
		k = g_width * mouse_y + mouse_x;
		for (i=0; i < nfiles; i++) profile[i] = (unsigned char) ice[i].data[k];
		mx0 = mouse_x;
		my0 = mouse_y;
		}
	}

static void draw_plot_axes (void) {
	int i;
	int px,py;
	double u1x,u1y,u2x,u2y;
	char string[8];

	/*--------------------------------------------------------------*\
	 | Calculate the transformation for plotting the data.			|
	\*--------------------------------------------------------------*/

	p1x = 5 * font_width;
	p1y = p_height - 2 * font_height;
	p2x = p_width - 2 * font_width;
	p2y = font_height;

	u1x =  -0.5;
	u1y =   0.0;
	u2x =  -0.5 + (double) nfiles;
	u2y = 100.0;

	upa = ((double) (p2x - p1x)) / (u2x - u1x);
	upb = ((double) p1x) - u1x * upa;
	upc = ((double) (p2y - p1y)) / (u2y - u1y);
	upd = ((double) p1y) - u1y * upc;

	/*--------------------------------------------------------------*\
	 | Axis edges (lower left)										|
	\*--------------------------------------------------------------*/

	XDrawLine (display,p_win,gc,p1x,p2y,p1x,p1y);
	XDrawLine (display,p_win,gc,p1x,p1y,p2x,p1y);

	/*--------------------------------------------------------------*\
	 | X axis ticks and labels										|
	\*--------------------------------------------------------------*/

	for (i=0; i < nfiles; i++) {
		px = nint (upa * (double)i + upb);
		py = p1y;
		XDrawLine (display,p_win,gc,px,py,px,py+4);
		px -= strlen(ice[i].label) * font_width/2;
		py += font_height;
		XDrawImageString (display,p_win,gc,px,py,ice[i].label,strlen(ice[i].label));
		}

	/*--------------------------------------------------------------*\
	 | Y axis ticks and labels										|
	\*--------------------------------------------------------------*/

	for (i=0; i <= 100; i+=20) {
		px = p1x;
		py = nint (upc * (double)i + upd);
		XDrawLine (display,p_win,gc,px,py,px-4,py);
		px -= 5 * font_width;
		py += font_height/3;
		sprintf (string,"%3d%%",i);
		XDrawImageString (display,p_win,gc,px,py,string,strlen (string));
		}
	}

static void draw_plot_data (void) {
	int i;
	int px,py,x0,y0;

	x0 = 0;
	y0 = 0;

	for (i=0; i < nfiles; i++) {
		px = nint (upa * (double)i + upb);
		py = nint (upc * (double)profile[i] + upd);
		if (profile[i] != MISSING_CODE &&
			profile[i] != LAND_CODE) {
			if (nfiles <= 12) XFillRectangle (display,p_win,xgc,px-2,py-2,5,5);
			if (y0 > p2y && !(y0 == p1y && py == p1y))
				XDrawLine (display,p_win,xgc,x0,y0,px,py);
			}
		x0 = px;
		y0 = py;
		}
	}

static void refresh_plot (XExposeEvent *event) {
	if (event) {
		XClearWindow (display,p_win);
		get_new_profile();
		draw_plot_axes();
		draw_plot_data();
		}
	else {
		draw_plot_data();
		get_new_profile();
		draw_plot_data();
		}
	}

static int expand_tabs (char *t, char *s, int n) {
	int i = 0;
	while (*s && n > 0) {
		if (*s == '\t') {
			*t++ = tab_character;
			i++;
			while (i % tabstop) {
				*t++ = tab_fill_character;
				i++;
				}
			}
		else {
			*t++ = *s;
			i++;
			}
		s++;
		n--;
		if (i > MAXLEN - tabstop) break;
		}
	while (n > 0 && i < MAXLEN) {
		*t++ = ' ';
		i++;
		n--;
		}
	*t = 0;
	return (i);
	}

static void draw_top_line (void) {
	int x,y;
	char string[MAXLEN];

	x = font_width;
	y = font_height;

	if (hemisphere == 'N')
		strcpy (string,"Arctic Sea Ice Cover");
	else
		strcpy (string,"Antarctic Sea Ice Cover");
	strcat (string," (");
	strcat (string,ice[current].label);
	strcat (string,")");
	XDrawImageString (display,t_win,gc,x,y,string,strlen(string));
	}

static void draw_file_list (void) {
	int i,j,x,y;
	char string[MAXLEN];

	/*------------------------------------------------------------------*\
	 | List files and data to the right of the image.					|
	\*------------------------------------------------------------------*/

	x = 2 * font_width + g_width + 2;
	if (x < 36 * font_width) x = 36 * font_width;
	y = 2 * font_height;

	list_rect.x = x;
	list_rect.y = y - font_info->max_bounds.ascent;

	j = 0;
	for (i=0; i < nfiles; i++)
		if (strlen (ice[i].label) > j) j = strlen (ice[i].label);

	list_rect.w = font_width * (6 + j);
	list_rect.h = nfiles * font_height;

	for (i=0; i < nfiles; i++) {
		if (profile[i] <= 100) sprintf (string," %3d%% %s ",profile[i],ice[i].label);
		else 				   sprintf (string," ---- %s ",ice[i].label);
		if (i == current)
			XDrawImageString (display,t_win,rgc,x,y,string,strlen(string));
		else
			XDrawImageString (display,t_win,gc,x,y,string,strlen(string));
		y += font_height;
		}
	}

static void draw_help_text (void) {
	int i,x,y;

	/*------------------------------------------------------------------*\
	 | Place instructions below file list if nfiles is 12 or less, and	|
	 | to the right of the list if nfiles is more than 12.				|
	\*------------------------------------------------------------------*/

	if (nfiles <= 12) {
		x = 2 * font_width + g_width + 2;
		y = (2 + nfiles) * font_height;
		}
	else {
		x = list_rect.x + list_rect.w + 2 * font_width;
		y = list_rect.y;
		}

	y += font_height;
	for (i=0; help[i]; i++) {
		XDrawImageString (display,t_win,gc,x,y,help[i],strlen(help[i]));
		y += font_height;
		}
	}

draw_position_indicator (void) {
	int n,x,y;
	double lat,lon;
	char string[40];
	char lat_letter,lon_letter;
	char word[8];

	/*------------------------------------------------------------------*\
	 | Describe the position of the most recent mouse button press.		|
	\*------------------------------------------------------------------*/

	xytoll (mouse_x,mouse_y,&lat,&lon);

	lat_letter = 'N';
	if (lat < 0.0) {
		lat = -lat;
		lat_letter = 'S';
		}
	lon_letter = 'E';
	if (lon > 180.0) {
		lon = 360.0 - lon;
		lon_letter = 'W';
		}

	if (profile[current] == LAND_CODE) strcpy (word,"land");
	else
		if (profile[current] == MISSING_CODE) strcpy (word,"missing");
		else
			sprintf (word,"%d%%",profile[current]);

	x = font_width;
	y = 2 * font_height;

	if (topo) {
		int depth;

		depth = (int) topo [mouse_y * g_width + mouse_x];
		sprintf (string,"%4.1lf\260%c  %5.1lf\260%c  %5dm  (%s)",
			lat,lat_letter,
			lon,lon_letter,
			depth,
			word
			);
		}
	else
		sprintf (string,"%4.1lf\260%c  %5.1lf\260%c  (%s)",
			lat,lat_letter,
			lon,lon_letter,
			word
			);
	n = strlen (string);
	while (n < 35) string[n++] = ' ';
	string[n] = 0;
	XDrawImageString (display,t_win,gc,x,y,string,n);
	}

static void refresh_text (XExposeEvent *event) {
	draw_top_line();
	draw_file_list();
	if (event) draw_help_text();
	draw_position_indicator();
	}

static void process_key (XKeyEvent *event) {
	int i,x,y,n;
	char buffer[20];
	int bufsize = 20;
	KeySym keysym;
	XComposeStatus status;
	int count;
	XEvent next_event;
	long mask;
	int old_grid,old_marker;

	count = XLookupString (event,buffer,bufsize,&keysym,&status);

	if (event->state & Mod1Mask)
		switch (keysym) {
		case XK_q:
			user_exit();
			break;
		case XK_x:
			user_exit();
			break;
		default:
			break;
			}

	switch (keysym) {
		case XK_G:
			grid = (grid ? 0 : 1);
			fine_grid = 1;
			refresh_graphics (0);
			break;
		case XK_g:
			grid = (grid ? 0 : 1);
			fine_grid = 0;
			refresh_graphics (0);
			break;
		case XK_M:
		case XK_m:
			if (marker) {
				hide_marker();
				marker = 0;
				}
			else {
				marker = 1;
				show_marker();
				}
			break;
		case XK_F2:
			mask = ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask;
			old_grid = grid;
			grid = 0;
			old_marker = marker;
			marker = 0;
			while (!XCheckWindowEvent (display,t_win,mask,&next_event)) {
				if (current < nfiles - 1) current++;
				else current = 0;
				refresh_graphics (0);
				refresh_text (0);
				}
			marker = old_marker;
			grid = old_grid;
			refresh_graphics (0);
			XPutBackEvent (display,&next_event);
			break;
		case XK_KP_8:
		case XK_Up:
			if (mouse_y > 0) {
				if (marker) hide_marker();
				mouse_y--;
				if (marker) show_marker();
				refresh_plot (0);
				refresh_text (0);
				}
			break;
		case XK_KP_2:
		case XK_Down:
			if (mouse_y < g_height - 1) {
				if (marker) hide_marker();
				mouse_y++;
				if (marker) show_marker();
				refresh_plot (0);
				refresh_text (0);
				}
			break;
		case XK_KP_4:
		case XK_Left:
			if (mouse_x > 0) {
				if (marker) hide_marker();
				mouse_x--;
				if (marker) show_marker();
				refresh_plot (0);
				refresh_text (0);
				}
			break;
		case XK_KP_6:
		case XK_Right:
			if (mouse_x < g_width - 1) {
				if (marker) hide_marker();
				mouse_x++;
				if (marker) show_marker();
				refresh_plot (0);
				refresh_text (0);
				}
			break;
		case XK_KP_7:
		case XK_Home:
			current = 0;
			refresh_graphics (0);
			refresh_text (0);
			break;
		case XK_KP_1:
		case XK_End:
			current = nfiles - 1;
			refresh_graphics (0);
			refresh_text (0);
			break;
		case XK_KP_9:
		case XK_Prior:
			if (current > 0) current--;
			else current = nfiles - 1;
			refresh_graphics (0);
			refresh_text (0);
			break;
		case XK_KP_3:
		case XK_Next:
			if (current < nfiles - 1) current++;
			else current = 0;
			refresh_graphics (0);
			refresh_text (0);
			break;
		default:
			break;
		}
	}

static void process_button (XButtonEvent *e) {
	int i;

	if (e->window == g_win) {
		if (marker) hide_marker();
		mouse_x = e->x;
		mouse_y = e->y;
		if (marker) show_marker();
		refresh_plot (0);
		refresh_text (0);
		}
	else
		if (e->window == t_win) {
			if (e->x >= list_rect.x && e->x <= list_rect.x + list_rect.w &&
				e->y >= list_rect.y && e->y <= list_rect.y + list_rect.h) {
				i = (e->y - list_rect.y)/font_height;
				if (i >= 0 && i < nfiles) {
					current = i;
					refresh_text(0);
					refresh_graphics(0);
					}
				}
			}
		else
			if (e->window == p_win) {
				i = nint ((((double) e->x) - upb)/upa);
				if (i >= 0 && i < nfiles) {
					current = i;
					refresh_text (0);
					refresh_graphics (0);
					}
				}
	}

/*----------------------------------------------------------------------*\
\*----------------------------------------------------------------------*/
