#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
#include <Xm/Xm.h>
#include <math.h>

#include <stdio.h>
#include <string.h>

#include "bitmap.h"
#include "objects.h"
#include "graph_primi.h"
#include "mapobjects.h"
#include "colors.h"

#define BITMAPDEPTH 1
#define TOO_SMALL 0
#define BIG_ENOUGH 1

/* These are used as arguments to nearly every Xlib routine, so it saves 
 * routine arguments to declare them global.  If there were 
 * additional source files, they would be declared extern there. */
extern Display *display;
extern int screen_num;
extern Widget drawingarea;

static char *progname; /* name this program was invoked by */
static int insideMap = 0;
static Window win;
Window pop = 0;
static Window mapWin;
extern GC gc;
static GC mapgc;
static GC popgc;
extern GC mapgc;
extern GC gcxor;
extern GC gcplane;

static OBJECT *route_from, *route_to;

static unsigned int width, height;	/* window size */
int mapWidth, mapHeight;
double mapWidthcm, mapHeightcm;
unsigned int display_width, display_height;

char *savelines[50];        /* current text lines for text popup window */
int savenumlines;

extern LOCATION map_origin;
extern long map_scale;        /* from coord.c */

extern int media;
extern FILE *ps;

extern int vis_roads;
extern int vis_com;
extern int vis_cities;
extern int vis_airspace;
extern int vis_waypoints;
extern int vis_airfield;
extern int vis_water;
extern int vis_villages;

extern int db_objects, db_visible;   /* number of stored/visible objects */

XFontStruct *font_big, *font_small;

void (*popup_expose)() = NULL;

int numscales = 12;
int scale = -1;  /* -1 = not one of standard values */
long scales[] = {
  35000, 50000, 75000, 100000, 175000, 
  250000, 375000, 500000, 750000, 1000000, 1500000, 2500000 };

/* variables for distance measuring function */

int dist_flag = 0;   /* 0: no display */
int dist_x;          /* start point in window */
int dist_y;
int dist_lastx;      /* previous end point */
int dist_lasty;
LOCATION dist_a;     /* when we are near an object, take the more precise location */
LOCATION dist_b;
int dist_a_used;
int dist_b_used;
extern OBJECT *saveobj;

double tt;   /* true track */


#define numButtons 10

void button_quit();
void button_info();
void button_zoom_in();
void button_zoom_out();
void button_com();
void button_roads();
void button_airspace();
void button_waypoints();
void button_print();
void button_route();

static	Window button[numButtons];
static	char *buttontext[] = {
	  "Quit",
	  "Info",
	  "Zoom in",
	  "Zoom out",
	  "COM",
	  "Roads",
	  "Airspace",
	  "Waypoints",
	  "Print",
	  "Route" };

static	int buttonwidth[] = { 50, 50, 80, 80, 40, 60, 80, 90, 60, 60 };
static	void (*buttonfunc[])() = {
	  button_quit,
	  button_info,
	  button_zoom_in,
	  button_zoom_out,
	  button_com,
	  button_roads,
	  button_airspace,
	  button_waypoints,
	  button_print,
	  button_route };



void actually_draw (Window mapWin, GC gc, XFontStruct *font_small, 
		    XFontStruct *font_big);




void popup(int x, int y, int width, int height)
{
  XSetWindowAttributes winattr;
  unsigned long valuemask;
  XEvent report;
  XAnyEvent *genreport;

  /* if another popup is still visible, delete it first */

  if (pop)
    {
      XDestroyWindow (display, pop);
      pop = 0;
    }
    

  pop = XCreateSimpleWindow(display, RootWindow(display,screen_num),
			    x, y, width, height, 1,
			    BlackPixel(display, screen_num),
			    WhitePixel(display,screen_num));

  winattr.save_under = True;
  winattr.override_redirect = True;
  valuemask = CWSaveUnder | CWOverrideRedirect;
  XChangeWindowAttributes (display, pop, valuemask, &winattr);

  XSetTransientForHint (display, pop, win);
  
  XSelectInput(display, pop, ExposureMask | KeyPressMask | ButtonPressMask);
  XMapWindow (display, pop);
}


