/*

                 I C A O 

                 (c) 1993 Martin Pauly


                 icao.c:   main user interface
*/


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

#include <Xm/Xm.h>
#include <Xm/MainW.h>
#include <Xm/List.h>
#include <Xm/PushB.h>
#include <Xm/Form.h>
#include <Xm/Label.h>
#include <Xm/RowColumn.h>
#include <Xm/TextF.h>
#include <Xm/DrawingA.h>
#include <Xm/DialogS.h>
#include <Xm/ToggleB.h>

/* Drag and Drop only supported by Motif 1.2 */
#if (XmVersion >= 1002, 0)  /* drag and drop currently disabled */
#define DRAGDROP
#include <Xm/DragDrop.h>
#endif

#include <X11/cursorfont.h>
#include "bitmap.h"
#include "objects.h"
#include "mapobjects.h"
#include "popup.h"
#include "colors.h"
#include "graph_primi.h"
#include "router.h"

/* a couple of global variables */

GC gc;
GC gcxor;
GC gcplane;
Cursor pointer, waitpointer;

#ifdef DRAGDROP
Widget ddc;    /* drag context widget */
#endif


/* variables for distance measuring function */

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


Widget  toplevel, drawingarea, scalewidget, coordwidget, objectname;
Widget  l_to, l_via1, l_via2, l_via3, l_dest, l_title;
Widget  r_to, r_via1, r_via2, r_via3, r_dest, objectpanel;
Widget  scaleshell, visobjshell, projshell;
Display *display;
int     screen_num;
Window  pop;
char    *savelines[20];        /* current text lines for text popup window */
char    new_projection;
int     savenumlines;
FILE    *input;
extern  FILE   *yyin;
extern  OBJECT *saveobj;
long newscale;  /* set by buttons in scale menu */
Atom   ddcTarget;
OBJECT *selectedobject;   /* currently selected object */
Pixmap icon_pixmap;
String fallbacks[] = {
  "Icao.width: 620",
  "Icao.height: 460",
  "Icao*left*fontList: 6x10",
  "Icao*route1*fontList: 7x13",
  "Icao*routef*bottomOffset: -90",
  "Icao*routef*fontList: 10x15",
  NULL
};


/* current route description */

#define FROM  0
#define VIA1  1
#define VIA2  2
#define VIA3  3
#define DEST  4

OBJECT *currentroute[] = {NULL, NULL, NULL, NULL, NULL};
int selectedroutebutton = -1;   /* currently selected button for route spec, -1 = none */


/* current route to be drawn */

int drawroutenumpoints = 0;
LOCATION drawroutepoints[100];


/* external references */

extern int tableentries;   /* number of strings in browser */
extern char **namearray;   /* object names for browser */
extern int mapWidth, mapHeight;
extern double mapWidthcm, mapHeightcm;
extern XFontStruct *font_big, *font_small;
extern int numscales;
extern long scales[];
extern long map_scale;      /* from coord.c */
extern LOCATION map_origin;
extern int vis_com;         /* communications */
extern int vis_roads;       /* roads */
extern int vis_airspace;    /* airspace structure */
extern int vis_cities;      /* cities */
extern int vis_waypoints;   /* waypoints */
extern int vis_airfield;    /* smaller airfields */
extern int vis_water;       /* rivers, lakes */
extern int vis_villages;    /* villages */
extern int numvis;
extern char *visstring[];
extern int visobj[];
extern int db_objects;


/* prototypes */

void scale_dialog();
void visobj_dialog();
void proj_dialog();
void drawroute();
void undrawroute();
void drawcurrentroute();

static Widget labelofbutton (int index);
static Widget widgetofbutton (int index);



/* callback for bottom left text widget (input focus) */

void textfocusCB (Widget w, XtPointer client_data, XmAnyCallbackStruct *cbs)
{
  XtVaSetValues (objectname,
		 XmNvalue, "");
}


/* callback for changed text in objectname */

void textchangedCB (Widget w, XtPointer client_data, XmAnyCallbackStruct *cbs)
{
  char *string;
  int i, found, pos, len;

  /* copy contents of text field into temp string */
  
  XtVaGetValues (objectname,
		 XmNvalue, &string,
		 NULL);
  
  /* search string in object browser */
  
  found = 0;
  i = 0;
  len = strlen (string);
  
  while ((!found) && (i < tableentries))
    {
      if (!strncasecmp (namearray[i], string, len))
	{
	  found = 1;
	  pos = i;
	}
      i++;
    }
  
  if (found && len)
    {
      /* client data is 1 if return has been pressed */
      
/*      XmListSelectPos (objectpanel, pos+1, False);  */
      XmListSelectPos (objectpanel, pos+1, (client_data) ? True : False); 
      XmListSetPos (objectpanel, pos+1);
    }
}



/*
  routine to draw lines that can be removed 
  this works either with xor graphics operations or
  with the extended color system (using one plane only)
*/


void undrawline (int x1, int y1, int x2, int y2)
{
  if (extendedcolor())
    {
      XSetFunction (display, gcplane, GXclear);
      XDrawLine (display, XtWindow (drawingarea), gcplane,
		 x1, y1, x2, y2);
    }
  else
    XDrawLine (display, XtWindow (drawingarea), gcxor,
	       x1, y1, x2, y2);
}


void drawline (int x1, int y1, int x2, int y2)
{
  if (extendedcolor())
    {
      XSetPlaneMask (display, gcplane, plane_mask (1));
      XSetFunction (display, gcplane, GXset);
      XDrawLine (display, XtWindow (drawingarea), gcplane,
		 x1, y1, x2, y2);
    }
  else
    XDrawLine (display, XtWindow (drawingarea), gcxor,
	       x1, y1, x2, y2);
}




/* callback for file menu */

void fileCB (Widget w, int item_no)
{
  int line;


  switch (item_no) {
  case 0:  /* load world file */
    break;


  case 1:  /* Info */
    line = 0;
    strcpy (popupstrings[line++], "");
    strcpy (popupstrings[line++], "     ICAO Map - An Aviation Utility");
    strcpy (popupstrings[line++], "     ------------------------------");
    strcpy (popupstrings[line++], "          \251 1993 Martin Pauly");
    strcpy (popupstrings[line++], "");
    strcpy (popupstrings[line++], "              Version 0.6b");
    strcpy (popupstrings[line++], "");
    sprintf (popupstrings[line++], "  Currently %d objects in database. ", db_objects);
    strcpy (popupstrings[line++], "");
    strcpy (popupstrings[line++], "   Feel free to contact the author:");
    strcpy (popupstrings[line++], " pauly@pool.informatik.rwth-aachen.de ");
    strcpy (popupstrings[line++], "");
    popupbox (line, "ICAO Map Info");
    break;

  case 2:   /* Quit */
    freecolor (&gc);

    XUnloadFont(display, font_small->fid);
    XUnloadFont(display, font_big->fid);
    
    XFreeGC(display, gcxor);
    XFreeGC(display, gcplane);

    exit (0);
    break;
  }
}


/* callback for soaring menu */

void soaringCB (Widget w, int item_no)
{
  switch (item_no) {
  case 0:   /* suggest triangle */
    triangle();
    break;
  }
}


/* test menu, will be off release */

void testCB (Widget w, int item_no)
{
  drawcurrentroute();
}


/* routine that draws current route into map */

void drawcurrentroute()
{
  /* delete old route if there is one visible */

  if (drawroutenumpoints)
    undrawroute();

  drawroutenumpoints = 0;

  if (currentroute[FROM])
    {
      drawroutepoints[drawroutenumpoints++] = currentroute[FROM]->location;
      if (currentroute[VIA1])
	drawroutepoints[drawroutenumpoints++] = currentroute[VIA1]->location;
      if (currentroute[VIA2])
	drawroutepoints[drawroutenumpoints++] = currentroute[VIA2]->location;
      if (currentroute[VIA3])
	drawroutepoints[drawroutenumpoints++] = currentroute[VIA3]->location;
      if (currentroute[DEST])
	drawroutepoints[drawroutenumpoints++] = currentroute[DEST]->location;

      drawroute();
    }
}


