/*
 * imaptool.c
 * Copyright (C) 2000 Seth Spitzer - sspitzer@sspitzer.org
 * Copyright (C) 1996-1998 Teemu Maijala - uucee@sci.fi
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#include <X11/Intrinsic.h>
#include <X11/Xlib.h>
#include <X11/Shell.h>
#include <X11/StringDefs.h>
#include <X11/cursorfont.h>
#include <X11/Xatom.h>
#include <X11/Xaw/Label.h>
#include <X11/Xaw/Box.h>
#include <X11/Xaw/Form.h>
#include <X11/Xaw/Command.h>
#include <X11/Xaw/SimpleMenu.h>
#include <X11/Xaw/MenuButton.h>
#include <X11/Xaw/SmeBSB.h>
#include <X11/IntrinsicP.h>
#include <X11/CoreP.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include "file2pixmap.h"
#include "icon"
#include "iconmask"

#define VERSION "0.9"
#define HTMLCOMMENT "<!-- Map generated by imaptool %s  http://www.sspitzer.org/imaptool/ -->\n"
#define PROGINFO "imaptool %s\n\n(C) 2000 Seth Spitzer - sspitzer@sspitzer.org\n(C) 1996-1998 Teemu Maijala - uucee@sci.fi\n\nimaptool comes with ABSOLUTELY NO WARRANTY; for details see LICENCE.TXT.\n"

#define QUIT_BUTTON_TEXT					"Quit"
#define DUMP_HTML_BUTTON_TEXT				"Dump HTML"
#define CLEAR_HTML_BUTTON_TEXT				"Clear HTML"
#define SHAPE_BUTTON_TEXT					"Shape"

#define CIRCLE_MENU_ITEM_UNSELECTED			"  Circle"
#define CIRCLE_MENU_ITEM_SELECTED			"o Circle"
#define RECTANGLE_MENU_ITEM_UNSELECTED		"  Rectangle"
#define RECTANGLE_MENU_ITEM_SELECTED		"o Rectangle"
#define POLYGON_MENU_ITEM_UNSELECTED		"  Polygon (use the right mouse button to close the polygon)"
#define POLYGON_MENU_ITEM_SELECTED			"o Polygon (use the right mouse button to close the polygon)"

/* Shapes */
#define RECTANGLE  1
#define CIRCLE     2
#define POLYGON    3

/* Buttonstatus */
#define UP 0
#define DOWN 1

/* Maximum number of breakpoints in polygon */
#define POL_MAX_POINTS 100

/* Maximum number of areas, hard coded for now. */
#define MAX_AREAS 1000

/* the longest HTML line for a map area */
#define MAX_AREA_TEXT_LEN 1124

/* Some macros */
#define min(a,b) (a<b)?a:b
#define max(a,b) (a>b)?a:b

/* Misc functions */
void createpicturewindow();
void parseparams(int argc, char *argv[]);
void printusage();
void printversion();
void changeinfotext(char *infotext);
void insert(char *position);
void printrectangle(char *str);
void printcircle(char *str);
void printpolygon(char *str);

/* Event handlers for vviewwindow */
void buttondown(Widget w, XtPointer client_data, XEvent *event, Boolean *ctd);
void buttonup(Widget w, XtPointer client_data, XEvent *event, Boolean *ctd);
void expose(Widget w, XtPointer client_data, XEvent *event, Boolean *ctd);
void enterwindow(Widget w, XtPointer client_data, XEvent *event, Boolean *ctd);
void leavewindow(Widget w, XtPointer client_data, XEvent *event, Boolean *ctd);
void motion(Widget w, XtPointer client_data, XEvent *event, Boolean *ctd);

/* Event handler for client_message & selections (for WM_DELETE_WINDOW) */
void event_handler(Widget w, XtPointer client_data, XEvent *event,
		   Boolean *ctd);

/* Callback functions */
void quit(Widget w, XtPointer call_data, XtPointer client_data);
void dump(Widget w, XtPointer call_data, XtPointer client_data);
void clear(Widget w, XtPointer call_data, XtPointer client_data);
void shapeselect(Widget w, XtPointer call_data, XtPointer client_data);