void expose_textbox()
{
  int i, dummy, y;
  XCharStruct info;

  XSetFont(display, popgc, font_small->fid);

  y = 2;

  for (i=0; i<savenumlines; i++)
    {
      XTextExtents (font_small, savelines[i], strlen(savelines[i]), 
		    &dummy, &dummy, &dummy, &info);
      y += info.ascent + info.descent + 6;

      XSetFont(display, popgc, font_small->fid);

      XDrawString (display, pop, popgc, 10, y,
		   savelines[i], strlen(savelines[i]));
    }
}


/* standard text box, make box appear centered below absolute screen coord. x/y */

void popup_textbox (int x, int y, char **lines, int numlines)
{
  int i, dummy, width, height;
  XCharStruct info;

  if (numlines)
    {
      for (i=0; i<numlines; i++)
	strcpy (savelines[i], lines[i]);
      
      savenumlines = numlines;
            
      XSetFont(display, popgc, font_small->fid);
      
      width = height = 0;
      
      for (i=0; i<numlines; i++)
	{
	  XTextExtents (font_small, lines[i], strlen(lines[i]), 
			&dummy, &dummy, &dummy, &info);
	  height += info.ascent + info.descent + 6;
	  if (info.width > width)
	    width = info.width;
	}
      
      height += 12;
      width += 20;
      
      popup_expose = expose_textbox;
      popup (x - width/2, y+3, width, height);
    }
}



void infotext()
{
  strcpy (savelines[0], "     ICAO Map Program    Version 0.11");
  strcpy (savelines[1], "         written by Martin Pauly");
  strcpy (savelines[2], "");
  strcpy (savelines[3], "Geographical and aeronautical information");
  strcpy (savelines[4], "were taken from the German AIP and the");
  strcpy (savelines[5], "ICAO 1:500000 maps.");
  strcpy (savelines[6], "");

  sprintf (savelines[7], "Database consists of %d objects,", db_objects);
  sprintf (savelines[8], "%d of which are visible now.", db_visible);


  popup_textbox (DisplayWidth(display,screen_num)/2,
		 DisplayHeight(display,screen_num)/2-100, savelines, 9);

  
}



void button_info()
{
  infotext();
}


void button_route()
{
  router_radionav();
  router_findroute (route_from, route_to);
}

void button_quit()
{
  XUnloadFont(display, font_small->fid);
  XUnloadFont(display, font_big->fid);

  freecolor(&mapgc);

  XFreeGC(display, gc);
  XFreeGC(display, popgc);
  XFreeGC(display, mapgc);
  XFreeGC(display, gcxor);
  XFreeGC(display, gcplane);
  XCloseDisplay(display);

  exit(1);
}


void button_com()
{
  vis_com = 1 - vis_com;

  findvisibleobjects();
  XClearArea (display, win, 0, 0, 0, 0, True);
  XClearArea (display, mapWin, 0, 0, 0, 0, True);
}


void button_roads()
{
  vis_roads = 1 - vis_roads;

  findvisibleobjects();
  XClearArea (display, win, 0, 0, 0, 0, True);
  XClearArea (display, mapWin, 0, 0, 0, 0, True);
}


void button_airspace()
{
  vis_airspace = 1 - vis_airspace;

  findvisibleobjects();
  XClearArea (display, win, 0, 0, 0, 0, True);
  XClearArea (display, mapWin, 0, 0, 0, 0, True);
}


void button_waypoints()
{
  vis_waypoints = 1 - vis_waypoints;

  findvisibleobjects();
  XClearArea (display, win, 0, 0, 0, 0, True);
  XClearArea (display, mapWin, 0, 0, 0, 0, True);
}


void button_zoom_in()
{
  if (scale > 0)
    {
      scale--;
      map_scale = scales[scale];

      map_initcoord();
      
      /* cause redraw of window */
      XClearArea (display, win, 0, 0, 0, 0, True);
      XClearArea (display, mapWin, 0, 0, 0, 0, True);
    }
}

void button_zoom_out()
{
  if ((scale >= 0) && (scale < numscales-1))
    {
      scale++;
      map_scale = scales[scale]; 

      map_initcoord();
			 
      /* cause redraw of window */
      XClearArea (display, win, 0, 0, 0, 0, True);
      XClearArea (display, mapWin, 0, 0, 0, 0, True);
    }
}