/* routine that draws any route (from drawroutepoints) */

void drawroute()
{
  int i, x1, y1, x2, y2;

  if (drawroutenumpoints > 1)   /* a single point doesn't make a route */
    {
      gp_setcolor (BLACK);

      internal2window (drawroutepoints[0], &x1, &y1);

      for (i=1; i<drawroutenumpoints; i++)
	{
	  internal2window (drawroutepoints[i], &x2, &y2);

	  if (extendedcolor())   /* extended color -> draw using bit planes */
	    drawline (x1, y1, x2, y2);         /* which makes deleting lines simler*/
	  else    
	    XDrawLine (display, XtWindow (drawingarea), gc, x1, y1, x2, y2);
	    
	  x1 = x2;   y1 = y2;
	}
    }
}


/* routine to undraw an existing route */

void undrawroute()
{
  drawroutenumpoints = 0;

  if (extendedcolor())   /* simply clear bitplane */
    {
      XSetPlaneMask (display, gcplane, plane_mask (1));
      XSetFunction (display, gcplane, GXclear);
      XSetFillStyle (display, gcplane, FillSolid);
      XFillRectangle (display, XtWindow (drawingarea), gcplane,
		      0, 0, mapWidth, mapHeight);
    }
  else
    XClearArea (display, XtWindow (drawingarea), 0, 0, 0, 0, True);  /* redraw */
}




/* callback for route menu */

void routemenuCB (Widget w, int item_no)
{
  switch (item_no) {
  case 0:   /* autorouter */
    router_radionav();
    router_findroute (currentroute[FROM], currentroute[VIA1],
		      currentroute[VIA2], currentroute[VIA3],
		      currentroute[DEST]);
    break;

  case 1:   /* draw route */
    drawcurrentroute();
    break;

  case 2:   /* undraw route */
    undrawroute();
    break;
  }
}


/* callback for map menu */

void mapCB (Widget w, int item_no)
{
  switch (item_no) {
  case 0:   /* scales */
    scale_dialog();
    break;
  case 1:   /* visible objects */
    visobj_dialog();
    break;
  case 2:   /* projection type */
    proj_dialog();
    break;
  case 3:   /* print map */
    print_dialog();
    break;
  }
}



/* callback for object browser */


void objectpaneldefaultCB (Widget w, XtPointer client_data, XmListCallbackStruct *cbs)
{
  OBJECT *object;
  int  pos, i;

  /* double click, show object in map */

  pos = cbs->item_position;
  object = objectfromnamelist (pos-1);

  if (object)
    {
      map_origin = object->location;
      map_initcoord();
      XClearArea (display, XtWindow (drawingarea), 0, 0, 0, 0, True);  /* redraw */
    }
  
}


void objectpanelsimpleCB (Widget w, XtPointer client_data, XmListCallbackStruct *cbs)
{
  OBJECT *object;
  int  pos, i;
  String tempstr;
  static char line[128];

  /* single click, copy object name into text widget at the bottom */

  pos = cbs->item_position;
  object = objectfromnamelist (pos-1);

  strcpy (line, " ");  /* at least one blank for empty field to maintain height */

  if (object)
    {
      if (object->type == O_WAYPOINT)
	{
	  if (object->alias)
	    sprintf (line, "%s %s", object->alias, object->name);
	  else
	    sprintf (line, "%s", object->name);
	}
      else
	sprintf (line, "%s (%s)", object->name, objecttypestring(object->type));
      
      for (i=0; i<strlen (line); i++)
	if (line[i] == '_')
	  line[i] = ' ';
    }

  selectedobject = object;


#ifndef DRAGDROP

  /* test if a route button is active. if so, note new waypoint */

  if (selectedroutebutton != -1)
    {
      line[23] = 0;     /* see similar statement in service of Button down event */
     
      /* store object details */
      
      currentroute[selectedroutebutton] = selectedobject;
      
      tempstr = XmStringCreateSimple (line);  
      XtVaSetValues (widgetofbutton (selectedroutebutton),
		     XmNlabelString,   tempstr,
		     NULL);  
      XmStringFree (tempstr);  
      
      /* deselect button */
      
      XtVaSetValues (labelofbutton (selectedroutebutton),
		     XmNborderWidth,   0,
		     NULL);
      
      selectedroutebutton = -1;
      
      /* in extended color mode, updating the route is so fast
	 that we can do it right away: */
      
      /* only if user wanted to see route anyway! */
      if (extendedcolor() && (drawroutenumpoints))   
	drawcurrentroute();
      
    }
  
#endif
}


/* change label of a route button. this is used when from the suggest
   triangle window the user selects a route by a double click. */

void update_route_button (int button, OBJECT *object)
{
  char line[128] = " ";
  XmString tempstr;

  if (object)
    {
      if (object->type == O_WAYPOINT)
	{
	  if (object->alias)
	    sprintf (line, "%s %s", object->alias, object->name);
	  else
	    sprintf (line, "%s", object->name);
	}
      else
	sprintf (line, "%s (%s)", object->name, objecttypestring(object->type));
    }
  
  line[23] = 0;     /* see similar statement in service of Button down event */
  
  /* store object details */
  
  currentroute[selectedroutebutton] = selectedobject;
  
  tempstr = XmStringCreateSimple (line);  
  XtVaSetValues (widgetofbutton (button),
		 XmNlabelString,   tempstr,
		 NULL);  
  XmStringFree (tempstr);
}




/* let user choose a scale */

void scaleapplyCB (Widget w, XtPointer client_data, XmPushButtonCallbackStruct *cbs);
void scaleselectCB (Widget w, XtPointer client_data, XmToggleButtonCallbackStruct *cbs);