/* Functions to draw shapes on vviewwindow */
void drawcircle(Widget w);
void drawpolygon(Widget w);
void drawrectangle(Widget w);
void erase_polygon_motionline(Widget w);

/* Global variables */
Atom wm_protocols, wm_delete_window;
char *picturefilename; /* name of current gif/jpeg-file */ 
GC defgc;              /* GC for LineDoubleDash */
Pixmap pixmap;         /* Pixmap for gif/jpeg-file */
Pixmap iconpixmap, iconmask;     /* Pixmap for imaptool-icon and -mask*/
int currentarea = 0;
char areas[MAX_AREAS][MAX_AREA_TEXT_LEN];

#define QUIT_SAVENOTIFY 1
#define OTHER_SAVENOTIFY 0

struct _pixmasize {
  int width, height;
} pixmapsize;          /* size of current pixmap */
 
int nextshape = RECTANGLE;     /* shape selected in shapemenu */
int currentshape = RECTANGLE;  /* shape on screen */

struct _rectangle {            /* geometry of rectangle */
  int x1, y1, x2, y2;
} rectangle = {0,0,0,0};

struct _circle {               /* geometry of circle */
  int x, y, r;
} circle = {0,0,0};

struct _polygon_motionline {   /* motionline when drawing polygon */
  int x, y;
  Boolean active;
} polygon_motionline = {0,0, FALSE};

XPoint polygon[POL_MAX_POINTS];  /* A table containing brakpoints of polygon */
int polcount;                    /* total abount of breakpoints in polygon */

/* if active is true, drawcircle, drawrectangle and drawpolygon should draw
   polygon, otherwise they should erase it. Usually the functions are first
   called width active false and then with active true. */

Boolean active = FALSE;
Boolean outputHtmlInLowerCase = FALSE;

int buttonstatus; /* status of mousebuttons */ 

/* Widgets */
Widget root, picturewindow;
Widget vform, vmenubox, vbox, vviewwindow, vquit, vinfobox, vdump, vclear;
Widget vshapemenu, vshapemenubutton, vrectangle, vcircle, vpolygon;

int main(int argc, char *argv[])
{
  int screennum;
  XtAppContext app_context;
  int cursor_shape = XC_tcross;
  Cursor cursor;
  XGCValues values;
  root = XtVaAppInitialize(&app_context,
			   "imaptool",
			   NULL, 0,
			   &argc, argv,
			   NULL, NULL);
  screennum = XScreenNumberOfScreen(XtScreen(root));
  parseparams(argc, argv);
  /* loading picture to pixmap */
  if (!load_file_to_pixmap(picturefilename, XtDisplay(root), screennum,
			   &pixmapsize.width, &pixmapsize.height, &pixmap)) {
    printusage();
	exit(1);
  }
  /* creating pixmaps for icon & iconmask */
  iconpixmap = XCreatePixmapFromBitmapData(XtDisplay(root),
					   RootWindow(XtDisplay(root), screennum),
					   (char*)icon_bits,
					   icon_width,
					   icon_height,
					   WhitePixel(XtDisplay(root), screennum),
					   BlackPixel(XtDisplay(root), screennum),
					   DefaultDepth(XtDisplay(root),
							screennum));
  iconmask = XCreatePixmapFromBitmapData(XtDisplay(root),
					 RootWindow(XtDisplay(root), screennum),
					 (char*)iconmask_bits,
					 iconmask_width,
					 iconmask_height,
					 WhitePixel(XtDisplay(root), screennum),
					 BlackPixel(XtDisplay(root), screennum),
					 1);

  /* creating window widgets */
  createpicturewindow();
  
  /* showing windows */
  XtPopup(picturewindow, XtGrabNone);
  cursor = XCreateFontCursor(XtDisplay(vviewwindow), cursor_shape);
  XDefineCursor(XtDisplay(vviewwindow), XtWindow(vviewwindow), cursor);

  /* setting up default GC */
  values.foreground = WhitePixel(XtDisplay(picturewindow),
				 XScreenNumberOfScreen(XtScreen(picturewindow)));
  values.background = BlackPixel(XtDisplay(picturewindow),
				 XScreenNumberOfScreen(XtScreen(picturewindow)));
  values.line_style = LineDoubleDash;
  defgc = XCreateGC(XtDisplay(picturewindow), XtWindow(picturewindow),
		 (GCForeground|GCBackground|GCLineStyle), &values);

  XtAddEventHandler(vviewwindow, 0, TRUE, event_handler, (XtPointer)NULL);
  /* going to main loop */
  XtAppMainLoop(app_context);
}