void map (int argc, char **argv)
{
	int x, y, i; 	/* window position */
	unsigned int border_width = 4;	/* four pixels */
	unsigned int icon_width, icon_height;
	char *window_name = "ICAO map";
	char *icon_name = "ICAO map";
	Pixmap icon_pixmap;
	XSizeHints size_hints;
	XIconSize *size_list;
	int count;
	XEvent report;
	char *display_name = NULL;
	int window_size = BIG_ENOUGH;	/* or TOO_SMALL to display contents */
	Cursor pointer;
	XSetWindowAttributes winattr;
	XWindowChanges winchg;
	XCharStruct info;
	int dummy;
	int range;

	progname = argv[0];

	route_from = route_to = NULL;

	for (i=0; i<50; i++)
	  savelines[i] = (char *) malloc(80);

	/* connect to X server */

	if ( (display=XOpenDisplay(display_name)) == NULL )
	{
		(void) fprintf( stderr, "%s: cannot connect to X server %s\n", 
				progname, XDisplayName(display_name));
		exit( -1 );
	}


	/* get screen size from display structure macro */

	screen_num = DefaultScreen(display);
	display_width = DisplayWidth(display, screen_num);
	display_height = DisplayHeight(display, screen_num);


	/* Note that in a real application, x and y would default to 0
	 * but would be settable from the command line or resource database.  
	 */

	x = y = 0;


	/* size window with enough room for text */

	width = display_width*9/10, height = display_height*6/10;


	initcolor (&mapgc);

	/* create opaque window */

	win = XCreateSimpleWindow(display, RootWindow(display,screen_num), 
			x, y, width, height, border_width, BlackPixel(display,
			screen_num), WhitePixel(display,screen_num));

	mapWidth = width-60;     
	mapWidthcm = mapWidth * DisplayWidthMM(display, screen_num) 
                              / DisplayWidth(display, screen_num) / 10;

	mapHeight = height-80;
	mapHeightcm = mapHeight * DisplayHeightMM(display, screen_num) 
	                        / DisplayHeight(display, screen_num) / 10;

	/* now that we have all we need, we can calculate some details
	   of the map to be displayed: */

	map_initcoord();

	mapWin = XCreateSimpleWindow (display, win, 50, 20, width-20, height-30,
				      1, blackpix(), whitepix ());

	/* create buttons */

	x = 50; y = height-35;

	winattr.win_gravity = SouthWestGravity;

	for (i=0; i<numButtons; i++)
	  {
	    button[i] = XCreateSimpleWindow (display, win, x, height-35, 
					     buttonwidth[i], 25, 1, 
					     BlackPixel (display, screen_num),
					     WhitePixel (display, screen_num)); 
	    XChangeWindowAttributes (display, button[i], CWWinGravity, &winattr);
	    x += buttonwidth[i] + 20;
	  }

	/* create crosshair cursor */

	pointer = XCreateFontCursor (display, XC_crosshair);
	XDefineCursor (display, mapWin, pointer);

	pointer = XCreateFontCursor (display, XC_hand2);
	for (i=0; i<numButtons; i++)
	  XDefineCursor (display, button[i], pointer);

	/* Get available icon sizes from Window manager */

	XGetIconSizes(display, RootWindow(display,screen_num),	&size_list, &count);


	/* Create pixmap of depth 1 (bitmap) for icon */

	icon_pixmap = XCreateBitmapFromData(display, win, bitmap_bits, 
			bitmap_width, bitmap_height);


	/* Set size hints for window manager.  The window manager may
	 * override these settings.  Note that in a real
	 * application if size or position were set by the user
	 * the flags would be UPosition and USize, and these would
	 * override the window manager's preferences for this window. */

#ifdef X11R3
	size_hints.flags = PPosition | PSize | PMinSize;
	size_hints.x = x;
	size_hints.y = y;
	size_hints.width = width;
	size_hints.height = height;
	size_hints.min_width = 300;
	size_hints.min_height = 200;
#else /* X11R4 or later */
	/* x, y, width, and height hints are now taken from
	 * the actual settings of the window when mapped. Note
	 * that PPosition and PSize must be specified anyway. */

	size_hints.flags = PPosition | PSize | PMinSize;
	size_hints.min_width = 300;
	size_hints.min_height = 200;
#endif

#ifdef X11R3
	/* set Properties for window manager (always before mapping) */
	XSetStandardProperties(display, win, window_name, icon_name, 
			icon_pixmap, argv, argc, &size_hints);

#else /* X11R4 or later */
	{
	XWMHints wm_hints;
	XClassHint class_hints;

	/* format of the window name and icon name 
	 * arguments has changed in R4 */
	XTextProperty windowName, iconName;

	/* These calls store window_name and icon_name into
	 * XTextProperty structures and set their other 
	 * fields properly. */
	if (XStringListToTextProperty(&window_name, 1, &windowName) == 0) {
		(void) fprintf( stderr, "%s: structure allocation for windowName failed.\n", 
				progname);
		exit(-1);
	}
		
	if (XStringListToTextProperty(&icon_name, 1, &iconName) == 0) {
		(void) fprintf( stderr, "%s: structure allocation for iconName failed.\n", 
				progname);
		exit(-1);
	}

	wm_hints.initial_state = NormalState;
	wm_hints.input = True;
	wm_hints.icon_pixmap = icon_pixmap;
	wm_hints.flags = StateHint | IconPixmapHint | InputHint;

	class_hints.res_name = progname;
	class_hints.res_class = "Basicwin";

	XSetWMProperties(display, win, &windowName, &iconName, 
			argv, argc, &size_hints, &wm_hints, 
			&class_hints);
	}
#endif

	/* Select event types wanted */

	XSelectInput(display, win, ExposureMask | KeyPressMask | PointerMotionMask |
			ButtonPressMask | ButtonReleaseMask | StructureNotifyMask);

	XSelectInput(display, mapWin, ExposureMask | PointerMotionMask |
		      EnterWindowMask | LeaveWindowMask);

	for (i=0; i<numButtons; i++)
	  XSelectInput(display, button[i], ExposureMask |
		       EnterWindowMask | LeaveWindowMask);


	load_font(&font_small, &font_big);
	
	/* create GC for text and drawing */
	getGC(win, &gc, font_big);

	getGC(win, &mapgc, font_small);
	get2ndGC(win, &gcxor);
	get2ndGC(win, &gcplane);
	XSetLineAttributes (display, gcplane, 1, LineSolid, CapButt, JoinRound);
	getGC(win, &popgc, font_small);   /* used for popup windows */


	/* Display window */

	XMapWindow(display, win);
	XMapSubwindows (display, win);
	
	/* get events, use first to display text and graphics */

	width = 0;   /* force first configure notify to realize new (first) size */

	while (1)  {
	  XNextEvent(display, &report);
	  switch  (report.type) {
	     case Expose:
	                 /* unless this is the last contiguous expose,
	                  * don't draw the window */
			 if (report.xexpose.count != 0)
				 break;

			 /* check for popup window or main window */

			 if (report.xexpose.window == pop)
			   {
			     if (popup_expose)
			       (*popup_expose)();
			   }
			 else
			   {
			     printscale(win, gc, font_small, 0, 1);
			     
			     /* check what window has to be redrawn */
			     
			     if (report.xexpose.window == mapWin)
			       actually_draw (mapWin, mapgc, font_small, font_big);
			     
			     /* redraw buttons */
			     
			     for (i=0; i<numButtons; i++)
			       if (report.xexpose.window == button[i]) 
				 {
				   XTextExtents (font_big, buttontext[i],
						 strlen(buttontext[i]), 
						 &dummy, &dummy, &dummy, &info);
				   XSetFont(display, gc, font_big->fid);
				   XDrawString (display, button[i], gc,
						(buttonwidth[i]-info.width)/2, 
						22-info.ascent/2,
						buttontext[i], strlen(buttontext[i]));
				 }
			   }

			 break;
			 
		case EnterNotify:
			 if (report.xcrossing.window == mapWin)
			   insideMap = 1;

			 /* check buttons */

			 for (i=0; i<numButtons; i++)
			   if (report.xcrossing.window == button[i])
			     {
			       winchg.border_width = 3;
			       XConfigureWindow (display, button[i], CWBorderWidth,
						 &winchg);
			     }

			 break;


		case LeaveNotify:
			 if (report.xcrossing.window == mapWin)
			   {
			     insideMap = 0;
			     printCoord (win, gc, font_small, 0, 0);
			     printscale (win, gc, font_small, 0, 1);
			   }

			 /* check buttons */

			 for (i=0; i<numButtons; i++)
			   if (report.xcrossing.window == button[i])
			     {
			       winchg.border_width = 1;
			       XConfigureWindow (display, button[i], CWBorderWidth,
						 &winchg);
			     }
			 break;


		case MotionNotify:
			 printCoord (win, gc, font_small, report.xmotion.x, 
				     report.xmotion.y);

			 if (dist_flag)
			   {
			     char t[100];

			     if (dist_flag == 1)
			       {
				 dist_flag = 2;
			       }
			     else
			       {
				 /* clear old line */
				 
				 if (extendedcolor())
				   {
				     XSetFunction (display, gcplane, GXclear);
				     XDrawLine (display, mapWin, gcplane,
						dist_x, dist_y, dist_lastx, dist_lasty);
				   }
				 else
				   XDrawLine (display, mapWin, gcxor,
					      dist_x, dist_y, dist_lastx, dist_lasty);
			       }
			     
			     /* get new coordinates */

			     dist_lastx = report.xmotion.x - 50;
			     dist_lasty = report.xmotion.y - 20;

			     /* check if this 'snaps' to a close object */

			     dist_b_used = 0;

			     nearobject (dist_lastx, dist_lasty);
			     if (saveobj)
			       {
				 if (saveobj->type/10  != O_CTR/10)   /* no areas */
				   {
				     dist_b = saveobj->location;
				     dist_b_used = 1;
				     internal2window (saveobj->location,
						      &dist_lastx,
						      &dist_lasty);
				     
				     route_to = saveobj;
				   }
			       }
			     else
			       route_to = NULL;


			     /* draw new line */

			     if (extendedcolor())
			       {
				 XSetPlaneMask (display, gcplane, plane_mask (1));
				 XSetFunction (display, gcplane, GXset);
				 XDrawLine (display, mapWin, gcplane,
					    dist_x, dist_y, dist_lastx, dist_lasty);
			       }
			     else
			       XDrawLine (display, mapWin, gcxor,
					  dist_x, dist_y, dist_lastx, dist_lasty);

			     /* calc distance, true track and display */
			     {
			       LOCATION a, b;

			       if (dist_a_used)
				 a = dist_a;
			       else
				 a = window2internal (dist_x, dist_y);

			       if (dist_b_used)
				 b = dist_b;
			       else
				 b = window2internal (dist_lastx, dist_lasty);
			      
			       tt = truetrack (a, b);
			     }

			     sprintf (t, "Distance: %5.1f NM  True Track: %5.1f",
				      dist (dist_lastx, dist_lasty,
					    dist_x, dist_y), tt);

			     printscale (win, gc, font_small, t, 1);
			   }
			 else
			   printscale (win, gc, font_small, nearobject (report.xmotion.x,
								 report.xmotion.y), 0);
			 break;


		case ConfigureNotify:
			 /* window has been resized, change width and
			  * height to send to draw_text and draw_graphics
			  * in next Expose */

			 if ((width != report.xconfigure.width) || 
			     (height != report.xconfigure.height))
			   {
			     width = report.xconfigure.width;
			     height = report.xconfigure.height;
			     
			     if ((width < size_hints.min_width) || 
				 (height < size_hints.min_height))
			       window_size = TOO_SMALL;
			     else
			       window_size = BIG_ENOUGH;
			     
			     mapWidth = width-60;     
			     mapWidthcm = mapWidth * 
			       DisplayWidthMM(display, screen_num) /
				 DisplayWidth(display, screen_num) / 10;
			     
			     mapHeight = height-80;
			     mapHeightcm = mapHeight * 
			       DisplayHeightMM(display, screen_num) /
				 DisplayHeight(display, screen_num) / 10;
			     
			     XResizeWindow (display, mapWin, mapWidth, mapHeight);
			     
			     map_initcoord();
			     
			     /* cause redraw of window */
			     
			     XClearArea (display, mapWin, 0, 0, 0, 0, True);
			   }
			 break;

		case ButtonRelease:
			 /* end display of distance */

			 if (pop)    /* currently displaying popup window? */
			   {
			     XDestroyWindow (display, pop);
			     pop = 0;
			     popup_expose = NULL;
			   }

			 if ((report.xbutton.button == Button2) && (dist_flag))
			   {
			     if (dist_flag == 2)   /* has line been drawn? */
			       {
				 /* tidy up */
				 if (extendedcolor())
				   {
				     XSetFunction (display, gcplane, GXclear);
				     
				     XDrawLine (display, mapWin, gcplane, dist_x, 
						dist_y, dist_lastx, dist_lasty);
				   }
				 else
				   XDrawLine (display, mapWin, gcxor,
					      dist_x, dist_y, dist_lastx, dist_lasty);
			       }

			     dist_flag = 0;
			   }
			 break;


		case ButtonPress:
			 /* check for current pop-up menu, delete if button pressed */

			 if (report.xbutton.window == pop)
			   {
			     XDestroyWindow (display, pop);
			     pop = 0;
			   }

			 /* set new map origin, check buttons etc. */

			 if (report.xbutton.subwindow == mapWin)
			   {
			     if (report.xbutton.button == Button1)
			       {
				 map_origin = window2internal (report.xbutton.x-50,
							       report.xbutton.y-20);
				 map_initcoord();
				 
				 /* cause redraw of window */
				 XClearArea (display, win, 0, 0, 0, 0, True);
				 XClearArea (display, mapWin, 0, 0, 0, 0, True);
			       }

			     if (report.xbutton.button == Button2)
			       {
				 dist_a_used = dist_b_used = 0;
				 dist_flag = 1;  /* start display */
				 dist_lastx = dist_x = report.xbutton.x-50;
				 dist_lasty = dist_y = report.xbutton.y-20;
				 if (saveobj)
				   {
				     if (saveobj->type/10 != O_CTR/10)
				       {
					 route_from = saveobj;   /* save for routing */
					 
					 dist_a = saveobj->location;
					 dist_a_used = 1;
					 internal2window (dist_a,
							  &dist_lastx,
							  &dist_lasty);
					 dist_x = dist_lastx;
					 dist_y = dist_lasty;
				       }
				   }
				 else
				   route_from = NULL;
			       }

			     if (report.xbutton.button == Button3)
			       {
				 objectdescription (savelines, &savenumlines,
						    report.xbutton.x-50,
						    report.xbutton.y-20, &range);

				 /* if object has range data, draw a circle
				    with the respective radius */

				 if (range)
				   {
				     if (extendedcolor())
				       ;
				     else
				       ;
				   }

				 popup_textbox (report.xbutton.x_root,
						report.xbutton.y_root,
						savelines, savenumlines);

				 if (range)
				   {
				     if (extendedcolor())
				       ;
				     else
				       ;
				   }

			       }
			   }		     

			 /* check buttons */

			 for (i=0; i<numButtons; i++)
			   if (report.xbutton.subwindow == button[i])
			     (*buttonfunc[i])();
			 break;

		case KeyPress:

		default:
			/* all events selected by StructureNotifyMask
			 * except ConfigureNotify are thrown away here,
			 * since nothing is done with them */
			break;
		} /* end switch */
	} /* end while */
}