void scale_dialog()
{
  Widget rowcol, form, label, apply, cancel, toggle;
  int    i;
  char   string[30];
  Arg    args[10];
  String tempstr;

  scaleshell = XtVaCreateManagedWidget ("scaledialog", xmDialogShellWidgetClass, toplevel,
					XmNtitle,             "ICAO Map Scale",
					XmNmappedWhenManaged, False,
					XmNallowShellResize,  True,
					XmNdeleteResponse,    XmDESTROY,
					NULL);
 
  form = XtVaCreateManagedWidget ("scaleform", xmFormWidgetClass, scaleshell, 
				  NULL);

  tempstr = XmStringCreateSimple ("Select a new scale:");
  label = XtVaCreateManagedWidget ("scalelabel", xmLabelWidgetClass, form,
				   XmNlabelString,      tempstr,
				   XmNtopAttachment,    XmATTACH_FORM,
				   XmNleftAttachment,   XmATTACH_FORM,
				   XmNrightAttachment,  XmATTACH_FORM,
				   XmNalignment,        XmALIGNMENT_CENTER, 
				   NULL);
  XmStringFree (tempstr);

  rowcol = XtVaCreateManagedWidget ("scalemanager", xmRowColumnWidgetClass, form,
				    XmNradioBehavior,    True,
				    XmNorientation,      XmVERTICAL,
				    XmNnumColumns,       2,
				    XmNentryAlignment,   XmALIGNMENT_END,
				    XmNtopAttachment,    XmATTACH_WIDGET,
				    XmNtopWidget,        label,
				    XmNleftAttachment,   XmATTACH_FORM,
				    XmNleftOffset,       10,
				    XmNrightAttachment,  XmATTACH_FORM,
				    XmNrightOffset,      10,
				    NULL);

  newscale = map_scale;
  
  for (i=0; i<numscales; i++)
    {
      sprintf (string, "1:%ld", scales[i]);
      tempstr = XmStringCreateSimple (string);

      if (map_scale != scales[i])
	toggle = XtVaCreateManagedWidget ("scaleswitch", xmToggleButtonWidgetClass, 
					  rowcol,
					  XmNlabelString, tempstr,
					  NULL);
      else
	toggle = XtVaCreateManagedWidget ("scaleswitch", xmToggleButtonWidgetClass, 
					  rowcol,
					  XmNlabelString, tempstr,
					  XmNset,         True,
					  NULL);
      
      XmStringFree (tempstr);
      XtAddCallback (toggle, XmNvalueChangedCallback, (XtCallbackProc) scaleselectCB,
		     (XtPointer) scales[i]);
    }

  tempstr = XmStringCreateSimple ("Apply");
  apply = XtVaCreateManagedWidget ("apply", xmPushButtonWidgetClass, form,
				   XmNlabelString,      tempstr,
				   XmNtopAttachment,    XmATTACH_WIDGET,
				   XmNtopWidget,        rowcol,
				   XmNleftAttachment,   XmATTACH_FORM,
				   XmNleftOffset,       10,
				   XmNbottomAttachment, XmATTACH_FORM,
				   XmNbottomOffset,     10,
				   NULL);
  XmStringFree (tempstr);
  XtAddCallback (apply, XmNactivateCallback, (XtCallbackProc) scaleapplyCB, 
		 (XtPointer) 1L);

  tempstr = XmStringCreateSimple ("Quit");
  cancel = XtVaCreateManagedWidget ("cancel", xmPushButtonWidgetClass, form,
				    XmNlabelString,      tempstr,
				    XmNtopAttachment,    XmATTACH_WIDGET,
				    XmNtopWidget,        rowcol,
				    XmNleftAttachment,   XmATTACH_WIDGET,
				    XmNleftWidget,       apply,
				    XmNleftOffset,       10,
				    NULL);
  XmStringFree (tempstr);
  XtAddCallback (cancel, XmNactivateCallback, (XtCallbackProc) scaleapplyCB, NULL);

  XtMapWidget (scaleshell);
}


/* react to apply and cancel button */

void scaleapplyCB (Widget w, XtPointer client_data, XmPushButtonCallbackStruct *cbs)
{
  if (client_data)
    {
      map_scale = newscale;
      map_initcoord();
      XClearArea (display, XtWindow (drawingarea), 0, 0, 0, 0, True);  /* redraw */
    }

  XtUnmapWidget (scaleshell);
}


/* one of the scale radio buttons has been pressed */

void scaleselectCB (Widget w, XtPointer client_data, XmToggleButtonCallbackStruct *cbs)
{
  if (cbs->set == True)
    newscale = (long) client_data;  
}





/* let user choose visibleobjects */

void visapplyCB (Widget w, XtPointer client_data, XmPushButtonCallbackStruct *cbs);
void visselectCB (Widget w, XtPointer client_date, XmToggleButtonCallbackStruct *cbs);

void visobj_dialog()
{
  Widget rowcol, form, label, apply, cancel, toggle;
  int    i;
  char   string[30];
  Arg    args[10];
  String tempstr;

  visobjshell = XtVaCreateManagedWidget ("visibledialog", xmDialogShellWidgetClass, toplevel,
					 XmNtitle,             "ICAO Visible Objects",
					 XmNdeleteResponse,    XmDESTROY,
					 XmNallowShellResize,  True,
					 XmNmappedWhenManaged, False,
					 NULL);
 
  form = XtVaCreateManagedWidget ("visibleform", xmFormWidgetClass, visobjshell, 
				  NULL);

  tempstr = XmStringCreateSimple ("Choose objects to be displayed:");
  label = XtVaCreateManagedWidget ("visiblelabel", xmLabelWidgetClass, form,
				   XmNlabelString,      tempstr,
				   XmNtopAttachment,    XmATTACH_FORM,
				   XmNleftAttachment,   XmATTACH_FORM,
				   XmNrightAttachment,  XmATTACH_FORM,
				   XmNalignment,        XmALIGNMENT_CENTER, 
				   NULL);
  XmStringFree (tempstr);  

  rowcol = XtVaCreateManagedWidget ("visiblemanager", xmRowColumnWidgetClass, form,
				    XmNpacking,          XmPACK_COLUMN,
				    XmNorientation,      XmVERTICAL,
				    XmNnumColumns,       3,
				    XmNtopAttachment,    XmATTACH_WIDGET,
				    XmNtopWidget,        label,
				    XmNleftAttachment,   XmATTACH_FORM,
				    XmNleftOffset,       10,
				    NULL);

  for (i=0; i<numvis; i++)
    {
      tempstr = XmStringCreateSimple (visstring[i]);

      if (!visobj[i])
	toggle = XtVaCreateManagedWidget ("visibleswitch", xmToggleButtonWidgetClass, 
					  rowcol,
					  XmNlabelString, tempstr,
					  NULL);
      else
	toggle = XtVaCreateManagedWidget ("visibleswitch", xmToggleButtonWidgetClass, 
					  rowcol,
					  XmNlabelString, tempstr,
					  XmNset,         True,
					  NULL);
      
      XmStringFree (tempstr);
      XtAddCallback (toggle, XmNvalueChangedCallback, (XtCallbackProc) visselectCB,
		     (XtPointer) i);
    }

  tempstr = XmStringCreateSimple ("Apply");
  apply = XtVaCreateManagedWidget ("apply", xmPushButtonWidgetClass, form,
				   XmNlabelString,      tempstr,
				   XmNtopAttachment,    XmATTACH_WIDGET,
				   XmNtopWidget,        rowcol,
				   XmNtopOffset,        10,
				   XmNleftAttachment,   XmATTACH_FORM,
				   XmNleftOffset,       10,
				   XmNbottomAttachment, XmATTACH_FORM,
				   XmNbottomOffset,     10,
				   NULL);
  XmStringFree (tempstr);
  XtAddCallback (apply, XmNactivateCallback, (XtCallbackProc) visapplyCB, 
		 (XtPointer) 1L);

  XtMapWidget (visobjshell);
}


/* react to apply button */

void visapplyCB (Widget w, XtPointer client_data, XmPushButtonCallbackStruct *cbs)
{
  XtUnmapWidget (visobjshell);

  map_initcoord();  
  XClearArea (display, XtWindow (drawingarea), 0, 0, 0, 0, True);  /* redraw */
}


/* one of the toggle buttons has been pressed */

void visselectCB (Widget w, XtPointer client_data, XmToggleButtonCallbackStruct *cbs)
{
  if (cbs->set == True)
    visobj[(int) client_data] = 1;
  else
    visobj[(int) client_data] = 0;
}









/* input projection details */

void projapplyCB (Widget w, XtPointer client_data, XmPushButtonCallbackStruct *cbs);
void projselectCB (Widget w, XtPointer client_date, XmToggleButtonCallbackStruct *cbs);