void createpicturewindow()
{
  Pixel menuboxcolor;
  picturewindow = XtVaCreatePopupShell("picturewindow",
				       applicationShellWidgetClass,
				       root,
				       XtNtitle, picturefilename,
				       XtNiconName, picturefilename,
				       XtNiconPixmap, iconpixmap,
				       XtNiconMask, iconmask,
				       NULL);
  vform = XtVaCreateManagedWidget("rootform",
				  formWidgetClass,
				  picturewindow,
				  XtNresizable, TRUE,
				  NULL);
  vmenubox = XtVaCreateManagedWidget("menubox",
				     boxWidgetClass,
				     vform,
				     XtNborderWidth, 0,
				     XtNtop, XawChainTop,
				     XtNbottom, XawChainTop,
				     XtNleft, XawChainLeft,
				     XtNright, XawChainLeft,
				     XtNorientation, XtorientHorizontal, 
				     XtNresizable, TRUE, 
				     NULL);
  XtVaGetValues(vmenubox, XtNbackground, &menuboxcolor, NULL);
  vquit = XtVaCreateManagedWidget(QUIT_BUTTON_TEXT,
				  commandWidgetClass,
				  vmenubox,
				  NULL);
  XtAddCallback(vquit, XtNcallback, quit, (XtPointer)NULL); 
  vdump = XtVaCreateManagedWidget(DUMP_HTML_BUTTON_TEXT,
					commandWidgetClass,
					vmenubox,
					NULL);
  XtAddCallback(vdump, XtNcallback, dump, (XtPointer)NULL); 
  vclear= XtVaCreateManagedWidget(CLEAR_HTML_BUTTON_TEXT,
					commandWidgetClass,
					vmenubox,
					NULL);
  XtAddCallback(vclear, XtNcallback, clear, (XtPointer)NULL); 
  vshapemenu = XtVaCreatePopupShell("vshapemenu",
				    simpleMenuWidgetClass,
				    picturewindow,
				    NULL);
  vshapemenubutton = XtVaCreateManagedWidget("vshapemenubutton",
					     menuButtonWidgetClass,
					     vmenubox,
					     XtNmenuName, "vshapemenu",
					     XtNlabel, SHAPE_BUTTON_TEXT,
					     NULL);
  vrectangle = XtVaCreateManagedWidget(RECTANGLE_MENU_ITEM_SELECTED,
				       smeBSBObjectClass,
				       vshapemenu,
				       NULL);
  vcircle = XtVaCreateManagedWidget(CIRCLE_MENU_ITEM_UNSELECTED,
				    smeBSBObjectClass,
				    vshapemenu,
				    NULL);
  vpolygon = XtVaCreateManagedWidget(POLYGON_MENU_ITEM_UNSELECTED,
				     smeBSBObjectClass,
				     vshapemenu,
				     NULL);
  XtAddCallback(vrectangle, XtNcallback, shapeselect, (XtPointer)RECTANGLE);
  XtAddCallback(vcircle, XtNcallback, shapeselect, (XtPointer)CIRCLE);
  XtAddCallback(vpolygon, XtNcallback, shapeselect, (XtPointer)POLYGON);
  vinfobox = XtVaCreateManagedWidget("infobox",
				     labelWidgetClass,
				     vmenubox,
				     XtNborderWidth, 0,
				     XtNbackground, menuboxcolor,
				     XtNlabel, "                         ",
				     XtNresizable, TRUE,
				     NULL);
  vbox = XtVaCreateManagedWidget("box",
				 boxWidgetClass,
				 vform,
				 XtNfromVert, vmenubox,
				 XtNhSpace, 0,
				 XtNvSpace, 0,
				 XtNtop, XawChainTop,
				 XtNbottom, XawChainTop,
				 XtNleft, XawChainLeft,
				 XtNright, XawChainLeft,
				 NULL);
  vviewwindow = XtVaCreateManagedWidget("viewwindow",
					coreWidgetClass,
					vbox,
					XtNinternalHeight, 0,
					XtNinternalWidth, 0, 
					XtNborderWidth, 0,
					NULL);
  XtAddEventHandler(vviewwindow, ButtonPressMask, FALSE, buttondown,
		    (XtPointer)NULL);
  XtAddEventHandler(vviewwindow, ButtonReleaseMask, FALSE, buttonup,
		    (XtPointer)NULL);
  XtAddEventHandler(vviewwindow, ExposureMask, FALSE, expose,
		    (XtPointer)NULL);
  XtAddEventHandler(vviewwindow, PointerMotionMask, FALSE, motion,
		    (XtPointer)NULL);
  XtAddEventHandler(vviewwindow, EnterWindowMask, FALSE, enterwindow,
		    (XtPointer)NULL);
  XtAddEventHandler(vviewwindow, LeaveWindowMask, FALSE, leavewindow,
		    (XtPointer)NULL);
  XtVaSetValues(vviewwindow, XtNwidth, pixmapsize.width,
		XtNheight, pixmapsize.height, NULL);
}