/* draw line, coordinates given in cm, used by coord.c to draw grid */

GC staticgc;
Window staticwindow;


void map_drawline (double xcm1, double ycm1, double xcm2, double ycm2)
{
  int x1, y1, x2, y2;  /* window coordinates */

  /* calculate window coordinates of end points */

  x1 = (int) (xcm1*mapWidth/mapWidthcm+mapWidth/2);
  y1 = (int) -(ycm1*mapHeight/mapHeightcm-mapHeight/2);
  x2 = (int) (xcm2*mapWidth/mapWidthcm+mapWidth/2);
  y2 = (int) -(ycm2*mapHeight/mapHeightcm-mapHeight/2);


  switch (media) {
  case SCREEN:
    XDrawLine (display, staticwindow, staticgc, x1, y1, x2, y2);
    break;
  case POSTSCRIPT:
    fprintf (ps, "newpath\n%d %d moveto\n%d %d lineto\nstroke\n\n",
	     x1, y1, x2, y2);
    break;
  }
}




/* text print routine for coord_drawgrid to print coordinate information */

void grid_printleft (double ycm, char *string)
{
  int dummy, width, height, y;
  XCharStruct info;

  y = (int) -(ycm*mapHeight/mapHeightcm-mapHeight/2);

  /* get text extent first */

  XTextExtents (font_small, string, strlen(string), &dummy, &dummy, &dummy, &info);
  width = info.width;
  height = info.ascent;

  if ((y>10) && (y<mapHeight))
    {
      gp_setcolor (BLACK);
      gp_smalltext (2+width/2, y+7, string);

    }
}