void proj_dialog()
{
  Widget rowcol, form, label, apply, cancel, togglelambert, togglemercator;
  Widget v1l, v1, v2l, v2, zl, z, acl, ac;
  int    i;
  char   string[30];
  Arg    args[10];
  String tempstr;

  new_projection = map_projection;   /* default: nothing changed */

  projshell = XtVaCreateManagedWidget ("projdialog", xmDialogShellWidgetClass, toplevel,
				       XmNtitle,             "ICAO Projection Details",
				       XmNdeleteResponse,    XmDESTROY,
				       XmNallowShellResize,  True,
				       XmNmappedWhenManaged, False,
				       NULL);
 
  form = XtVaCreateManagedWidget ("projform", xmFormWidgetClass, projshell, 
				  NULL);

  tempstr = XmStringCreateSimple ("  Choose a projection and enter details:  ");
  label = XtVaCreateManagedWidget ("projlabel", xmLabelWidgetClass, form,
				   XmNlabelString,      tempstr,
				   XmNtopAttachment,    XmATTACH_FORM,
				   XmNleftAttachment,   XmATTACH_FORM,
				   XmNrightAttachment,  XmATTACH_FORM,
				   XmNalignment,        XmALIGNMENT_CENTER, 
				   NULL);
  XmStringFree (tempstr);  


  rowcol = XtVaCreateManagedWidget ("projmanager", xmRowColumnWidgetClass, form,
				    XmNradioBehavior,    True,
				    XmNorientation,      XmVERTICAL,
				    XmNnumColumns,       2,
				    XmNtopAttachment,    XmATTACH_WIDGET,
				    XmNtopWidget,        label,
				    XmNleftAttachment,   XmATTACH_FORM,
				    XmNleftOffset,       10,
				    NULL);


  tempstr = XmStringCreateSimple ("Lambert");
  togglelambert = XtVaCreateManagedWidget ("projlambert", xmToggleButtonWidgetClass, 
					   rowcol,
					   XmNlabelString, tempstr,
					   XmNset,         (map_projection == PROJECT_LAMBERT),
					   NULL);
  XtAddCallback (togglelambert, XmNvalueChangedCallback, (XtCallbackProc) projselectCB,
		 (XtPointer) PROJECT_LAMBERT);
  XmStringFree (tempstr);

  tempstr = XmStringCreateSimple ("Mercator");
  togglemercator = XtVaCreateManagedWidget ("projmercator", xmToggleButtonWidgetClass, 
					    rowcol,
					    XmNlabelString, tempstr,
					    XmNset,         (map_projection == PROJECT_MERCATOR),
					    NULL);
  XtAddCallback (togglemercator, XmNvalueChangedCallback,
		 (XtCallbackProc) projselectCB, (XtPointer) PROJECT_MERCATOR);
  XmStringFree (tempstr);
  
  


  tempstr = XmStringCreateSimple ("Apply");
  apply = XtVaCreateManagedWidget ("apply", xmPushButtonWidgetClass, form,
				   XmNlabelString,      tempstr,
				   XmNtopAttachment,    XmATTACH_WIDGET,
				   XmNtopWidget,        rowcol,
				   XmNtopOffset,        10,
				   XmNleftAttachment,   XmATTACH_FORM,
				   XmNleftOffset,       10,
				   XmNbottomAttachment, XmATTACH_FORM,
				   XmNbottomOffset,     10,
				   NULL);
  XmStringFree (tempstr);
  XtAddCallback (apply, XmNactivateCallback, (XtCallbackProc) projapplyCB, 
		 (XtPointer) 1L);

  tempstr = XmStringCreateSimple ("Quit");
  cancel = XtVaCreateManagedWidget ("cancel", xmPushButtonWidgetClass, form,
				    XmNlabelString,      tempstr,
				    XmNtopAttachment,    XmATTACH_WIDGET,
				    XmNtopWidget,        rowcol,
				    XmNtopOffset,        10,
				    XmNleftAttachment,   XmATTACH_WIDGET,
				    XmNleftWidget,       apply,
				    XmNleftOffset,       10,
				    XmNbottomAttachment, XmATTACH_FORM,
				    XmNbottomOffset,     10,
				    NULL);
  XmStringFree (tempstr);
  XtAddCallback (cancel, XmNactivateCallback, (XtCallbackProc) projapplyCB, NULL);

  XtMapWidget (projshell);
}


/* react to apply button */

void projapplyCB (Widget w, XtPointer client_data, XmPushButtonCallbackStruct *cbs)
{
  XtUnmapWidget (projshell);

  if (client_data)
    {
      map_projection = new_projection;
      map_initcoord();  
      XClearArea (display, XtWindow (drawingarea), 0, 0, 0, 0, True);  /* redraw */
    }
}


/* one of the radio buttons has been pressed */

void projselectCB (Widget w, XtPointer client_data, XmToggleButtonCallbackStruct *cbs)
{
  new_projection = (char) client_data;  /* may cause compiler warning, but is OK */
}









/* callbacks for drawing area */

void drawingarea_expose_callback (Widget w, XtPointer data, XmDrawingAreaCallbackStruct *cbs)
{
  Window win;

  if (!(cbs->event->xexpose.count))
    if (win = XtWindow (drawingarea))
      {
	XClearWindow (display, win);
	actually_draw (win, gc, font_small, font_big);
	drawroute();   /* draw selected route if desired */
      }
}


void drawingarea_resize_callback (Widget w, XtPointer data, XmDrawingAreaCallbackStruct *cbs)
{
  XWindowAttributes winattr;
  Window win;
  int smaller;

  /* get new size of drawing area and repaint */

  if (cbs->reason == XmCR_RESIZE)
    if (win = XtWindow (drawingarea))
      {
	XGetWindowAttributes (XtDisplay(drawingarea), win, &winattr);
	
	smaller = ((winattr.width < mapWidth) && (winattr.height < mapHeight));

	mapWidth  = winattr.width;
	mapHeight = winattr.height;

	mapWidthcm = mapWidth * DisplayWidthMM(display, screen_num) 
	  / DisplayWidth(display, screen_num) / 10;
	
	mapHeightcm = mapHeight * DisplayHeightMM(display, screen_num) 
	  / DisplayHeight(display, screen_num) / 10;

	map_initcoord();

	/* if map got smaller, force expose to redraw */

	if (smaller)
	  XClearArea (display, XtWindow (drawingarea), 0, 0, 0, 0, True);
      }
}


#ifndef DRAGDROP

void route_release_callback (Widget w, XtPointer data, XEvent *event, Boolean *c)
{
  if (event->xbutton.button == Button2)    
    puts ("released in to");
}

#endif

void drawingarea_leave_callback (Widget w, XtPointer data, XEvent *event, Boolean *c)
{
  String tempstr;
  
  tempstr = XmStringCreateSimple("");
  XtVaSetValues (coordwidget,
		 XmNlabelString, tempstr,
		 NULL);
  XtVaSetValues (scalewidget,
		 XmNlabelString, tempstr,
		 NULL);
  XmStringFree (tempstr);
}


#ifdef DRAGDROP
void ddcCB (Widget w, XtPointer client_data, char *anything)
{
  if (client_data)
    puts (client_data);
}

void ddcDropProc (Widget w, XtPointer client_data, XmDropProcCallbackStruct *cbs)
{
  Arg    args[20];
  int    i;

  XtVaSetValues (w, XmNlabelString, XmStringCreateSimple("dropped"), NULL);
}


void ddcEnterCB (Widget w, XtPointer client_data, XmDropSiteEnterCallbackStruct *cbs)
{
  if (w == ddc)
    fprintf (stderr, "Enter correct\n");
  else
    fprintf (stderr, "Enter wrong\n");
}

void ddcLeaveCB (Widget w, XtPointer client_data, XmDropSiteLeaveCallbackStruct *cbs)
{
  fprintf (stderr, "Leave\n");
}

#endif