void parseparams(int argc, char *argv[])
{
  if (argc < 2) {
	printusage();
    exit(0);
  }

  if ((strcmp("-h",argv[1]) == 0) ||
	  (strcmp("-help",argv[1]) == 0) ||
	  (strcmp("--help",argv[1]) == 0))  {
	printusage();
    exit(0);
  }

  if ((strcmp("-v",argv[1]) == 0) ||
	  (strcmp("-version",argv[1]) == 0) ||
 	  (strcmp("--version",argv[1]) == 0)) {
	printversion();
    exit(0);
  }

  if ((strcmp("-l",argv[1]) == 0) ||
	  (strcmp("-lower",argv[1]) == 0) ||	
	  (strcmp("--lower",argv[1]) == 0)) {
	if (argc == 3) {
		outputHtmlInLowerCase = TRUE;
  		picturefilename = argv[2];
		return;
	}
	else {
		printusage();
		exit(0);
	}
  }

  if ((strcmp("-u",argv[1]) == 0) ||
	  (strcmp("-upper",argv[1]) == 0) ||	
	  (strcmp("--upper",argv[1]) == 0)) {
	if (argc == 3) {
		outputHtmlInLowerCase = FALSE;
  		picturefilename = argv[2];
		return;
	}
	else {
		printusage();
		exit(0);
	}
  }
	
  if (argc == 2) { 
	  picturefilename = argv[1]; 
      return;
  }
  else {
	  printusage();
      exit(0);    
  }
}

void printversion()
{
  printf(PROGINFO,VERSION);
}

void printusage()
{ 
  printf("Usage: imaptool [options] file\n");
  printf("options:\n");
  printf("\t-v\tversion\n");
  printf("\t-h\tusage\n");
  printf("\t-l\toutput html in lowercase\n");
  printf("\t-u\toutput html in uppercase\n");
  printf("\n");
  printf("file types currently supported JPEG and GIF\n");
  printf("\n");
  printf("Report bugs to sspitzer@sspitzer.org\n");
}

void changeinfotext(char *text)
{
  int width, height;
  XExposeEvent xeev;
  char *current_label;
  XtVaGetValues(vinfobox, XtNlabel, &current_label, NULL);
  if(strcmp(current_label, text)) {
    xeev.type = Expose;
    xeev.display = XtDisplay(vinfobox);
    xeev.window = XtWindow(vinfobox);
    xeev.x = xeev.y = 0;
    XtVaGetValues(vinfobox, XtNwidth, &width, XtNheight, &height, NULL);
    xeev.width = width;
    xeev.height = height;
    XtVaSetValues(vinfobox, XtNlabel, text, NULL);
    (XtClass(vinfobox))->core_class.expose (vinfobox, (XEvent*)&xeev, NULL);
  }
}