void grid_printunder (double xcm, char *string)
{
  int dummy, width, height, x;
  XCharStruct info;

  x = (int) (xcm*mapWidth/mapWidthcm+mapWidth/2);

  /* get text extent first */

  XTextExtents (font_small, string, strlen(string), &dummy, &dummy, &dummy, &info);
  width = info.width;
  height = info.ascent;

  if ((x>10) && (x<mapWidth)) 
    {
      gp_setcolor (BLACK);
      gp_smalltext (x, mapHeight-4, string);
    }
}


/* draw map window */

void actually_draw (Window mapWin, GC gc, XFontStruct *font_small, 
		    XFontStruct *font_big)
{
  staticgc = gc;       /* save GC for later use */
  staticwindow = mapWin;

  if (!extendedcolor())        /* in simple color mode: */
    drawvisibleobjects(1)   ;  /* do CTRs first because they have filled polygons */

  XSetLineAttributes (display, gc, 0, LineSolid, CapButt, JoinMiter);
  gp_setcolor (LIGHTGRAY);
  coord_drawgrid (1);        /* draw coordinate grid */
  gp_setcolor (BLACK);

  drawvisibleobjects(0);      /* in mapobjects.c */
  
  if (extendedcolor()) 
    drawvisibleobjects(1);    /* do CTRs last in multi-plane color mode */
}