void drawingarea_input_callback (Widget w, XtPointer data, XmDrawingAreaCallbackStruct *cbs)
{
  int    x, y, type, range;
  Arg    args[20];
  char   temp[128];
  char   *tmpptr;
  double xcm, ycm;
  String tempstr;
  int    i, dummy, width, height;

  XCharStruct          info;
  XEvent               report;
  static char          lastobject[128];
  XSetWindowAttributes winattr;
  unsigned long        valuemask;


  type = cbs->event->xany.type;



  if (type == ButtonPress)
    {
      x = cbs->event->xbutton.x;
      y = cbs->event->xbutton.y;

      if (cbs->event->xbutton.button == Button1)   /* note differences in DragDrop mode */
	{
#ifdef DRAGDROP	  
	  map_origin = window2internal (x, y);
	  map_initcoord();
	  
	  XClearArea (display, XtWindow (drawingarea), 0, 0, 0, 0, True);  /* redraw */      
#else
	  tmpptr = nearobject (x, y);     /* found object stored in *saveobj */
	  if ((selectedroutebutton == -1) || (!tmpptr))
	    {
	    /* no route button selected -> like DragDrop */
	      map_origin = window2internal (x, y);
	      map_initcoord();
	      
	      XClearArea (display, XtWindow (drawingarea), 0, 0, 0, 0, True);  /* redraw */
	    }
	  else     /* if a button is selected, take coordinates of object and deselect */
	    if ((saveobj->type / 10) != (O_CTR / 10))   /* no airspace regions */
	      {
		/* copy string into pushbutton */
		
		/* a bug in Motif? the push button label mustn't be longer than the button,
		   otherwise some "strange" behaviour will be noticed (23 blanks do work, however) */
		
		tmpptr[23] = 0;    /* limit string to 23 characters (see above) */
		
		tempstr = XmStringCreateSimple (tmpptr);  
		XtVaSetValues (widgetofbutton (selectedroutebutton),
			       XmNlabelString,   tempstr,
			       NULL);  
		XmStringFree (tempstr);  
		
		/* store object details */
		
		currentroute[selectedroutebutton] = saveobj;
		
		/* deselect button */
		
		XtVaSetValues (labelofbutton (selectedroutebutton),
			       XmNborderWidth,   0,
			       NULL);
		
		selectedroutebutton = -1;
		
		/* in extended color mode, updating the route is so fast
		   that we can do it right away: */
		
		/* only if user wanted to see route anyway! */
		if (extendedcolor() && (drawroutenumpoints))   
		  drawcurrentroute();
	      }
#endif
	}
      
#ifdef DRAGDROP	
      if (cbs->event->xbutton.button == Button2)     /* DragDrop */
	{
	  if (tmpptr = nearobject (x, y))   /* found object stored in *saveobj */
	    {
	      i = 0;
	      XtSetArg (args[i], XmNexportTargets,    ddcTarget);     i++;
	      XtSetArg (args[i], XmNnumExportTargets, 1);             i++;
 	      XtSetArg (args[i], XmNdragOperations, XmDROP_COPY);     i++;
	      ddc = XmDragStart (drawingarea, cbs->event, args, 0);

	      XtAddCallback (ddc, XmNdropSiteEnterCallback, (XtCallbackProc) ddcEnterCB, "enter drop");
	      XtAddCallback (ddc, XmNdropSiteLeaveCallback, (XtCallbackProc) ddcLeaveCB, "leave drop");
	    }
	}
#else
      if (cbs->event->xbutton.button == Button2)     /* no DragDrop */
	{
	}
#endif

      /* right mouse button: display small info box for object */
      /* if shifted: init rubber line for distance measuring */

      if (cbs->event->xbutton.button == Button3)
	if (cbs->event->xbutton.state & ShiftMask)
	  {
	    /* distance measuring and track display */
	    
	    dist_a_used = dist_b_used = 0;
	    dist_flag = 1;  /* start display */
	    dist_lastx = dist_x = cbs->event->xbutton.x;
	    dist_lasty = dist_y = cbs->event->xbutton.y;

	    if (saveobj)
	      {
		if (saveobj->type/10 != O_CTR/10)
		  {
		    dist_a = saveobj->location;
		    dist_a_used = 1;
		    internal2window (dist_a,
				     &dist_lastx,
				     &dist_lasty);
		    dist_x = dist_lastx;
		    dist_y = dist_lasty;
		  }
	      }
	  }
	else     /* shift not held? display info box then */
	  {
	    if (pop)    /* delete existing popup window */
	      {
		XDestroyWindow (display, pop);
		pop = 0;
	      }
	    
	    objectdescription (savelines, &savenumlines,
			       x, y, &range);
	    
	    x = cbs->event->xbutton.x_root;
	    y = cbs->event->xbutton.y_root;
	    
	    if (savenumlines)
	      {
		
		XSetFont(display, gc, font_small->fid);
		
		width = height = 0;
		
		for (i=0; i<savenumlines; i++)
		  {
		    XTextExtents (font_small, savelines[i], strlen(savelines[i]), 
				  &dummy, &dummy, &dummy, &info);
		    height += info.ascent + info.descent + 6;
		    if (info.width > width)
		      width = info.width;
		  }
		
		height += 12;
		width += 20;
		
		pop = XCreateSimpleWindow(display, RootWindow(display,screen_num),
					  x-width/2, y+10, 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, XtWindow (toplevel));
		XSelectInput(display, pop, ExposureMask);
		XMapWindow (display, pop);
		
		/* write actual information */
		
		y = 2;
		
		gp_setcolor (BLACK);
		for (i=0; i<savenumlines; i++)
		  {
		    y += info.ascent + info.descent + 6;
		    
		    XDrawString (display, pop, gc, 10, y,
				 savelines[i], strlen(savelines[i]));
		  }
	      }
	  }
    }
  
  else if (type == ButtonRelease)
    {
      if (pop)     /* hide popup dialog */
	{
	  XDestroyWindow (display, pop);
	  pop = 0;
	}
      
      if (dist_flag == 2)   /* has line been drawn? */
	{
	  /* tidy up */
	  if (extendedcolor())
	    {
	      XSetFunction (display, gcplane, GXclear);
	      
	      XDrawLine (display, XtWindow (drawingarea), gcplane, dist_x, 
			 dist_y, dist_lastx, dist_lasty);
	    }
	  else
	    XDrawLine (display, XtWindow (drawingarea), gcxor,
		       dist_x, dist_y, dist_lastx, dist_lasty);
	}
      dist_flag = 0; 
    }

  else if (type == EnterNotify)
    puts ("");

  else if (type == MotionNotify)
    {
      /* display current coordinates */

      x = cbs->event->xmotion.x;
      y = cbs->event->xmotion.y;
      xcm = ((double)x-mapWidth/2)/mapWidth*mapWidthcm;
      ycm = ((double)mapHeight/2-y)/mapHeight*mapHeightcm;
      
      strcpy (temp, cm2string (xcm, ycm));

      tempstr = XmStringCreateSimple(temp);
      XtVaSetValues (coordwidget,
		     XmNlabelString, tempstr,
		     NULL);
      XmStringFree (tempstr);


      /* either find closest object or draw rubber line with distance/track info*/

      if (!dist_flag)
	{
	  tmpptr = nearobject (x, y);
	  if (tmpptr)
	    strcpy (temp, tmpptr);
	  else
	    sprintf (temp, "Scale: 1:%ld", map_scale);
	}
      else     /* rubber line mode */
	{
	  char t[100];
	  
	  if (dist_flag == 1)
	    {
	      dist_flag = 2;
	    }
	  else
	    {
	      /* clear old line */
	      
	      if (extendedcolor())
		{
		  XSetFunction (display, gcplane, GXclear);
		  XDrawLine (display, XtWindow (drawingarea), gcplane,
			     dist_x, dist_y, dist_lastx, dist_lasty);
		}
	      else
		XDrawLine (display, XtWindow (drawingarea), gcxor,
			   dist_x, dist_y, dist_lastx, dist_lasty);
	    }
	  
	  /* get new coordinates */
	  
	  dist_lastx = cbs->event->xmotion.x;
	  dist_lasty = cbs->event->xmotion.y;
	  
	  /* check if this 'snaps' to a close object */
	  
	  dist_b_used = 0;
	  
	  nearobject (dist_lastx, dist_lasty);
	  if (saveobj && (cbs->event->xmotion.state & ShiftMask))
	    {
	      if (saveobj->type/10  != O_CTR/10)   /* no areas */
		{
		  dist_b = saveobj->location;
		  dist_b_used = 1;
		  internal2window (saveobj->location,
				   &dist_lastx,
				   &dist_lasty);
		}
	    }
	  
	  
	  /* draw new line */
	  
	  if (extendedcolor())
	    {
	      XSetPlaneMask (display, gcplane, plane_mask (1));
	      XSetFunction (display, gcplane, GXset);
	      XDrawLine (display, XtWindow (drawingarea), gcplane,
			 dist_x, dist_y, dist_lastx, dist_lasty);
	    }
	  else
	    XDrawLine (display, XtWindow (drawingarea), 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);
	  
	  strcpy (temp, t);
	}



      if (strcmp (temp, lastobject))    /* changed since last call? */
	{
	  strcpy (lastobject, temp);
	  tempstr = XmStringCreateSimple(temp);
	  XtVaSetValues (scalewidget,
			 XmNlabelString, tempstr,
			 NULL);
	  XmStringFree (tempstr);
	}
    }
}