void buttondown(Widget w, XtPointer client_data, XEvent *event, Boolean *ctd)
{
  active = FALSE;
  switch(currentshape) {
  case RECTANGLE:
    drawrectangle(w);
    break;
  case CIRCLE:
    drawcircle(w);
    break;
  case POLYGON:
    if (nextshape!=POLYGON) drawpolygon(w);
    break;
  }
  switch(nextshape) {
  case RECTANGLE:
    currentshape = RECTANGLE;
    rectangle.x1 = event->xbutton.x;
    rectangle.y1 = event->xbutton.y;
    rectangle.x2 = event->xbutton.x;
    rectangle.y2 = event->xbutton.y;
    buttonstatus = DOWN;
    break;
  case CIRCLE:
    currentshape = CIRCLE;
    circle.x = event->xbutton.x;
    circle.y = event->xbutton.y;
    circle.r = 0;
    break;
  case POLYGON:
    currentshape = POLYGON;
    break;
  }
  buttonstatus = DOWN;
}

void incrementcurrentarea()
{
    currentarea++;
    if (currentarea >= MAX_AREAS) {
	printf("maximum number of areas reached.  overwriting the last area\n");
	currentarea = MAX_AREAS-1;
    }
}

void buttonup(Widget w, XtPointer client_data, XEvent *event, Boolean *ctd)
{
  int x, y;
  XSetSelectionOwner(XtDisplay(w), XA_PRIMARY,
		     XtWindow(w), CurrentTime);  

  switch(currentshape) {
  case RECTANGLE:
    active = FALSE;
    drawrectangle(w);
    rectangle.x2 = event->xbutton.x;
    rectangle.y2 = event->xbutton.y;
    active = TRUE;
    drawrectangle(w);
    printrectangle(areas[currentarea]);
    incrementcurrentarea();
    break;
   case CIRCLE:
    active = FALSE;
    drawcircle(w);
    x = circle.x-event->xbutton.x;
    y = circle.y-event->xbutton.y;
    circle.r = (int)sqrt(x*x+y*y);
    active = TRUE;
    drawcircle(w);
    printcircle(areas[currentarea]);
    incrementcurrentarea();
    break;
  case POLYGON:
    if (event->xbutton.button==1) {
      if (polcount<POL_MAX_POINTS) {
			polygon[polcount].x = event->xbutton.x;
			polygon[polcount].y = event->xbutton.y;
			polcount++;
      }
      active = TRUE;
      drawpolygon(w);
  	  polygon_motionline.active = TRUE;
    } else if (event->xbutton.button==2) {
		/* ignore middle button events */
    } else if (event->xbutton.button==3) {
      active = FALSE;
      drawpolygon(w);

	  printpolygon(areas[currentarea]);
      incrementcurrentarea();

      if (polygon_motionline.active == TRUE) erase_polygon_motionline(w);
      polcount = 0;
      polygon_motionline.active = FALSE;
    }
    break;
  }
  buttonstatus = UP;
}

void expose(Widget w, XtPointer client_data, XEvent *event, Boolean *ctd)
{
  XCopyArea(XtDisplay(w), pixmap, XtWindow(w), defgc, event->xexpose.x,
	    event->xexpose.y, event->xexpose.width, event->xexpose.height,
	    event->xexpose.x, event->xexpose.y);
  switch(currentshape) {
  case RECTANGLE:
    drawrectangle(w);
    break;
  case CIRCLE:
    drawcircle(w);
    break;
  case POLYGON:
    drawpolygon(w);
    break;
  }
}

void enterwindow(Widget w, XtPointer client_data, XEvent *event, Boolean *ctd)
{
  if ((currentshape==POLYGON)&&(polygon_motionline.active==TRUE)) {
    polygon_motionline.x = event->xcrossing.x;
    polygon_motionline.y = event->xcrossing.y;
    XDrawLine(XtDisplay(w), XtWindow(w), defgc, polygon[polcount-1].x,
	      polygon[polcount-1].y, polygon_motionline.x, polygon_motionline.y);
  }
}