/* print coordinates at top of window */

printCoord(Window win, GC gc, XFontStruct *font, int x, int y)
{
  char temp[50];
  double xcm, ycm;

  if ((insideMap) || (dist_flag))
    {
      /* see where we are measured in cm from the center of window */

      xcm = ((double)x-mapWidth/2)/mapWidth*mapWidthcm;
      ycm = ((double)mapHeight/2-y)/mapHeight*mapHeightcm;

      strcpy (temp, cm2string (xcm, ycm));
    }
  else
    sprintf (temp, "                                              ");

  XSetFont(display, gc, font_big->fid);
  XDrawImageString(display, win, gc, 60, 15,
	      temp, strlen(temp)); 

  XFlush (display);
}


/* print either scale or name of nearest object */
/* to prevent flicker: if forceupdate is false, only write if string has changed */

printscale (Window win, GC gc, XFontStruct *font, char *objname, int forceupdate)
{
  char temp[50], temp1[50];
  static char last[50];

  XSetFont(display, gc, font_big->fid);

  if (!objname)
    sprintf (temp, "Scale 1:%ld", map_scale);
  else
    sprintf (temp, "%s", objname);

  sprintf (temp1, "                                                   ");
  strcpy (temp1 + 45-strlen(temp), temp);   /* make string right justified */

  if (forceupdate || (strcmp (last, temp1)))   /* different? */
    {
      XDrawImageString (display, win, gc, width-450, 15, temp1, strlen(temp1));
      strcpy (last, temp1);
    }
}