/* react to route buttons */



/* return the label left of a push button in the route menu */

static Widget labelofbutton (int index)  /* index is FROM...DEST */
{
  Widget label;

  switch (index) {
  case FROM:
    label = l_to;
    break;
  case VIA1:
    label = l_via1;
    break;
  case VIA2:
    label = l_via2;
    break;
  case VIA3:
    label = l_via3;
    break;
  case DEST:
    label = l_dest;
    break;
  }

  return label;
}


/* return button that belongs to index */

static Widget widgetofbutton (int index)  /* index is FROM...DEST */
{
  Widget label;

  switch (index) {
  case FROM:
    label = r_to;
    break;
  case VIA1:
    label = r_via1;
    break;
  case VIA2:
    label = r_via2;
    break;
  case VIA3:
    label = r_via3;
    break;
  case DEST:
    label = r_dest;
    break;
  }

  return label;
}



void routeCB (Widget w, XtPointer client_data, XmPushButtonCallbackStruct *cbs)
{
  int index = (int) client_data;    /* button that has been pressed (FROM...DEST) */
  Widget label;                     /* label left of pushed button */

  /* deselect current button (if any) */

  if (selectedroutebutton != -1)   /* change border of label */
    XtVaSetValues (labelofbutton (selectedroutebutton), 
		   XmNborderWidth,  0,
		   NULL);

  if (index == selectedroutebutton)   
    selectedroutebutton = -1;        /* switch off */
  else
    {
      selectedroutebutton = index;    /* select new one */
      XtVaSetValues (labelofbutton (index), 
		     XmNborderWidth,  3,
		     NULL);
    }


}




/* construct main window */