void leavewindow(Widget w, XtPointer client_data, XEvent *event, Boolean *ctd)
{
  if ((currentshape==POLYGON)&&(polygon_motionline.active==TRUE)) {
    erase_polygon_motionline(w);
    active = TRUE;
    drawpolygon(w);
  }
}

void motion(Widget w, XtPointer client_data, XEvent *event, Boolean *ctd)
{
  char infotext[80];
  int x1, x2, y1, y2;
  switch(currentshape) {
  case RECTANGLE:
    if (buttonstatus==DOWN) {
      active = FALSE;
      drawrectangle(w);
      rectangle.x2 = event->xmotion.x;
      rectangle.y2 = event->xmotion.y;
      active = TRUE;
      drawrectangle(w);
    }
    x1 = min(rectangle.x1, rectangle.x2);
    y1 = min(rectangle.y1, rectangle.y2);
    x2 = max(rectangle.x1, rectangle.x2);
    y2 = max(rectangle.y1, rectangle.y2);
    sprintf(infotext, "%d,%d : %d,%d,%d,%d", event->xmotion.x,
	    event->xmotion.y, x1, y1, x2, y2);
    changeinfotext(infotext);
    break;
  case CIRCLE:
    if (buttonstatus==DOWN) {
      active = FALSE;
      drawcircle(w);
      circle.r = (int)hypot((circle.x-event->xbutton.x),
			    (circle.y-event->xbutton.y));
      active = TRUE;
      drawcircle(w);
    }
    sprintf(infotext, "%d,%d : %d,%d,%d", event->xmotion.x, event->xmotion.y,
	    circle.x, circle.y, circle.r);
    changeinfotext(infotext);
    break;
  case POLYGON:
    if (polygon_motionline.active == TRUE) {
      erase_polygon_motionline(w);
      polygon_motionline.x = event->xmotion.x;
      polygon_motionline.y = event->xmotion.y;
      active = TRUE;
      drawpolygon(w);
      XDrawLine(XtDisplay(w), XtWindow(w), defgc, polygon_motionline.x,
		polygon_motionline.y, polygon[polcount-1].x,
		polygon[polcount-1].y);
      XFlush(XtDisplay(w));
    }
    sprintf(infotext, "%d,%d : %d,%d - %d,%d", event->xmotion.x, event->xmotion.y,
	    polygon[0].x, polygon[0].y, polygon[polcount-1].x,
	    polygon[polcount-1].y);
    changeinfotext(infotext);
    break;
  }
}

void event_handler(Widget w, XtPointer client_data, XEvent *event,
		   Boolean *ctd)
{
  if (event->type==SelectionClear) {
    active = FALSE;
    switch(currentshape) {
    case RECTANGLE:
      drawrectangle(w);
      break;
    case CIRCLE:
      drawcircle(w);
      break;
    case POLYGON:
      drawpolygon(w);
      polcount = 0;
      polygon_motionline.active = FALSE;
      break;
    }
  }
  if (event->type==SelectionRequest) {
    char position[MAX_AREA_TEXT_LEN];
    int i;
    XEvent sendevent;
    insert(position);
    sendevent.type = SelectionNotify;
    sendevent.xselection.display = XtDisplay(w);
    sendevent.xselection.requestor = event->xselectionrequest.requestor;
    sendevent.xselection.selection = event->xselectionrequest.selection;
    sendevent.xselection.target = event->xselectionrequest.target;
    sendevent.xselection.property = event->xselectionrequest.property;
    sendevent.xselection.time = event->xselectionrequest.time;
    XChangeProperty(XtDisplay(root),
		    event->xselectionrequest.requestor,
		    event->xselectionrequest.property,
		    event->xselectionrequest.target,
		    8,
		    PropModeReplace,
		    position,
		    strlen(position));
    XSendEvent(XtDisplay(root),
	       event->xselectionrequest.requestor,
               True,
               0,
               &sendevent);
    XFlush(XtDisplay(root));
  }
}