getGC(win, gc, font_info)
Window win;
GC *gc;
XFontStruct *font_info;
{
	unsigned long valuemask = 0; /* ignore XGCvalues and use defaults */
	XGCValues values;
	unsigned int line_width = 0;
	int line_style = LineSolid;
	int cap_style = CapButt;
	int join_style = JoinRound;
	int dash_offset = 0;
	static char dash_list[] = {12, 24};
	int list_length = 2;

	/* Create default Graphics Context */
	*gc = XCreateGC(display, win, valuemask, &values);

	/* specify font */
	XSetFont(display, *gc, font_info->fid);

	/* specify black foreground since default window background is 
	 * white and default foreground is undefined. */
	XSetForeground(display, *gc, BlackPixel(display,screen_num));
	XSetBackground(display, *gc, WhitePixel(display,screen_num));

	/* set line attributes */
	XSetLineAttributes(display, *gc, line_width, line_style, 
			cap_style, join_style);

	/* set dashes */
	XSetDashes(display, *gc, dash_offset, dash_list, list_length);
}


get2ndGC(Window win, GC *gc)
{
  unsigned long valuemask = 0; /* ignore XGCvalues and use defaults */
  XGCValues values;
  unsigned int line_width = 1;
  int line_style = LineOnOffDash;
  int cap_style = CapButt;
  int join_style = JoinRound;
  int dash_offset = 0;
  static char dash_list[] = {4, 3};
  int list_length = 2;
  
  /* Create default Graphics Context */
  *gc = XCreateGC(display, win, valuemask, &values);
  
  /* specify black foreground since default window background is 
   * white and default foreground is undefined. */

  XSetForeground(display, *gc, BlackPixel(display,screen_num));
  XSetBackground(display, *gc, WhitePixel(display,screen_num));
  
  /* set line attributes */
  XSetLineAttributes(display, *gc, line_width, line_style, 
		     cap_style, join_style);
  
  /* set dashes */
  XSetDashes(display, *gc, dash_offset, dash_list, list_length);

  /* set xor mode */

  XSetFunction (display, *gc, GXxor);
}