main(int argc, char *argv[])
{
    Widget           mainwindow;
    Widget           menubar;
    Widget           form, leftpanel, rightpanel, routepanel;
    String           file, map, route, plane, quit, scale, load, tempstr,tempstr2;
    String           scales, visobj, info, proj, router, soaring, triangle, testmenu;
    String           testfunc, drawr, undrawr, print;
    XtAppContext     app;
    int              i;
    Arg              args[20];
    Widget           quitButton;
    XmString         label;
    XmFontList       smallfont;
    XFontStruct      *font_info;
    XmString         *browselist;

    XGCValues        gcv;
    unsigned int     icon_width, icon_height;
    char             *window_name = "ICAO map";
    char             *icon_name = "ICAO map";

    XSetWindowAttributes attributes;

    Pixel     bg_color;
    Colormap  cmap;
    XColor    color, unused;

    String translations = "<Motion>:  DrawingAreaInput()
                           <BtnDown>: DrawingAreaInput()
                           <BtnUp>:   DrawingAreaInput()
                           <Key>:     DrawingAreaInput()
                           <EnterWindow>:   DrawingAreaInput()
                           <LeaveWindow>:   DrawingAreaInput()";
    


    toplevel = XtVaAppInitialize(&app, "Icao", NULL, 0,
        &argc, argv, fallbacks,
	XmNminWidth,            550,
	XmNminHeight,           400,
        XmNbaseWidth,             1,
        XmNbaseHeight,            1, 
	XmNmappedWhenManaged, False,			
	XmNtitle,               "ICAO Map - An Aviation Utility    \251 1993 Martin Pauly",
	NULL);

    mainwindow = XtVaCreateManagedWidget ("main_window", xmMainWindowWidgetClass, 
					  toplevel, NULL);


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


    /* create menu bar */

    file    = XmStringCreateSimple ("File");
    map     = XmStringCreateSimple ("Map");
    route   = XmStringCreateSimple ("Route");
    soaring = XmStringCreateSimple ("Soaring");
    testmenu= XmStringCreateSimple ("Test");

    menubar = XmVaCreateSimpleMenuBar (mainwindow, "menubar",
				       XmVaCASCADEBUTTON, file, 'F',
				       XmVaCASCADEBUTTON, map, 'M',
				       XmVaCASCADEBUTTON, route, 'R',
				       XmVaCASCADEBUTTON, soaring, 'S',
/*				       XmVaCASCADEBUTTON, testmenu, 'T', */
				       NULL);
    XmStringFree (file);
    XmStringFree (map);
    XmStringFree (route);
    XmStringFree (soaring);
    XmStringFree (testmenu);

    /* generate file menu */

    load = XmStringCreateSimple ("Load World File...");
    info = XmStringCreateSimple ("Information...");  
    quit = XmStringCreateSimple ("Quit");

    XmVaCreateSimplePulldownMenu (menubar, "filemenu", 0, (XtCallbackProc) fileCB,
				  XmVaPUSHBUTTON, load, 'W', NULL, NULL,
				  XmVaPUSHBUTTON, info, 'I', NULL, NULL,
				  XmVaPUSHBUTTON, quit, 'Q', NULL, NULL,
				  NULL);
    XmStringFree (quit);
    XmStringFree (info);
    XmStringFree (load);


    /* generate map menu */

    scales = XmStringCreateSimple ("Change Scale...");
    visobj = XmStringCreateSimple ("Select Visible Objects...");
    proj   = XmStringCreateSimple ("Select Map Projection...");
    print  = XmStringCreateSimple ("Print current map...");

    XmVaCreateSimplePulldownMenu (menubar, "mapmenu", 1, (XtCallbackProc) mapCB,
				  XmVaPUSHBUTTON, scales, 'S', NULL, NULL,
				  XmVaPUSHBUTTON, visobj, 'V', NULL, NULL,
				  XmVaPUSHBUTTON, proj,   'P', NULL, NULL,
				  XmVaPUSHBUTTON, print,  'r', NULL, NULL,
				  NULL);
    XmStringFree (scales);
    XmStringFree (visobj);
    XmStringFree (proj);
    XmStringFree (print);


    /* generate route menu */

    router  = XmStringCreateSimple ("Autorouter");
    drawr   = XmStringCreateSimple ("Draw Current Route");
    undrawr = XmStringCreateSimple ("Undraw Route");

    XmVaCreateSimplePulldownMenu (menubar, "routemenu", 2, (XtCallbackProc) routemenuCB,
				  XmVaPUSHBUTTON, router,  'r', NULL, NULL,
				  XmVaPUSHBUTTON, drawr,   'D', NULL, NULL,
				  XmVaPUSHBUTTON, undrawr, 'U', NULL, NULL,
				  NULL);
    XmStringFree (router);
    XmStringFree (drawr);
    XmStringFree (undrawr);



    XtManageChild (menubar);


    /* generate soaring menu */

    triangle = XmStringCreateSimple ("Suggest Triangle...");
    
    XmVaCreateSimplePulldownMenu (menubar, "soaringmenu", 3, (XtCallbackProc) soaringCB,
				  XmVaPUSHBUTTON, triangle, 'T', NULL, NULL,
				  NULL);
    XmStringFree (triangle);
    


    /* generate test menu */

/*    testfunc = XmStringCreateSimple ("Test");
    
    XmVaCreateSimplePulldownMenu (menubar, "testmenu", 4, (XtCallbackProc) testCB,
				  XmVaPUSHBUTTON, testfunc, 'T', NULL, NULL,
				  NULL);
    XmStringFree (testfunc);  */
    


    XtManageChild (menubar);


    /* create form widget for main window */

    form = XtVaCreateManagedWidget ("form", xmFormWidgetClass, mainwindow,
				    NULL);


    leftpanel = XtVaCreateManagedWidget ("left", xmFormWidgetClass, form,
					 XmNtopAttachment,    XmATTACH_FORM,
					 XmNbottomAttachment, XmATTACH_FORM,
					 XmNleftAttachment,   XmATTACH_FORM,
					 XmNrightAttachment,  XmATTACH_OPPOSITE_FORM,
					 XmNrightOffset,      -200,
					 NULL);

    coordwidget = XtVaCreateManagedWidget ("coordinates", xmLabelWidgetClass, form,
					   XmNlabelString,      XmStringCreateSimple (""),
					   XmNtopAttachment,    XmATTACH_FORM,
					   XmNbottomAttachment, XmATTACH_OPPOSITE_FORM,
					   XmNbottomOffset,     -20,
					   XmNleftAttachment,   XmATTACH_WIDGET,
					   XmNleftWidget,       leftpanel,
					   XmNwidth,            180,
					   NULL);


    scalewidget = XtVaCreateManagedWidget ("objectinfo", xmLabelWidgetClass, form,
					   XmNlabelString,      XmStringCreateSimple (""),
					   XmNalignment,        XmALIGNMENT_END,
					   XmNtopAttachment,    XmATTACH_FORM,
					   XmNbottomAttachment, XmATTACH_OPPOSITE_FORM,
					   XmNbottomOffset,     -20,
					   XmNleftAttachment,   XmATTACH_WIDGET,
					   XmNleftWidget,       coordwidget,
					   XmNrightAttachment,  XmATTACH_FORM,
					   NULL);



    drawingarea = XtVaCreateManagedWidget ("drawingarea", xmDrawingAreaWidgetClass, 
					   form,
					   XmNtopAttachment,    XmATTACH_WIDGET,
					   XmNtopWidget,        coordwidget,
					   XmNbottomAttachment, XmATTACH_FORM,
					   XmNleftAttachment,   XmATTACH_WIDGET,
					   XmNleftWidget,       leftpanel,
					   XmNrightAttachment,  XmATTACH_FORM,
					   XmNborderWidth,      1,
					   NULL);

    XtAddCallback (drawingarea, XmNexposeCallback, 
		   (XtCallbackProc) drawingarea_expose_callback, NULL);
    XtAddCallback (drawingarea, XmNresizeCallback, 
		   (XtCallbackProc) drawingarea_resize_callback, NULL);
    XtAddCallback (drawingarea, XmNinputCallback, 
		   (XtCallbackProc) drawingarea_input_callback, NULL);

    XtOverrideTranslations (drawingarea,
			   XtParseTranslationTable (translations));



    /* obtain a GC and store it */

    display = XtDisplay (toplevel);
    screen_num = DefaultScreen(display);

    gcv.foreground = BlackPixelOfScreen (XtScreen (drawingarea));

    gc = XCreateGC (XtDisplay (drawingarea),
		    RootWindowOfScreen (XtScreen (drawingarea)),
		    GCForeground, &gcv);

    get2ndGC (RootWindowOfScreen (XtScreen (drawingarea)), &gcxor);
    get2ndGC (RootWindowOfScreen (XtScreen (drawingarea)), &gcplane);

    XSetLineAttributes (display, gcplane, 1, LineSolid, CapButt, JoinRound);


    /* create left panel */

    routepanel = XtVaCreateManagedWidget ("routef", xmFormWidgetClass, leftpanel,
					  XmNtopAttachment,    XmATTACH_FORM,
					  XmNleftAttachment,   XmATTACH_FORM,
					  XmNrightAttachment,  XmATTACH_FORM,
					  XmNallowShellResize, True,
					  NULL);

    /* a bug in Motif? the push button label mustn't be longer than the button,
       otherwise some "strange" behaviour will be noticed (23 blanks do work, however) */

    tempstr2 = XmStringCreateSimple ("                       ");

    tempstr = XmStringCreateSimple ("Current route:");
    l_title = XtVaCreateManagedWidget ("route1", xmLabelWidgetClass, routepanel, 
				       XmNlabelString, tempstr,
				       XmNtopAttachment,    XmATTACH_FORM,
				       XmNleftAttachment,   XmATTACH_FORM,
				       NULL);
    XmStringFree (tempstr);

    tempstr = XmStringCreateSimple ("t/o");
    l_to = XtVaCreateManagedWidget ("takeoffl", xmLabelWidgetClass, routepanel, 
				    XmNlabelString,      tempstr,
				    XmNtopAttachment,    XmATTACH_WIDGET,
				    XmNtopWidget,        l_title,
				    XmNleftAttachment,   XmATTACH_FORM,
				    NULL);

    /* depending on whether we use drag'n drop technique or not, the right
       side boxes of the current route display have to be labels or pushbuttons */

#ifdef DRAGDROP
#define RBOX xmLabelWidgetClass
#else
#define RBOX xmPushButtonWidgetClass
#endif

    XmStringFree (tempstr);
    r_to = XtVaCreateManagedWidget ("takeoffr", RBOX, routepanel, 
				    XmNlabelString,      tempstr2,
				    XmNalignment,        XmALIGNMENT_BEGINNING, 
				    XmNtopAttachment,    XmATTACH_WIDGET,
				    XmNtopWidget,        l_title,
				    XmNleftAttachment,   XmATTACH_FORM,
				    XmNleftOffset,       40,
				    XmNrightAttachment,  XmATTACH_FORM,
				    XmNrightOffset,      5,
				    XmNborderWidth,      1,
				    NULL);    


    tempstr = XmStringCreateSimple ("via");
    l_via1 = XtVaCreateManagedWidget ("via1l", xmLabelWidgetClass, routepanel, 
				      XmNlabelString,      tempstr,
				      XmNtopAttachment,    XmATTACH_WIDGET,
				      XmNtopWidget,        r_to,
				      XmNleftAttachment,   XmATTACH_FORM,
				      NULL);

    r_via1 = XtVaCreateManagedWidget ("via1r", RBOX, routepanel, 
				      XmNlabelString,      tempstr2,
				      XmNalignment,        XmALIGNMENT_BEGINNING, 
				      XmNtopAttachment,    XmATTACH_WIDGET,
				      XmNtopWidget,        r_to,
				      XmNleftAttachment,   XmATTACH_FORM,
				      XmNleftOffset,       40,
				      XmNrightAttachment,  XmATTACH_FORM,
				      XmNrightOffset,      5,
				      XmNborderWidth,      1,
				      NULL);
    
    l_via2 = XtVaCreateManagedWidget ("via2l", xmLabelWidgetClass, routepanel, 
				      XmNlabelString,      tempstr,
				      XmNtopAttachment,    XmATTACH_WIDGET,
				      XmNtopWidget,        r_via1,
				      XmNleftAttachment,   XmATTACH_FORM,
				      NULL);
    r_via2 = XtVaCreateManagedWidget ("via2r", RBOX, routepanel, 
				      XmNlabelString,      tempstr2,
				      XmNalignment,        XmALIGNMENT_BEGINNING, 
				      XmNtopAttachment,    XmATTACH_WIDGET,
				      XmNtopWidget,        r_via1,
				      XmNleftAttachment,   XmATTACH_FORM,
				      XmNleftOffset,       40,
				      XmNrightAttachment,  XmATTACH_FORM,
				      XmNrightOffset,      5,
				      XmNborderWidth,      1,
				      NULL);
    
    l_via3 = XtVaCreateManagedWidget ("via3l", xmLabelWidgetClass, routepanel, 
				      XmNlabelString,      tempstr,
				      XmNtopAttachment,    XmATTACH_WIDGET,
				      XmNtopWidget,        r_via2,
				      XmNleftAttachment,   XmATTACH_FORM,
				      NULL);
    r_via3 = XtVaCreateManagedWidget ("via3r", RBOX, routepanel, 
				      XmNlabelString,      tempstr2,
				      XmNalignment,        XmALIGNMENT_BEGINNING, 
				      XmNtopAttachment,    XmATTACH_WIDGET,
				      XmNtopWidget,        r_via2,
				      XmNleftAttachment,   XmATTACH_FORM,
				      XmNleftOffset,       40,
				      XmNrightAttachment,  XmATTACH_FORM,
				      XmNrightOffset,      5,
				      XmNborderWidth,      1,
				      NULL);
    XmStringFree (tempstr);

    tempstr = XmStringCreateSimple ("dest");
    l_dest = XtVaCreateManagedWidget ("destl", xmLabelWidgetClass, routepanel, 
				      XmNlabelString,      tempstr,
				      XmNtopAttachment,    XmATTACH_WIDGET,
				      XmNtopWidget,        r_via3,
				      XmNleftAttachment,   XmATTACH_FORM,
				      NULL);

    r_dest = XtVaCreateManagedWidget ("destr", RBOX, routepanel, 
				      XmNlabelString,      tempstr2,
				      XmNalignment,        XmALIGNMENT_BEGINNING, 
				      XmNtopAttachment,    XmATTACH_WIDGET,
				      XmNtopWidget,        r_via3,
				      XmNleftAttachment,   XmATTACH_FORM,
				      XmNleftOffset,       40,
				      XmNrightAttachment,  XmATTACH_FORM,
				      XmNrightOffset,      5,
				      XmNborderWidth,      1,
				      NULL);
    XmStringFree (tempstr);
    XmStringFree (tempstr2);
    
    
    /* drop site registration */

#ifdef DRAGDROP
    i = 0;
    XtSetArg (args[i], XmNimportTargets,    ddcTarget);                  i++;
    XtSetArg (args[i], XmNnumImportTargets, 1);                          i++;
    XtSetArg (args[i], XmNdropProc,         ddcDropProc);                i++;
    XtSetArg (args[i], XmNanimationStyle,   XmDRAG_UNDER_HIGHLIGHT);     i++; 
    XmDropSiteRegister (r_to, args, i);
#else
  XtAddCallback (r_to, XmNactivateCallback, (XtCallbackProc) routeCB, (XtPointer) FROM);
  XtAddCallback (r_via1, XmNactivateCallback, (XtCallbackProc) routeCB, (XtPointer) VIA1);
  XtAddCallback (r_via2, XmNactivateCallback, (XtCallbackProc) routeCB, (XtPointer) VIA2);
  XtAddCallback (r_via3, XmNactivateCallback, (XtCallbackProc) routeCB, (XtPointer) VIA3);
  XtAddCallback (r_dest, XmNactivateCallback, (XtCallbackProc) routeCB, (XtPointer) DEST);
#endif


    /* create object list */

    i=0;
    XtSetArg (args[i], XmNtopAttachment,    XmATTACH_WIDGET);  i++;
    XtSetArg (args[i], XmNtopWidget,        routepanel);       i++;
    XtSetArg (args[i], XmNbottomAttachment, XmATTACH_FORM);    i++;
    XtSetArg (args[i], XmNbottomOffset,     32);               i++;    
    XtSetArg (args[i], XmNleftAttachment,   XmATTACH_FORM);    i++;
    XtSetArg (args[i], XmNrightAttachment,  XmATTACH_FORM);    i++;

    objectpanel = XmCreateScrolledList (leftpanel, "objects", args, i);

    XtAddCallback (objectpanel, XmNdefaultActionCallback, 
		   (XtCallbackProc) objectpaneldefaultCB, NULL);
    XtAddCallback (objectpanel, XmNbrowseSelectionCallback, 
		   (XtCallbackProc) objectpanelsimpleCB, NULL);

    XtManageChild (objectpanel);


    objectname = XtVaCreateManagedWidget ("objectname", xmTextFieldWidgetClass, leftpanel,
					  XmNtopAttachment,    XmATTACH_WIDGET,
					  XmNtopWidget,        objectpanel,
					  XmNbottomAttachment, XmATTACH_FORM,
					  XmNleftAttachment,   XmATTACH_FORM,
					  XmNrightAttachment,  XmATTACH_FORM,
					  NULL);
    XtAddCallback (objectname, XmNfocusCallback, 
		   (XtCallbackProc) textfocusCB, NULL);
    XtAddCallback (objectname, XmNvalueChangedCallback, 
		   (XtCallbackProc) textchangedCB, NULL);
    XtAddCallback (objectname, XmNactivateCallback, 
		   (XtCallbackProc) textchangedCB, (XtPointer) 1);

					  
					  

    /* realize main window */

    XtRealizeWidget(toplevel);

    /* load objects, init rest of application */

    if (argc == 2)
      {
	input = fopen (argv[1], "r");
	if (input)
	  yyin = input;
	else
	  printf ("Could not find input file %s - aborted!\n", argv[1]);
      }
    
    objects_init();
    initpopup();
    
    yyparse(); 
    
    map_initcoord();
    sort_objects();




    /* insert object strings into browser */

    browselist = (XmString *) malloc (sizeof(XmString) * tableentries);

    for (i=0; i<tableentries; i++)
      {
	browselist[i] = XmStringCreateSimple (namearray[i]);
      }

    XmListAddItems (objectpanel, browselist, tableentries, 0);



    /* set up color table */

    initcolor (&gc);
    
    /* get size of map display in cm */

    XtVaGetValues (drawingarea,
		   XmNwidth, &mapWidth,
		   XmNheight, &mapHeight,
		   NULL);

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

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

    map_initcoord();

    
    /* create crosshair cursor for map window */
    
    pointer = XCreateFontCursor (display, XC_crosshair);
    waitpointer = XCreateFontCursor (display, XC_watch);
    XDefineCursor (display, XtWindow (drawingarea), pointer);
    

    /* load required fonts */

    load_font(&font_small, &font_big);


    /* make white background */

    bg_color = whitepix();
    XtVaSetValues (drawingarea,
		   XmNbackground,  bg_color,
		   NULL);
    


    /* register icon for minimized application */

    icon_pixmap = XCreateBitmapFromData(display, XtWindow (toplevel), bitmap_bits, 
					bitmap_width, bitmap_height);
    XtVaSetValues (toplevel,
		   XmNiconPixmap,  icon_pixmap,
		   NULL);


    /* enable backing store for map window, that speeds up quite a bit */

    attributes.backing_store = WhenMapped;
    
    XChangeWindowAttributes (display, XtWindow (drawingarea), 
			     CWBackingStore, &attributes);

    XtAddEventHandler (drawingarea, LeaveWindowMask, False, drawingarea_leave_callback,
		       (XtPointer) 0L);

#ifndef DRAGDROP
    XtAddEventHandler (r_to, ButtonReleaseMask, False, route_release_callback,
		       (XtPointer) 0L);

#endif


#ifdef DRAGDROP
    ddcTarget = XInternAtom (display, "ICAO_OBJECT", False);
#endif

    XtMapWidget (toplevel);

    XtAppMainLoop(app);
}