void printrectangle(char *str)
{
    int x1, x2, y1, y2, i;

    x1 = min(rectangle.x1, rectangle.x2);
    y1 = min(rectangle.y1, rectangle.y2);
    x2 = max(rectangle.x1, rectangle.x2);
    y2 = max(rectangle.y1, rectangle.y2);

	if (outputHtmlInLowerCase) {
    	sprintf(str,
				"<area shape=\"rect\" coords=\"%d,%d,%d,%d\" href=\"\">",
				x1, y1, x2, y2);
    }
	else {
    	sprintf(str,
				"<AREA SHAPE=\"RECT\" COORDS=\"%d,%d,%d,%d\" HREF=\"\">",
				x1, y1, x2, y2);
    }
}

void printcircle(char *str)
{
	if (outputHtmlInLowerCase) {
			sprintf(str, "<area shape=\"circle\" coords=\"%d,%d,%d\" href=\"\">", circle.x, circle.y, circle.r);
	}	
	else {	
			sprintf(str, "<AREA SHAPE=\"CIRCLE\" COORDS=\"%d,%d,%d\" HREF=\"\">", circle.x, circle.y, circle.r);
	}
}

void printpolygon(char *str) 
{
    char temp[255];
    int i;

	if (outputHtmlInLowerCase) {
		sprintf(str, "<area shape=\"polygon\" coords=\"");
	}
	else {
		sprintf(str, "<AREA SHAPE=\"POLYGON\" COORDS=\"");
	}
    for (i=0;i<polcount;i++) {
      sprintf(temp,"%d,%d,", polygon[i].x, polygon[i].y);
      strcat(str, temp);
      if (strlen(str)>1020) break;
    }
	if (outputHtmlInLowerCase) {
		strcpy(str+strlen(str)-1, "\" href=\"\">");
	}
	else {
		strcpy(str+strlen(str)-1, "\" HREF=\"\">");
	}
}

void insert(char *position)
{
  switch(currentshape) {
  case RECTANGLE:
    printrectangle(position);
    break;
  case CIRCLE:
    printcircle(position);
    break;
  case POLYGON:
    printpolygon(position);
  }
} 

void clear(Widget w, XtPointer client_data, XtPointer call_data)
{
	currentarea = 0;
}
void dump(Widget w, XtPointer client_data, XtPointer call_data)
{
	int i;
	/* if the picturefile name is "/tmp/foobar.gif", let the mapname be "foobar.gif" */
	char *mapname = NULL;

	mapname = strrchr(picturefilename,'/');
        if (!mapname) {
		printf("unexpected error\n");
		return;
        }

	/* skip over the "\" */
	mapname++;

	if (outputHtmlInLowerCase) {
		printf("<map name=\"map-%s\">\n",mapname);
	}
	else {
		printf("<MAP NAME=\"map-%s\">\n",mapname);
	}
	for (i=0;i<currentarea;i++) {
		printf("%s\n",areas[i]);
	}
	
	if (outputHtmlInLowerCase) {
		printf("</map>\n");
		printf(HTMLCOMMENT,VERSION);
		printf("<img src=\"%s\" usemap=\"#map-%s\" alt=\"map-%s\" border=0>\n",mapname,mapname,mapname);
	}
	else {
		printf("</MAP>\n");
		printf(HTMLCOMMENT,VERSION);
		printf("<IMG SRC=\"%s\" USEMAP=\"#map-%s\" ALT=\"map-%s\" BORDER=0>\n",mapname,mapname,mapname);
	}
	printf("\n\n\n");
}

void quit(Widget w, XtPointer client_data, XtPointer call_data)
{
  XtPopdown(picturewindow);
  XFreePixmap(XtDisplay(root), pixmap);
  XtDestroyWidget(root);
  exit(0);
}