load_font(font_small, font_big)
XFontStruct **font_small, **font_big;
{
	char *font1 = "9x15";
	char *font2 = "6x10";

	/* Load font and get font information structure. */

	if ((*font_big = XLoadQueryFont(display,font1)) == NULL)
	{
		(void) fprintf( stderr, "%s: Cannot open %s font\n", 
				progname, font1);
		exit( -1 );
	}

	if ((*font_small = XLoadQueryFont(display,font2)) == NULL)
	{
		(void) fprintf( stderr, "%s: Cannot open %s font\n", 
				progname, font2);
		exit( -1 );
	}
}




draw_map(Window win, GC gc, XFontStruct* font_info, int win_width, int win_height)
{
	char *string1 = "Hi! I'm a window, who are you?";
	char *string2 = "To terminate program; Press any key";
	char *string3 = "or button while in this window.";
	char *string4 = "Screen Dimensions:";
	int len1, len2, len3, len4;
	int width1, width2, width3;
	char cd_height[50], cd_width[50], cd_depth[50];
	int font_height;
	int initial_y_offset, x_offset;


	/* need length for both XTextWidth and XDrawString */
	len1 = strlen(string1);
	len2 = strlen(string2);
	len3 = strlen(string3);

	/* get string widths for centering */
	width1 = XTextWidth(font_info, string1, len1);
	width2 = XTextWidth(font_info, string2, len2);
	width3 = XTextWidth(font_info, string3, len3);

	font_height = font_info->ascent + font_info->descent;

	/* output text, centered on each line */
	XDrawString(display, win, gc, (win_width - width1)/2, 
			font_height,
			string1, len1);
	XDrawString(display, win, gc, (win_width - width2)/2, 
			(int)(win_height - (2 * font_height)),
			string2, len2);
	XDrawString(display, win, gc, (win_width - width3)/2, 
			(int)(win_height - font_height),
			string3, len3);

	/* copy numbers into string variables */
	(void) sprintf(cd_height, " Height - %d pixels", 
			DisplayHeight(display,screen_num));
	(void) sprintf(cd_width, " Width  - %d pixels", 
			DisplayWidth(display,screen_num));
	(void) sprintf(cd_depth, " Depth  - %d plane(s)", 
			DefaultDepth(display, screen_num));

	/* reuse these for same purpose */
	len4 = strlen(string4);
	len1 = strlen(cd_height);
	len2 = strlen(cd_width);
	len3 = strlen(cd_depth);

	/* To center strings vertically, we place the first string
	 * so that the top of it is two font_heights above the center
	 * of the window.  Since the baseline of the string is what we
	 * need to locate for XDrawString, and the baseline is one
	 * font_info->ascent below the top of the character,
	 * the final offset of the origin up from the center of the 
	 * window is one font_height + one descent. */

	initial_y_offset = win_height/2 - font_height - font_info->descent;
	x_offset = (int) win_width/4;
	XDrawString(display, win, gc, x_offset, (int) initial_y_offset, 
			string4,len4);

	XDrawString(display, win, gc, x_offset, (int) initial_y_offset + 
			font_height,cd_height,len1);
	XDrawString(display, win, gc, x_offset, (int) initial_y_offset + 
			2 * font_height,cd_width,len2);
	XDrawString(display, win, gc, x_offset, (int) initial_y_offset + 
			3 * font_height,cd_depth,len3);
}

draw_graphics(win, gc, window_width, window_height)
Window win;
GC gc;
unsigned int window_width, window_height;
{
	int x, y;
	int width, height;

	height = window_height/2;
	width = 3 * window_width/4;
	x = window_width/2 - width/2;  /* center */
	y = window_height/2 - height/2;
	XDrawRectangle(display, win, gc, x, y, width, height);
}

TooSmall(win, gc, font_info)
Window win;
GC gc;
XFontStruct *font_info;
{
/*
	char *string1 = "Too Small";
	int y_offset, x_offset;

	y_offset = font_info->ascent + 2;
	x_offset = 2;

	XDrawString(display, win, gc, x_offset, y_offset, string1, 
			strlen(string1));
*/
}