void shapeselect(Widget w, XtPointer call_data, XtPointer client_data)
{
  switch(nextshape) {
  case RECTANGLE:
    XtVaSetValues(vrectangle, XtNlabel, RECTANGLE_MENU_ITEM_UNSELECTED, NULL);
    break;
  case CIRCLE:
    XtVaSetValues(vcircle, XtNlabel, CIRCLE_MENU_ITEM_UNSELECTED, NULL);
    break;
  case POLYGON:
    XtVaSetValues(vpolygon, XtNlabel, POLYGON_MENU_ITEM_UNSELECTED, NULL);
    break;
  }
  nextshape = (int)call_data;
  switch(nextshape) {
  case RECTANGLE:
    polygon_motionline.active = FALSE;
    XtVaSetValues(vrectangle, XtNlabel, RECTANGLE_MENU_ITEM_SELECTED, NULL);
    break;
  case CIRCLE:
    polygon_motionline.active = FALSE;
    XtVaSetValues(vcircle, XtNlabel, CIRCLE_MENU_ITEM_SELECTED, NULL);
    break;
  case POLYGON:
    if (currentshape != POLYGON) {
      polcount = 0;
      polygon_motionline.active = FALSE;
    }
    XtVaSetValues(vpolygon, XtNlabel, POLYGON_MENU_ITEM_SELECTED, NULL);
    break;
  }
}

void drawcircle(Widget w)
{
  if (active) {
    XDrawArc(XtDisplay(w), XtWindow(w), defgc, circle.x-circle.r,
	     circle.y-circle.r, 2*circle.r, 2*circle.r, 0, 360*64);
  } else {
    int x, y;
    x = circle.x-circle.r; y = circle.y-circle.r;
    if (x<0) x=0; if (y<0) y=0;
    XCopyArea(XtDisplay(w), pixmap, XtWindow(w), defgc, x, y, 2*circle.r+1,
	      2*circle.r+1, x, y);
  }
  XFlush(XtDisplay(w));
}

void drawpolygon(Widget w)
{
  if (active) {
    XDrawLines(XtDisplay(w), XtWindow(w), defgc, polygon, polcount,
	       CoordModeOrigin);
  } else {
    int minx, miny, maxx, maxy, i;
    minx = miny = maxx = maxy = 0;
    for (i=0;i<polcount;i++) {
      if (polygon[i].x<minx) minx = polygon[i].x;
      if (polygon[i].y<miny) miny = polygon[i].y;
      if (polygon[i].x>maxx) maxx = polygon[i].x;
      if (polygon[i].y>maxy) maxy = polygon[i].y;
    }
    XCopyArea(XtDisplay(w), pixmap, XtWindow(w), defgc, minx, miny,
	      maxx-minx+1, maxy-miny+1, minx, miny);
  }
  XFlush(XtDisplay(w));
}

void drawrectangle(Widget w)
{
  int x1, x2, y1, y2;
  x1 = min(rectangle.x1, rectangle.x2);
  y1 = min(rectangle.y1, rectangle.y2);
  x2 = max(rectangle.x1, rectangle.x2);
  y2 = max(rectangle.y1, rectangle.y2);
  if (active) {
    XDrawRectangle(XtDisplay(w), XtWindow(w), defgc, x1, y1, x2-x1, y2-y1);
  } else {
    GC gc;
    gc = XCreateGC(XtDisplay(w), XtWindow(w), 0, NULL);
     if (x1<0) x1=0; if (y1<0) y1=0;
    XCopyArea(XtDisplay(w), pixmap, XtWindow(w), gc, x1, y1, x2-x1+1, y2-y1+1,
	      x1, y1);
    XFreeGC(XtDisplay(w), gc);
  }
  XFlush(XtDisplay(w));
}

void erase_polygon_motionline(Widget w)
{
  int x1, y1, x2, y2;
  x1 = min(polygon[polcount-1].x, polygon_motionline.x);
  y1 = min(polygon[polcount-1].y, polygon_motionline.y);
  x2 = max(polygon[polcount-1].x, polygon_motionline.x);
  y2 = max(polygon[polcount-1].y, polygon_motionline.y);
  XCopyArea(XtDisplay(w), pixmap, XtWindow(w), defgc, x1, y1, x2-x1+1,
	    y2-y1+1, x1, y1);
  XFlush(XtDisplay(w));
}












