
/*
 * Graphics MARKERS.
 * --------------------
 * A marker is an active graphics object displayed on top of a drawing to
 * mark, annotate, or outline a region.  Markers can respond to events and
 * move, resize, or modify themselves, optionally executing callback
 * procedures when the marker changes state.  Markers are used to
 * interactively define regions with the mouse, to provide a dynamic graphical
 * display which doesn't interfere with the underlying graphics frame, or as a
 * graphical means of command input, using callbacks to perform some operation
 * when the marker is moved or resized by the user.
 * 
 *	       GtMarkerInit (w)
 *	       GtMarkerFree (w)
 *
 *	      gm = GmCreate (w, type, interactive)
 *		GmRedisplay (w, region|NULL)
 *		gm = GmCopy (gm)
 *		  GmDestroy (gm)
 *	      GmAddCallback (gm, events, func, client_data)
 *	   GmDeleteCallback (gm, func, client_data)
 *	      gm = GmSelect (gt, x, y, &what)
 *
 *		  GmMarkpos (gm)
 *		   GmRedraw (gm, func, erase)
 *		    GmRaise (gm, ref_gm|NULL)
 *		    GmLower (gm, ref_gm|NULL)
 *		   GmNotify (gm, events, event, param, nparams)
 *
 *		    GmAddPt (gm, x, y)
 *		 GmDeletePt (gm, x, y)
 *		   GmMovePt (gm, x, y)
 *		     GmMove (gm, x, y)
 *		   GmResize (gm, x, y)
 *		   GmRotate (gm, x, y)
 *
 *          GmSetAttributes (gm, args, nargs, type)
 *          GmGetAttributes (gm, args, nargs, type)
 *	     GmSetAttribute (gm, attribute, value, type)
 *	     GmGetAttribute (gm, attribute, value, type)
 *	      GmSetVertices (gm, points, first, npts)
 *     npts = GmGetVertices (gm, points, first, maxpts)
 *	   GmGetBoundingBox (gm, x, y, width, height)
 *
 *	 type = GmStrToType (marker_type)
 *     event = GmStrToEvent (event_type)
 *   func = GmStrToFunction (drawing_function)
 *
 * Markers operate in screen coordinates (raster 0).  The SelectRaster
 * and MapVector routines may be used to convert to and from raster
 * coordinates if desired.
 *
 *  raster = GtSelectRaster (gt, dras, dt, dx, dy, rt, &rx, &ry, &mp)
 *              GtMapVector (gt, mp, dir, st, sv, dt, dv, npts, clip)
 *
 * The Gm procedures above implement the main functionality of markers.  User
 * interaction is provided at a higher level using action procedures which
 * are bound to pointer and keyboard events via translations (or by the GUI
 * itself directly calling the above procedures).
 */

static void gm_text_init(), gm_line_init(), gm_plin_init(), gm_rect_init();
static void gm_boxx_init(), gm_circ_init(), gm_elip_init(), gm_pgon_init();
static int gm_putint(), gm_putfloat(), gm_do_callbacks();
static void gm_constraint();
static int gm_getint(), gm_getattribute(), gm_gettype();
static double gm_getfloat();
static char *gm_getstring();

static void gm_markpos(), gm_erase(), gm_redraw(), gm_setCurRect();
static void gm_linkafter(), gm_unlink();
static double gm_niceAngle();
static Pixel gm_getpixel();
static int gm_select();
static int gm_getfillstyle();

static GmVMethod gm_classinit[] = {
    gm_text_init, gm_line_init, gm_plin_init, gm_rect_init,
    gm_boxx_init, gm_circ_init, gm_elip_init, gm_pgon_init
};

static Region null_region;
static XRectangle null_rect = { 0, 0, 0, 0 };
#define NullRect(r)	(!(r)->width || !(r)->height)

#define PI_2		1.57079632679489661923
#define PI_4		0.78539816339744830962
#define BORDER		5

static void M_create(), M_destroy(), M_destroyNull(), M_set(), M_raise();
static void M_lower(), M_notify(), M_markpos(), M_markposAdd(), M_redraw();
static void M_addPt(), M_deletePt(), M_movePt(), M_deleteDestroy();
static void M_move(), M_resize(), M_moveResize(), M_rotate();
static void M_rotateResize(), M_input();
static void gm_focusin(), gm_focusout();
static int point_in_poly ();

static XtActionsRec markerActionsList[] = {
	{ "m_create",		M_create },
	{ "m_destroy",		M_destroy },
	{ "m_destroyNull",	M_destroyNull },
	{ "m_set",		M_set },
	{ "m_raise",		M_raise },
	{ "m_lower",		M_lower },
	{ "m_notify",		M_notify },
	{ "m_input",		M_input },
	{ "m_markpos",		M_markpos },
	{ "m_markposAdd",	M_markposAdd },
	{ "m_redraw",		M_redraw },
	{ "m_addPt",		M_addPt },
	{ "m_deletePt",		M_deletePt },
	{ "m_movePt",		M_movePt },
	{ "m_deleteDestroy",	M_deleteDestroy },
	{ "m_move",		M_move },
	{ "m_resize",		M_resize },
	{ "m_moveResize",	M_moveResize },
	{ "m_rotate",		M_rotate },
	{ "m_rotateResize",	M_rotateResize },
};


/* GtMarkerInit -- Initialize the marker subsystem.
 */
void
GtMarkerInit (w)
    GtermWidget w;
{
    register Marker gm, prev;
    XColor fg_color, bg_color;
    Display *display = w->gterm.display;
    int type, i;
    GC gc;

    for (gm = w->gterm.gm_tail;  gm;  gm = prev) {
	prev = gm->prev;
        GmDestroy (gm);
    }

    if (!w->gterm.gm_initialized) {
	/* Register some additional actions for markers. */
	XtAppAddActions (XtWidgetToApplicationContext((Widget)w),
	    markerActionsList, XtNumber(markerActionsList));

	/* Get the gterm widget translations. */
	if ((char *)w->gterm.defTranslations == NULL) {
	    char *translations = NULL;
	    XtTranslations tt;
	    XtResource r;
	    int ttype, i;

	    r.resource_name   = XtNtranslations;
	    r.resource_class  = XtCTranslations;
	    r.resource_type   = XtRString;
	    r.resource_size   = sizeof (char *);
	    r.resource_offset = 0;
	    r.default_type    = XtRString;
	    r.default_addr    = (caddr_t) NULL;

	    XtGetApplicationResources ((Widget)w, &translations, &r, 1,NULL,0);

	    if (translations) {
		if (strncmp (translations, "#augment", 8) == 0)
		    ttype = T_augment;
		else if (strncmp (translations, "#override", 9) == 0)
		    ttype = T_override;
		else
		    ttype = T_replace;

		if (ttype == T_replace) {
		    w->gterm.defTranslations =
			XtParseTranslationTable (translations);
		} else if ((i = w->gterm.nauxTrans) < MAX_AUXTRANS) {
		    w->gterm.defTranslations =
			XtParseTranslationTable (defaultGtermTranslations);
		    w->gterm.auxTrans[i] =
			XtParseTranslationTable (translations);
		    w->gterm.auxTType[i] = ttype;
		    w->gterm.nauxTrans++;
		}

	    } else {
		w->gterm.defTranslations =
		    XtParseTranslationTable (defaultGtermTranslations);
	    }

	    /* Get the marker translations. */
	    if ((char *)w->gterm.gm_defTranslations == NULL)
		w->gterm.gm_defTranslations =
		    XtParseTranslationTable (w->gterm.gm_translations);
	}

	/* Cancel any load translation table interval timer. */
	if (w->gterm.gm_timer_id) {
	    XtRemoveTimeOut (w->gterm.gm_timer_id);
	    w->gterm.gm_timer_id = (XtIntervalId) NULL;
	}

	/* Set the default gterm window translations. */
	gm_load_translations (w, NULL);

	/* Get graphics drawing GC. */
/*	gc = XCreateGC (display, w->gterm.root, 0, NULL); */
	gc = XCreateGC (display, w->gterm.window, 0, NULL);
	XSetBackground (display, gc, w->gterm.color0);
	XSetForeground (display, gc, w->gterm.color1);
	XSetLineAttributes (display, gc,
	    w->gterm.gm_lineWidth, w->gterm.gm_lineStyle, CapButt, JoinMiter);
	w->gterm.gm_drawGC = gc;

	/* Get graphics rubber-band GC. */
/*	gc = XCreateGC (display, w->gterm.root, 0, NULL); */
	gc = XCreateGC (display, w->gterm.window, 0, NULL);
	XSetFunction (display, gc, GXxor);
	XSetFillStyle (display, gc, FillSolid);
	XSetForeground (display, gc, w->gterm.gm_xorFillColor);
	XSetBackground (display, gc, w->gterm.gm_xorFillBgColor);
	XSetLineAttributes (display, gc,
	    0, LineDoubleDash, CapButt, JoinMiter);
	w->gterm.gm_rubberGC = gc;

	fg_color.pixel = w->gterm.gm_cursorFgColor;
	bg_color.pixel = w->gterm.gm_cursorBgColor;
	XQueryColor (display, w->core.colormap, &fg_color);
	XQueryColor (display, w->core.colormap, &bg_color);

	w->gterm.gm_markerCursor = XCreateFontCursor (display, XC_fleur);
	XRecolorCursor (display, w->gterm.gm_markerCursor, &fg_color,&bg_color);
	w->gterm.gm_edgeCursor = XCreateFontCursor (display, XC_dotbox);
	XRecolorCursor (display, w->gterm.gm_edgeCursor, &fg_color,&bg_color);
	w->gterm.gm_pointCursor = XCreateFontCursor (display, XC_sizing);
	XRecolorCursor (display, w->gterm.gm_pointCursor, &fg_color,&bg_color);

	if (!(type = GmStrToType (w->gterm.gm_defaultMarker)))
	    type = Gm_Rectangle;
	w->gterm.gm_defaultType = type;

	if (!null_region)
	    null_region = XCreateRegion();
	w->gterm.gm_initialized++;
    }

    w->gterm.gm_create = NULL;
    w->gterm.gm_active = NULL;
    w->gterm.gm_redisplay = False;
    w->gterm.preserve_screen = 0;
}


/* GtMarkerFree -- Free any marker subsystem resources.
 */
static void
GtMarkerFree (w)
    register GtermWidget w;
{
    register Display *display = w->gterm.display;
    register Marker gm;

    /* Cancel any load translation table interval timer. */
    if (w->gterm.gm_timer_id) {
	XtRemoveTimeOut (w->gterm.gm_timer_id);
	w->gterm.gm_timer_id = (XtIntervalId) NULL;
    }

    /* Set the default gterm window translations. */
    gm_load_translations (w, NULL);

    for (gm = w->gterm.gm_tail;  gm;  gm = gm->prev)
        GmDestroy (gm);

    if (!w->gterm.gm_initialized)
	return;

    XFreeGC (display, w->gterm.gm_drawGC);
    XFreeGC (display, w->gterm.gm_rubberGC);

    /* This call can fail - see comments elsewhere in this file about
     * XFreeCursor.
     *
    XFreeCursor (display, w->gterm.gm_markerCursor);
    XFreeCursor (display, w->gterm.gm_edgeCursor);
    XFreeCursor (display, w->gterm.gm_pointCursor);
     */

    w->gterm.gm_initialized = 0;
}


/* gm_focusin -- Called when gterm window input is directed to a marker.
 */
static void
gm_focusin (w, gm, what)
    register GtermWidget w;
    register Marker gm;
    GmSelection what;
{
    Cursor cursor;
    int erase;
    Marker am;

    if (!w || !XtIsRealized ((Widget)w))
	return;

    if ((am = w->gterm.gm_active)) {
	if (am != gm)
	    gm_focusout (w, 0);
	else if (what && what->type == w->gterm.gm_selection.type) {
	    /* no change */
	    return;
	}
    }

    if (what) {
	switch (what->type) {
	case Ge_Point:
	    cursor = w->gterm.gm_pointCursor;
	    break;
	case Ge_Edge:
	    cursor = w->gterm.gm_edgeCursor;
	    break;
	default:
	    cursor = w->gterm.gm_markerCursor;
	    break;
	}
    } else
	cursor = w->gterm.gm_markerCursor;

    erase_crosshair (w);
    XDefineCursor (w->gterm.display, w->gterm.window, cursor);
    w->gterm.gm_active = gm;
    w->gterm.gm_selection = *what;

    if (gm && gm != am) {
	gm_request_translations (w, gm);
	GmMarkpos (gm);
	GmRedraw (gm, GXcopy, erase=True);
    }

    gm_do_callbacks (gm, GmEvFocusIn, NULL, NULL, 0);
}


/* gm_focusout -- Called to restore the normal gterm window input when the
 * pointer moves off a marker.
 */
static void
gm_focusout (w, enableSetTrans)
    register GtermWidget w;
    int enableSetTrans;			/* replace translations */
{
    register Display *display = w->gterm.display;
    register Marker gm = w->gterm.gm_active;
    int erase, i;

    if (!w || !XtIsRealized ((Widget)w))
	return;

    /* Restore the default gterm window translations. */
    if (enableSetTrans)
	gm_request_translations (w, NULL);

    XDefineCursor (display, w->gterm.window, w->gterm.cursor);
    w->gterm.gm_active = NULL;

    if (gm) {
	GmMarkpos (gm);
	GmRedraw (gm, GXcopy, erase=True);
    }

    gm_do_callbacks (gm, GmEvFocusOut, NULL, NULL, 0);
}


/* gm_refocus -- Simulate a pointer event to recompute the marker pointer
 * focus.  Called when a software event changes the marker stacking order
 * in some way.
 */
static void 
gm_refocus (w)
    GtermWidget w;
{
    XMotionEvent event;
    int nparams = 0;

    event.type = MotionNotify;					/* MF009 */
    event.x = w->gterm.last_x;
    event.y = w->gterm.last_y;
    HandleTrackCursor ((Widget)w, &event, NULL, &nparams);
}


/* 
 * Translation tables.  The widget's translation table must not be replaced
 * while a translation is executing.  This can be a problem as it is often
 * events and their translations which lead to the translation table getting
 * replaced.  To avoid this problem we merely queue a timer event to load
 * the desired translation table, allowing any existing translation to
 * finish executing before the translation table is swapped out.  If multiple
 * translation table load requests are issued only the final one has any
 * effect.
 */

/* gm_request_translations -- Queue a request to load the translations for the
 * specified marker (or NULL to load the default gterm translations).  If this
 * is the first request and timers are enabled a timer is posted to load the
 * translations when any current event processing is complete.  If a request
 * is already active then the most recent request supercedes any previous one.
 */
static void
gm_request_translations (w, gm)
    register GtermWidget w;
    Marker gm;
{
    w->gterm.gm_reqTranslations = gm;

    if (!w->gterm.useTimers)
	gm_load_translations (w, NULL);
    else if (!w->gterm.gm_timer_id) {
	w->gterm.gm_timer_id =
	    XtAppAddTimeOut (XtWidgetToApplicationContext((Widget)w), 0,
		gm_load_translations, (XtPointer)w);
    }
}


/* gm_load_translations -- Swap out the widget's translation table.  This is
 * a no-op if the requested translation table is already loaded.
 */
static void
gm_load_translations (w, id)
    register GtermWidget w;
    XtIntervalId id;
{
    register Marker am, gm;
    register int i;

    w->gterm.gm_timer_id = (XtIntervalId) NULL;

    am = w->gterm.gm_curTranslations;
    gm = w->gterm.gm_reqTranslations;
    if (am == gm && w->gterm.gm_initialized)
	return;

    if (gm) {
	/* Set the translations for the indicated marker. */
	if (!am || am->translations != gm->translations)
	    XtOverrideTranslations ((Widget)w, gm->translations);
    } else {
	/* Restore the default gterm window translations. */
	XtVaSetValues ((Widget)w,
	    XtNtranslations, (XtArgVal)w->gterm.defTranslations, NULL);
	for (i=0;  i < w->gterm.nauxTrans;  i++) {
	    switch (w->gterm.auxTType[i]) {
	    case T_augment:
		XtAugmentTranslations ((Widget)w, w->gterm.auxTrans[i]);
		break;
	    case T_override:
		XtOverrideTranslations ((Widget)w, w->gterm.auxTrans[i]);
		break;
	    }
	}
    }

    w->gterm.gm_curTranslations = w->gterm.gm_reqTranslations;
}


/* Public marker functions.
 * --------------------------
 */

/* GmCreate -- Create a new marker.
 */
XtPointer
GmCreate (w, type, interactive)
    GtermWidget w;
    int type;			/* marker type */
    int interactive;		/* use pointer to set position */
{
    register Marker gm;

    /* Allocate descriptor. */
    if (type < 1 || type > Gm_NTypes)
	return (NULL);
    if (!(gm = (Marker) XtCalloc (1, sizeof (struct marker))))
	return (NULL);

    /* Initialize descriptor. */
    gm->w = w;
    gm->type = type;
    gm->flags = interactive ? (Gm_Visible|Gm_Sensitive) : 0;
    gm->translations = w->gterm.gm_defTranslations;
    gm->old_region = XCreateRegion();
    gm->cur_region = XCreateRegion();
    (gm_classinit[type-1]) (gm, interactive);

    /* Link marker to tail of marker list. */
    gm_linkafter (gm, w->gterm.gm_tail);

    /* If marker is being created interactive, set flag to indicate that the
     * next create marker event should finish creating this marker.
     */
    if (w->gterm.gm_create)
	GmDestroy (w->gterm.gm_create);
    w->gterm.gm_create = interactive ? gm : NULL;

    return (gm);
}


/* GmDestroy -- Destroy a marker.
 */
int
GmDestroy (gm)
    register Marker gm;
{
    register GtermWidget w = gm->w;
    Region old_region, cur_region;

    /* GmDestroy can be called recursively during a destroy operation as a
     * side effect of the destroy callback.  Set the Gm_BeingDestroyed flag
     * to cause these redundant destroy requests to be ignored.
     */
    if (gm->flags & Gm_BeingDestroyed)
	return (OK);
    gm->flags |= Gm_BeingDestroyed;

    /* Release the focus if active marker.   This should be done before
     * proceeding to destroy the marker, i.e. before calling the destroy
     * callbacks.
     */
    if (w->gterm.gm_active == gm) {
	gm_focusout (w, 1);
	w->gterm.gm_active = NULL;
    }

    /* Inform any clients that have registered a callback for this marker
     * that we are about to destroy the marker.
     */
    gm_do_callbacks (gm, GmEvDestroy, NULL, NULL, 0);

    /* Erase the marker from the screen. */
    GmMarkpos (gm);
    gm_erase (gm);

    /* Note marker position. */
    old_region = gm->old_region;
    cur_region = gm->cur_region;

    /* Free all storage and unlink the marker. */
    if (gm->npoints > GM_MAXVERTICES)
	XtFree ((char *)gm->points);
    if (gm->text)
	XtFree ((char *)gm->text);
    if (gm->pgon)
	XtFree ((char *)gm->pgon);

    gm_unlink (gm);
    XtFree ((char *)gm);

    /* Redraw any markers that were obscured by the deleted marker. */
    update_transients (w, old_region);

    XDestroyRegion (old_region);
    XDestroyRegion (cur_region);

    /* Recompute the marker focus. */
    gm_refocus (w);

    return (OK);
}


/* GmCopy -- Copy a marker.
 */
Marker
GmCopy (gm)
    register Marker gm;
{
    register GtermWidget w = gm->w;
    register Marker nm;

    if (!(nm = (Marker) XtCalloc (1, sizeof (struct marker))))
	return (NULL);

    *nm = *gm;
    nm->parent = gm;
    nm->old_region = NULL;
    nm->cur_region = NULL;
    nm->points = NULL;
    nm->pgon = NULL;
    nm->text = NULL;

    /* Copy region descriptors. */
    if ((char *)(nm->old_region = XCreateRegion()) == NULL)
	goto fail;
    if ((char *)(nm->cur_region = XCreateRegion()) == NULL)
	goto fail;
    XUnionRegion (nm->old_region, gm->cur_region, nm->cur_region);

    /* Copy any polypoint data. */
    if (gm->pgon) {
	nm->pgon = (DPoint *) XtMalloc (gm->npoints * sizeof(DPoint));
	if (nm->pgon == NULL)
	    goto fail;
	memmove (nm->pgon, gm->pgon, gm->npoints * sizeof(DPoint));
    }

    /* Copy region polygon. */
    if (gm->npoints > GM_MAXVERTICES) {
	if (!(nm->points = (XPoint *) XtMalloc (gm->npoints * sizeof(XPoint))))
	    goto fail;
	memmove (nm->points, gm->points, gm->npoints * sizeof(XPoint));
    }

    /* Copy any text data. */
    if (gm->text) {
	int nchars = strlen (gm->text);
	if (!(nm->text = XtMalloc (nchars + 1)))
	    goto fail;
	memmove (nm->text, gm->text, nchars + 1);
    }

    gm_linkafter (nm, w->gterm.gm_tail);
    return (nm);

fail:
    if (nm->text)
	XtFree (nm->text);
    if (nm->pgon)
	XtFree ((char *)nm->pgon);
    if (nm->points && nm->points != nm->point_data)
	XtFree ((char *)nm->points);
    if ((char *)nm->cur_region)
	XDestroyRegion (nm->cur_region);
    if ((char *)nm->old_region)
	XDestroyRegion (nm->old_region);

    XtFree ((char *)nm);
    return (NULL);
}


/* GmAddCallback -- Add a callback to a marker.
 */
void
GmAddCallback (gm, events, func, client_data)
    register Marker gm;
    int events;			/* events callback is to receive */
    GmIMethod func;		/* function to be called */
    XtPointer client_data;	/* client data for above */
{
    register struct markerCallback *cb;
    register int i;

    /* Find an empty callback slot. */
    for (i=0;  i < GM_MAXCALLBACKS;  i++)
	if (!gm->callback[i].events)
	    break;

    /* Register the callback. */
    if (i < GM_MAXCALLBACKS) {
	cb = &gm->callback[i];
	cb->events = events;
	cb->func = func;
	cb->client_data = client_data;
	gm->ncallbacks = max (gm->ncallbacks, i + 1);
    }

    if (events & GmEvConstraint)
	gm->constraints++;
}


/* GmDeleteCallback -- Delete a previously posted callback given the
 * function pointer and client data passed when the callback was registered.
 */
void
GmDeleteCallback (gm, func, client_data)
    register Marker gm;
    GmIMethod func;		/* callback function */
    XtPointer client_data;	/* client data for above */
{
    register struct markerCallback *cb;
    register int i, n;

    for (i=n=0;  i < GM_MAXCALLBACKS;  i++) {
	cb = &gm->callback[i];

	if (cb->func == func && cb->client_data == client_data) {
	    if (cb->events & GmEvConstraint)
		gm->constraints--;
	    cb->events = 0;
	    cb->func = (GmIMethod)NULL;
	    cb->client_data = (XtPointer)NULL;
	} else if (cb->events)
	    n = i;
    }

    gm->ncallbacks = n + 1;
}


/* GmSelect -- Scan the marker list to see if the given pointer coordinates
 * are within an active marker.  If so, the marker descriptor is returned as
 * the function value, and the "what" argument is set to indicate what part
 * of the marker was selected.
 */
Marker
GmSelect (w, x, y, what)
    GtermWidget w;
    int x, y;
    GmSelection what;
{
    register int flags = (Gm_Activated|Gm_Visible|Gm_Sensitive);
    register XRectangle *r;
    register Marker gm;

    for (gm = w->gterm.gm_tail;  gm;  gm = gm->prev) {
	if (!((gm->flags & (flags|Gm_BeingDestroyed)) == flags))
	    continue;
	r = &gm->cur_rect;
	if (x < (int)r->x || x >= (int)(r->x + r->width) ||
	    y < (int)r->y || y >= (int)(r->y + r->height))
	    continue;
	if (gm->select (gm, x, y, what))
	    return (gm);
    }

    return (NULL);
}


/* GmMarkpos -- Save the current marker position, e.g., prior to modifying
 * the marker.  This is used to erase the old marker when the modified
 * marker is later redrawn.
 */
void
GmMarkpos (gm)
    register Marker gm;
{
    gm->markpos (gm);
}


/* GmRedraw -- Redraw a marker using the given drawing function.  If the erase
 * flag is not set (as when in rubber-band mode) the marker is merely drawn
 * to the screen.  Otherwise if the old marker position has been saved the
 * old marker is first erased, then any markers affected by the erase are
 * redrawn, and finally the current marker is redrawn at the new location.
 */
void
GmRedraw (gm, func, erase)
    Marker gm;
    int func;
    int erase;
{
    register Marker mm;
    register XRectangle *o, *n, *r;
    int flags = (Gm_Activated|Gm_Visible);
    Region clip_region, temp_region, temp;
    GtermWidget w = gm->w;
    int outside;

    /* Recompute marker polygon if any attributes have changed. */
    gm->update (gm);

    clip_region = XCreateRegion();
    temp_region = XCreateRegion();

    /* Erase the previously marked region (old position). */
    if (erase) {
	XUnionRegion (gm->old_region, clip_region, temp_region);
	temp = clip_region; clip_region = temp_region; temp_region = temp;
	XSetRegion (w->gterm.display, w->gterm.exposeGC, clip_region);
	gm_erase (gm);
    }

    if (!erase && func == GXxor)
	gm->redraw (gm, func);
    else {
	/* Draw the marker and any markers it intersects, clipping to the
	 * new marker region.
	 */
	o = &gm->old_rect;
	n = &gm->cur_rect;

	XUnionRegion (gm->cur_region, clip_region, temp_region);
	temp = clip_region; clip_region = temp_region; temp_region = temp;
	XSetRegion (w->gterm.display, w->gterm.gm_drawGC, clip_region);

	for (mm = gm->w->gterm.gm_head;  mm;  mm = mm->next) {
	    if (!((mm->flags & flags) == flags))
		continue;

	    /* Redraw a marker if it intersects either the old rect or the
	     * new rect.
	     */
	    if (mm != gm) {
		r = &mm->cur_rect;
		outside = 0;
		if ((int)r->x >= (int)o->x + (int)o->width  ||
			(int)r->x + (int)r->width <= (int)o->x ||
		    (int)r->y >= (int)o->y + (int)o->height ||
			(int)r->y + (int)r->height <= (int)o->y)
		    outside++;
		if ((int)r->x >= (int)n->x + (int)n->width  ||
			(int)r->x + (int)r->width  <= (int)n->x ||
		    (int)r->y >= (int)n->y + (int)n->height ||
			(int)r->y + (int)r->height <= (int)n->y)
		    outside++;
		if (outside == 2)
		    continue;
	    }
	    mm->redraw (mm, func);
	}

	XSetClipMask (w->gterm.display, w->gterm.gm_drawGC, None);
    }

    if (erase)
	XSetClipMask (w->gterm.display, w->gterm.exposeGC, None);
    XDestroyRegion (clip_region);
    XDestroyRegion (temp_region);

    if (func != GXxor && gm->width > 0 && gm->height > 0) {
	/* Redraw callback. */
	gm_do_callbacks (gm, GmEvRedraw, NULL, NULL, 0);

	/* Generate moveResize callback, if marker was moved or resized.
	 */
	if (gm->old_rect.x != gm->cur_rect.x ||
	    gm->old_rect.y != gm->cur_rect.y ||
	    gm->old_rect.width != gm->cur_rect.width ||
	    gm->old_rect.height != gm->cur_rect.height) {

	    char x[32], y[32];
	    char width[32], height[32];
	    char *argv[5];
	    int argc;

	    /* If the marker was just created (old_rect null) or the marker
	     * moved and we did a full erase and redraw, any old markpos is
	     * obsolete so we may as well update the saved position.
	     */
	    if (erase || !gm->old_rect.width || !gm->old_rect.height)
		GmMarkpos (gm);

	    sprintf (x, "%d", gm->x);
	    sprintf (y, "%d", gm->y);
	    sprintf (width, "%d", gm->width);
	    sprintf (height, "%d", gm->height);
	    argv[0] = x;
	    argv[1] = y;
	    argv[2] = width;
	    argv[3] = height;
	    argv[4] = NULL;
	    argc = 4;

	    gm_do_callbacks (gm, GmEvMoveResize, NULL, argv, argc);
	}
    }
}


/* GmRedisplay -- Redisplay the markers in the given region, or redisplay
 * the entire window if the region is given as (char *)NULL.
 */
void
GmRedisplay (w, region)
    GtermWidget w;
    Region region;
{
    register int flags = (Gm_Activated|Gm_Visible);
    register XRectangle *r;
    register Marker gm;

    if (!w || !XtIsRealized ((Widget)w))
	return;

    /* Set the clip mask to only draw in the affected region. */
    if (region)
	XSetRegion (w->gterm.display, w->gterm.gm_drawGC, region);

    /* Draw all markers that intersect the target region. */
    for (gm = w->gterm.gm_head;  gm;  gm = gm->next) {
	if (!((gm->flags & flags) == flags))
	    continue;

	if ((char *)region) {
	    gm->update (gm);
	    r = &gm->cur_rect;
	    if (XRectInRegion (region,
		r->x, r->y, r->width, r->height) == RectangleOut)
		continue;
	}

	gm->redraw (gm, GXcopy);
    }

    XSetClipMask (w->gterm.display, w->gterm.gm_drawGC, None);
    w->gterm.gm_redisplay = False;
}


/* GmRaise -- Change the stacking order of a marker relative to another
 * marker, causing the first marker to be drawn above the second.
 */
void
GmRaise (gm, ref_gm)
    register Marker gm, ref_gm;
{
    register GtermWidget w = gm->w;
    int erase;

    /* Already on top? */
    if (gm == w->gterm.gm_tail || ref_gm && ref_gm->next == gm)
	return;

    /* Raise it. */
    gm_unlink (gm);
    gm_linkafter (gm, ref_gm ? ref_gm : w->gterm.gm_tail);

    GmMarkpos (gm);
    GmRedraw (gm, GXcopy, erase=True);
    gm_refocus (w);
}


/* GmLower -- Change the stacking order of a marker relative to another
 * marker, causing the first marker to be drawn below the second.
 */
void
GmLower (gm, ref_gm)
    register Marker gm, ref_gm;
{
    register GtermWidget w = gm->w;
    int erase;

    /* Already lowered? */
    if (gm == w->gterm.gm_head || ref_gm && ref_gm->prev == gm)
	return;

    /* Lower it. */
    gm_unlink (gm);
    if (ref_gm && ref_gm->prev)
	gm_linkafter (gm, ref_gm->prev);
    else {
	gm->next = w->gterm.gm_head;
	w->gterm.gm_head = gm;
	if (gm->next)
	    gm->next->prev = gm;
	if (!w->gterm.gm_tail)
	    w->gterm.gm_tail = gm;
    }

    GmMarkpos (gm);
    GmRedraw (gm, GXcopy, erase=True);
    gm_refocus (w);
}


/* GmNotify -- Notify any clients that have registered callbacks that the
 * given marker events have occurred.
 */
void
GmNotify (gm, events, event, params, nparams)
    register Marker gm;
    int events;
    XEvent *event;
    String *params;
    Cardinal nparams;
{
    gm_do_callbacks (gm, events, event, params, nparams);
}


/* GmAddPt -- Add a point to a marker.
 */
void
GmAddPt (gm, x, y)
    register Marker gm;
    int x, y;
{
    int erase;
    if (gm->addPt) {
	GmRedraw (gm, GXxor, erase=False);
	gm->addPt (gm, x, y);
	GmRedraw (gm, GXxor, erase=False);
	gm_refocus (gm->w);
    }
}


/* GmDeletePt -- Delete a point from a marker.
 */
void
GmDeletePt (gm, x, y)
    register Marker gm;
    int x, y;
{
    int erase;
    if (gm->deletePt) {
	GmMarkpos (gm);
	gm->deletePt (gm, x, y);
	GmRedraw (gm, GXcopy, erase=True);
	gm_refocus (gm->w);
    }
}


/* GmMovePt -- Move a point within a marker.
 */
void
GmMovePt (gm, x, y)
    register Marker gm;
    int x, y;
{
    int erase;
    if (gm->movePt) {
	GmRedraw (gm, GXxor, erase=False);
	gm->movePt (gm, x, y);
	GmRedraw (gm, GXxor, erase=False);
    }
}


/* GmMove -- Move a marker.
 */
void
GmMove (gm, x, y)
    register Marker gm;
    int x, y;
{
    int erase;
    if (gm->move) {
	GmRedraw (gm, GXxor, erase=False);
	gm->move (gm, x, y);
	GmRedraw (gm, GXxor, erase=False);
    }
}


/* GmResize -- Resize a marker.
 */
void
GmResize (gm, x, y)
    register Marker gm;
    int x, y;
{
    int erase;
    if (gm->resize) {
	GmRedraw (gm, GXxor, erase=False);
	gm->resize (gm, x, y);
	GmRedraw (gm, GXxor, erase=False);
    }
}


/* GmRotate -- Rotate a marker.
 */
void
GmRotate (gm, x, y)
    register Marker gm;
    int x, y;
{
    int erase;
    if (gm->rotate) {
	GmRedraw (gm, GXxor, erase=False);
	gm->rotate (gm, x, y);
	GmRedraw (gm, GXxor, erase=False);
    }
}


/* GmSetAttributes -- Set a list of attributes.  Requires that all attribute
 * values be specified in the same type.  Autoredraw, if enabled, is suspended
 * until all attributes have been changed.
 */
int
GmSetAttributes (gm, args, nargs, argtype)
    register Marker gm;
    ArgList args;
    int nargs;
    char *argtype;
{
    register int i;
    int autoredraw, erase;
    int status = OK;

    if ((autoredraw = (gm->flags & Gm_AutoRedraw))) {
	gm->flags &= ~Gm_AutoRedraw;
	GmMarkpos (gm);
    }
    
    for (i=0;  i < nargs;  i++) {
	status |= GmSetAttribute (gm, args[i].name, args[i].value, argtype);
	if (strcmp (args[i].name, GmAutoRedraw) == 0)
	    autoredraw = gm_getint (args[i].value, argtype);
    }

    if (autoredraw) {
	gm->flags |= Gm_AutoRedraw;
	GmRedraw (gm, GXcopy, erase=True);
    }

    return (status ? ERR : OK);
}


/* GmSetAttribute -- Set the value of a marker attribute.
 */
int
GmSetAttribute (gm, attribute, value, type)
    register Marker gm;
    char *attribute;
    XtArgVal value;
    char *type;
{
    GtermWidget w = gm->w;
    int marker_type, atType;
    int erase, n, i;

    if (gm->flags & Gm_AutoRedraw)
	GmMarkpos (gm);

    switch (atType = gm_getattribute (attribute)) {
    case Ga_Type:
	switch (gm_gettype (type)) {
	case Gt_String:
	    marker_type = GmStrToType ((char *)value);
	    break;
	case Gt_Int:
	    marker_type = gm_getint (value, type);
	    break;
	default:
	    return (ERR);
	}

	marker_type = max(1, min(Gm_NTypes, marker_type));
	(gm_classinit[marker_type-1]) (gm, False);
	gm->flags |= Gm_Modified;
	break;

    case Ga_Activated:
	if (gm_getint (value, type)) {
	    if (!(gm->flags & Gm_Activated)) {
		gm->flags |= Gm_Activated;
		GmRedraw (gm, GXcopy, erase=False);
	    }
	} else {
	    GmMarkpos (gm);
	    gm_erase (gm);
	    gm->flags &= ~Gm_Activated;
	}
	return (OK);

    case Ga_Visible:
	if (gm_getint (value, type)) {
	    if (!(gm->flags & Gm_Visible)) {
		gm->flags |= Gm_Visible;
 		GmRedraw (gm, GXcopy, erase=False); 
	    }
	} else if (gm->flags & Gm_Visible) {
	    GmMarkpos (gm);
	    gm_erase (gm);
	    gm->flags &= ~Gm_Visible;
	}
	return (OK);

    case Ga_Sensitive:
	if (gm_getint (value, type))
	    gm->flags |= Gm_Sensitive;
	else
	    gm->flags &= ~Gm_Sensitive;
	return (OK);

    case Ga_AutoRedraw:
	if (gm_getint (value, type))
	    gm->flags |= Gm_AutoRedraw;
	else
	    gm->flags &= ~Gm_AutoRedraw;
	return (OK);

    case Ga_Translations:
	switch (gm_gettype (type)) {
	case Gt_String:
	    gm->translations = XtParseTranslationTable ((char *)value);
	    break;
	default:
	    return (ERR);
	}
	return (OK);

    case Ga_X:
	gm->x = gm_getint (value, type);
	break;
    case Ga_Y:
	gm->y = gm_getint (value, type);
	break;

    case Ga_Width:
    case Ga_Height:
	/* For a text marker a size can be specified either in integer
	 * pixels or in characters, e.g., "40ch" or "40 chars".
	 */
	if (gm->type == Gm_Text && type == XtRString) {
	    XFontStruct *fp = gm->font;
	    int char_width, char_height;
	    int l_pix, r_pix;
	    char *ip;

	    for (n=0, ip=(char *)value;  *ip && isdigit(*ip);  ip++)
		n = n * 10 + (*ip - '0');

	    while (isspace (*ip))
		ip++;
	    if (ip[0] == 'c' && ip[1] == 'h') {
		char_width  = fp->max_bounds.width;
		char_height = fp->max_bounds.ascent + fp->max_bounds.descent;
		l_pix = (gm->lineWidth + 1) / 2 + gm->textBorder - 1;
		r_pix = (gm->lineWidth + 1) / 2 + gm->textBorder - 1 -
		    (fp->max_bounds.width - fp->max_bounds.rbearing);

		if (atType == Ga_Width)
		    n = n * char_width + l_pix + r_pix;
		else
		    n = n * char_height + l_pix * 2;
	    }
	} else
	    n = gm_getint (value, type);

	if (atType == Ga_Width)
	    gm->width = n;
	else
	    gm->height = n;
	break;

    case Ga_Rotangle:						/* MF022 */
 	gm->rotangle = gm_getfloat (value, type) * (M_PI / (double) 180.0);
	break;

    case Ga_HighlightColor:
	gm->highlightColor = gm_getpixel (w, value, type);
	break;
    case Ga_LineColor:
	gm->lineColor = gm_getpixel (w, value, type);
	break;
    case Ga_LineWidth:
	gm->lineWidth = gm_getint (value, type);
	break;
    case Ga_LineStyle:
	gm->lineStyle = gm_getint (value, type);
	break;

    case Ga_KnotColor:
	gm->knotColor = gm_getpixel (w, value, type);
	break;
    case Ga_KnotSize:
	gm->knotSize = gm_getint (value, type);
	break;

    case Ga_Fill:
	gm->fill = gm_getint (value, type);
	break;
    case Ga_FillColor:
	gm->fillColor = gm_getpixel (w, value, type);
	break;
    case Ga_FillBgColor:
	gm->fillBgColor = gm_getpixel (w, value, type);
	break;
    case Ga_FillStyle:
	switch (gm_gettype (type)) {
	case Gt_String:
	    gm->fillStyle = gm_getfillstyle (w, value, type);
	    break;
	default:
	    break;
	}
	break;
    case Ga_FillPattern:
	switch (gm_gettype (type)) {
	case Gt_Pointer:
	    gm->fillPattern = (Pixmap) (value);
	    break;
	default:
	    return (ERR);
	}
	break;

    case Ga_TextColor:
	gm->textColor = gm_getpixel (w, value, type);
	break;
    case Ga_TextBgColor:
	gm->textBgColor = gm_getpixel (w, value, type);
	break;
    case Ga_TextBorder:
	gm->textBorder = gm_getint (value, type);
	break;
    case Ga_ImageText:
	gm->imageText = gm_getint (value, type);
	break;
    case Ga_Font:
	switch (gm_gettype (type)) {
	case Gt_Int:
	    i = gm_getint (value, type);
	    if (i >= 0 && i < NDialogFonts)
		gm->font = w->gterm.dialog_fonts[i];
	    break;
	case Gt_Pointer:
	    gm->font = (XFontStruct *) (value);
	    break;
	default:
	    return (ERR);
	}
	break;
    case Ga_Text:
	switch (gm_gettype (type)) {
	case Gt_Pointer:
	case Gt_String:
	    if (gm->text)
		XtFree (gm->text);
	    if (!(gm->text = XtMalloc (strlen((char *)value) + 1)))
		return (ERR);
	    strcpy (gm->text, (char *)value);
	    break;
	default:
	    return (ERR);
	}
	break;
    case Ga_RotIndicator:					/* MF020 */
	gm->rotIndicator = gm_getint (value, type);
	break;

    default:
	return (ERR);
    }

    gm->flags |= Gm_Modified;

    if (gm->flags & Gm_AutoRedraw)
	GmRedraw (gm, GXcopy, erase=True);

    /* Notify client that a marker attribute has changed. */
    {   char *argv[2];
	int argc;

	argv[0] = attribute;
	argv[1] = NULL;
	argc = 1;

	gm_do_callbacks (gm, GmEvModify, NULL, argv, argc);
    }

    return (OK);
}


/* GmGetAttributes -- Get a list of attributes.  Requires that all attribute
 * values be specified in the same type.
 */
void
GmGetAttributes (gm, args, nargs, argtype)
    register Marker gm;
    ArgList args;
    int nargs;
    char *argtype;
{
    register int i;

    for (i=0;  i < nargs;  i++)
	GmGetAttribute (gm, args[i].name, args[i].value, argtype);
}


/* GmGetAttribute -- Get the value of a marker attribute.
 */
int
GmGetAttribute (gm, attribute, value, type)
    register Marker gm;
    char *attribute;
    XtArgVal value;
    char *type;
{
    GtermWidget w = gm->w;
    int i;

    switch (gm_getattribute (attribute)) {
    case Ga_Type:
	switch (gm_gettype (type)) {
	case Gt_String:
	    switch (gm->type) {
	    case Gm_Text:
		strcpy ((char *)value, GmText);
		break;
	    case Gm_Line:
		strcpy ((char *)value, GmLine);
		break;
	    case Gm_Polyline:
		strcpy ((char *)value, GmPolyline);
		break;
	    case Gm_Rectangle:
		strcpy ((char *)value, GmRectangle);
		break;
	    case Gm_Box:
		strcpy ((char *)value, GmBox);
		break;
	    case Gm_Circle:
		strcpy ((char *)value, GmCircle);
		break;
	    case Gm_Ellipse:
		strcpy ((char *)value, GmEllipse);
		break;
	    case Gm_Polygon:
		strcpy ((char *)value, GmPolygon);
		break;
	    default:
		return (ERR);
	    }
	    break;
	case Gt_Int:
	    if (gm_putint (gm->type, value, type) == ERR)
		return (ERR);
	    break;
	default:
	    return (ERR);
	}
	break;

    case Ga_Activated:
	if (gm_putint ((gm->flags & Gm_Activated) != 0, value, type) == ERR)
	    return (ERR);
	break;
    case Ga_Visible:
	if (gm_putint ((gm->flags & Gm_Visible) != 0, value, type) == ERR)
	    return (ERR);
	break;
    case Ga_Sensitive:
	if (gm_putint ((gm->flags & Gm_Sensitive) != 0, value, type) == ERR)
	    return (ERR);
	break;
    case Ga_AutoRedraw:
	if (gm_putint ((gm->flags & Gm_AutoRedraw) != 0, value, type) == ERR)
	    return (ERR);
	break;

    case Ga_X:
	if (gm_putint (gm->x, value, type) == ERR)
	    return (ERR);
	break;
    case Ga_Y:
	if (gm_putint (gm->y, value, type) == ERR)
	    return (ERR);
	break;
    case Ga_Width:
	if (gm_putint (gm->width, value, type) == ERR)
	    return (ERR);
	break;
    case Ga_Height:
	if (gm_putint (gm->height, value, type) == ERR)
	    return (ERR);
	break;
    case Ga_Rotangle:						/* MF022 */
	if (gm_putfloat(((double)180.0/M_PI)*(gm->rotangle),value,type) == ERR)
	    return (ERR);
	break;

    case Ga_HighlightColor:
	if (gm_putint ((int)gm->highlightColor, value, type) == ERR)
	    return (ERR);
	break;
    case Ga_LineColor:
	if (gm_putint ((int)gm->lineColor, value, type) == ERR)
	    return (ERR);
	break;
    case Ga_LineWidth:
	if (gm_putint (gm->lineWidth, value, type) == ERR)
	    return (ERR);
	break;
    case Ga_LineStyle:
	if (gm_putint (gm->lineStyle, value, type) == ERR)
	    return (ERR);
	break;
    case Ga_KnotColor:
	if (gm_putint ((int)gm->knotColor, value, type) == ERR)
	    return (ERR);
	break;
    case Ga_KnotSize:
	if (gm_putint (gm->knotSize, value, type) == ERR)
	    return (ERR);
	break;

    case Ga_Fill:
	if (gm_putint (gm->fill, value, type) == ERR)
	    return (ERR);
	break;
    case Ga_FillColor:
	if (gm_putint ((int)gm->fillColor, value, type) == ERR)
	    return (ERR);
	break;
    case Ga_FillBgColor:
	if (gm_putint ((int)gm->fillBgColor, value, type) == ERR)
	    return (ERR);
	break;

    case Ga_FillStyle:
	switch (gm_gettype (type)) {
	case Gt_String:
	    switch (gm->fillStyle) {
	    case FillSolid:
		strcpy ((char *)value, "FillSolid");
		break;
	    case FillTiled:
		strcpy ((char *)value, "FillTiled");
		break;
	    case FillStippled:
		strcpy ((char *)value, "FillStippled");
		break;
	    case FillOpaqueStippled:
		strcpy ((char *)value, "FillOpaqueStippled");
		break;
	    default:
		strcpy ((char *)value, "FillSolid");
		break;
	    }
	    break;
	case Gt_Int:
	    if (gm_putint (gm->fillStyle, value, type) == ERR)
		return (ERR);
	    break;
	default:
	    return (ERR);
	}
	break;

    case Ga_FillPattern:
	switch (gm_gettype (type)) {
	case Gt_Pointer:
	    *(Pixmap *)value = gm->fillPattern;
	    break;
	default:
	    return (ERR);
	}
	break;

    case Ga_TextColor:
	if (gm_putint ((int)gm->textColor, value, type) == ERR)
	    return (ERR);
	break;
    case Ga_TextBorder:
	if (gm_putint (gm->textBorder, value, type) == ERR)
	    return (ERR);
	break;
    case Ga_ImageText:
	if (gm_putint (gm->imageText, value, type) == ERR)
	    return (ERR);
	break;
    case Ga_Font:
	switch (gm_gettype (type)) {
	case Gt_Int:
	    for (i=0;  i < NDialogFonts;  i++)
		if (gm->font == w->gterm.dialog_fonts[i]) {
		    if (gm_putint (i, value, type) == ERR)
			return (ERR);
		    break;
		}
	    break;
	case Gt_Pointer:
	    *(XFontStruct **)value = gm->font;
	    break;
	default:
	    return (ERR);
	}
	break;
    case Ga_Text:
	switch (gm_gettype (type)) {
	case Gt_Pointer:
	    *((char **)value) = gm->text;
	    break;
	case Gt_String:
	    strcpy ((char *)value, gm->text);
	    break;
	default:
	    return (ERR);
	}
	break;
    case Ga_RotIndicator:					/* MF020 */
	if (gm_putint (gm->rotIndicator, value, type) == ERR)
	    return (ERR);
	break;


    default:
	return (ERR);
    }

    return (OK);
}


/* GmSetVertices -- Set the vertices of a "poly" type object.
 */
void
GmSetVertices (gm, points, first, npts)
    Marker gm;
    DPoint *points;		/* input array of points */
    int first;			/* first point to be set */
    int npts;			/* number of points to set */
{
    register DPoint *ip, *pp;
    register XPoint *op;
    register int i;
    int erase;

    /* The point vector is automatically extended if more space is needed.
     * Small vectors are stored directly in the marker descriptor in the
     * point_data array.
     */
    if (first + npts != gm->npoints) {				/* MF013 */
	if (gm->npoints > GM_MAXVERTICES) {
	    if ((gm->points = (XPoint *) XtRealloc ((char *)gm->points,
		    first + npts)) == (XPoint *)NULL)
		return;
	} else if (first + npts > GM_MAXVERTICES) {
	    if ((gm->points = (XPoint *)
		    XtMalloc (first + npts)) == (XPoint *)NULL)
		return;
	} else if (!gm->points)
	    gm->points = gm->point_data;

	gm->npoints = first + npts;
    }

    /* Copy the point data. */
    ip = points;
    op = &gm->points[first];
    for (i=0;  i < npts;  i++) {
	op->x = (int) ip->x + 0.5;
	op->y = (int) ip->y + 0.5;
	ip++, op++;
    }

    /* If we're defining the vertices of a 'poly' marker update the
     * pgon[] array with the new set of points.  Polygons are initialized
     * with a unit rectangle and since vertices can't be set as an attribute
     * the must be set with a setVertices call so we need to update the
     * structure here.
     */
    if (gm->type == Gm_Polygon) {				/* MF018 */

        if (gm->pgon)						/* MF018 */
            XtFree ((char *)gm->pgon);
        gm->pgon = (DPoint *) XtCalloc (first+npts+1, sizeof(DPoint));

        /* Copy the point data to the polygon array. */
        op = &gm->points[0];
        pp = &gm->pgon[0];
        for (i=0; i< gm->npoints; i++, pp++, op++) {
            pp->x = (double)op->x - gm->x;
            pp->y = (double)op->y - gm->y;
        }
        gm->points[first+npts] = gm->points[0];   /* Close the polygon.       */

        gm->npoints = gm->pgon_npts = first + npts + 1;
        gm->rotangle = 0.0;             	  /* reset rotation angle     */
        gm->flags |= Gm_Modified; 		  /* marker has been modified */
    }

    /* Redraw the marker if autoredraw is enabled. */
    if (gm->flags & Gm_AutoRedraw) {
	GmMarkpos (gm);
	GmRedraw (gm, GXcopy, erase=True);
    }
}


/* GmGetVertices -- Get the vertices of a "poly" type object.  The actual
 * number of points output is returned as the function value.
 */
int
GmGetVertices (gm, points, first, maxpts)
    register Marker gm;
    register DPoint *points;	/* output array of points */
    int first;			/* first point to be returned */
    int maxpts;			/* max number of points to return */
{
    register XPoint *ip;
    register DPoint *op;
    register int i;
    int top, nout;

    if (first >= gm->npoints)
	return (0);
    top = min (first + maxpts, gm->npoints);
    nout = top - first;

    /* In the case of a poly object don't return the closing segment. */
    if (gm->type == Gm_Polygon) 				/* MF027 */
        --nout;

    if (points) {
	ip = &gm->points[first];
	op = points;
	for (i=0;  i < nout;  i++) {
	    op->x = ip->x;
	    op->y = ip->y;
	    ip++, op++;
	}
    }

    return (nout);
}


/* GmGetBoundingBox -- Returns a rect large enough to completely enclose a
 * marker, regardless of its type or orientation.
 */
void
GmGetBoundingBox (gm, x, y, width, height)
    register Marker gm;
    int *x, *y;
    int *width, *height;
{
    register XRectangle *r = &gm->cur_rect;

    *x = r->x;
    *y = r->y;
    *width = r->width;
    *height = r->height;
}


/* GmStrToType -- Convert a marker type string to a marker type code.
 */
int
GmStrToType (marker_type)
register char *marker_type;
{
    register int type;

    if (strcmp (marker_type, GmText) == 0)
	type = Gm_Text;
    else if (strcmp (marker_type, GmLine) == 0)
	type = Gm_Line;
    else if (strcmp (marker_type, GmPolyline) == 0)
	type = Gm_Polyline;
    else if (strcmp (marker_type, GmRectangle) == 0)
	type = Gm_Rectangle;
    else if (strcmp (marker_type, GmBox) == 0)
	type = Gm_Box;
    else if (strcmp (marker_type, GmCircle) == 0)
	type = Gm_Circle;
    else if (strcmp (marker_type, GmEllipse) == 0)
	type = Gm_Ellipse;
    else if (strcmp (marker_type, GmPolygon) == 0)
	type = Gm_Polygon;
    else
	type = 0;

    return (type);
}


/* GmStrToEvent -- Convert a marker event type string to a marker event code.
 */
int
GmStrToEvent (event_type)
register char *event_type;
{
    register int type;

    if (strcmp (event_type, "notify") == 0)
	type = GmEvNotify;
    else if (strcmp (event_type, "moveResize") == 0)
	type = GmEvMoveResize;
    else if (strcmp (event_type, "modify") == 0)
	type = GmEvModify;
    else if (strcmp (event_type, "redraw") == 0)
	type = GmEvRedraw;
    else if (strcmp (event_type, "destroy") == 0)
	type = GmEvDestroy ;
    else if (strcmp (event_type, "input") == 0)
	type = GmEvInput;
    else if (strcmp (event_type, "focusIn") == 0)
	type = GmEvFocusIn;
    else if (strcmp (event_type, "focusOut") == 0)
	type = GmEvFocusOut;
    else if (strcmp (event_type, "constraint") == 0)
	type = GmEvConstraint;
    else
	type = 0;

    return (type);
}


/* GmStrToFunction -- Convert a drawing function string to the corresponding
 * XLIB function code.
 */
int
GmStrToFunction (function)
register char *function;
{
    register int code;

    if (strcmp (function, "clear") == 0)
	code = GXclear;
    else if (strcmp (function, "and") == 0)
	code = GXand;
    else if (strcmp (function, "andReverse") == 0)
	code = GXandReverse;
    else if (strcmp (function, "copy") == 0)
	code = GXcopy;
    else if (strcmp (function, "andInverted") == 0)
	code = GXandInverted;
    else if (strcmp (function, "noop") == 0)
	code = GXnoop;
    else if (strcmp (function, "xor") == 0)
	code = GXxor;
    else if (strcmp (function, "or") == 0)
	code = GXor;
    else if (strcmp (function, "nor") == 0)
	code = GXnor;
    else if (strcmp (function, "equiv") == 0)
	code = GXequiv;
    else if (strcmp (function, "invert") == 0)
	code = GXinvert;
    else if (strcmp (function, "orReverse") == 0)
	code = GXorReverse;
    else if (strcmp (function, "copyInverted") == 0)
	code = GXcopyInverted;
    else if (strcmp (function, "orInverted") == 0)
	code = GXorInverted;
    else if (strcmp (function, "nand") == 0)
	code = GXnand;
    else if (strcmp (function, "set") == 0)
	code = GXset;
    else
	code = -1;

    return (code);
}


/* Internal procedures for above code.
 * ------------------------------------
 */

static int
gm_getint (value, type)
    XtArgVal value;
    char *type;
{
    register int ch;

    switch (gm_gettype (type)) {
    case Gt_Bool:
    case Gt_Int:
	return ((int)value);
    case Gt_DFloatP:
	return (*(double *)value);
    case Gt_String:
	ch = *((char *)value);
	if (ch == 'T' || ch == 't')
	    return (1);
	else if (ch == 'F' || ch == 'f')
	    return (0);
	else
	    return (atoi((char *)value));
    default:
	return (0);
    }
}


static Pixel
gm_getpixel (w, value, type)
    GtermWidget w;
    XtArgVal value;
    char *type;
{
    XrmValue from, to;
    Pixel pixel;
    char *str;

    switch (gm_gettype (type)) {
    case Gt_Int:
	/* Pixel value (colormap index). */
	return ((Pixel)value);

    case Gt_String:
	/* The pixel is expressed either as a pixel number input as a string,
	 * or as a color name.  The latter case requires a type conversion.
	 */
	str = (char *)value;
	if (isdigit(str[0]) && (int)strlen(str) <= 3) {
	    int index = atoi (str);
	    pixel = w->gterm.cmap[index];
	    return (pixel);
	}

	if (w->gterm.useDefaultCM || !w->gterm.haveColormap) {
	    /* Allocate color from default colormap.
	     */
	    from.size = strlen ((char *)value) + 1;
	    from.addr = (char *)value;
	    to.addr = (caddr_t) &pixel;
	    to.size = sizeof(pixel);

	    if (!XtConvertAndStore ((Widget)w, XtRString, &from, XtRPixel, &to))
		pixel = w->gterm.cmap[1];

	} else {
	    /* Allocate closest match from custom colormap.  This is crude,
	     * but for the standard colors this will return an exact match.
	     */
	    int index, min_dist, dist, i;
	    XColor exact, best, *cp;

	    pixel = w->gterm.cmap[1];
	    if (XLookupColor (w->gterm.display,
		    get_colormap(w), str, &exact, &best)) {
		min_dist = 9999;
		index = 1;

		for (i=0;  i < w->gterm.ncolors;  i++) {
		    cp = &w->gterm.color[i];
		    dist = abs((int)exact.red - (int)cp->red) +
			   abs((int)exact.green - (int)cp->green) +
			   abs((int)exact.blue - (int)cp->blue);
		    if (dist == 0) {
			index = i;
			break;
		    } else if (dist < min_dist) {
			index = i;
			min_dist = dist;
		    }
		}

		pixel = w->gterm.color[index].pixel;
	    }
	}
	return (pixel);

    default:
	return (w->gterm.cmap[1]);
    }
}


static int
gm_getfillstyle (w, value, type)
    GtermWidget w;
    XtArgVal value;
    char *type;
{
    switch (gm_gettype (type)) {
    case Gt_String:
	if (strcmp ((char *)value, "FillSolid") == 0)
	    return (FillSolid);
	else if (strcmp ((char *)value, "FillTiled") == 0)
	    return (FillTiled);
	else if (strcmp ((char *)value, "FillStippled") == 0)
	    return (FillStippled);
	else if (strcmp ((char *)value, "FillOpaqueStippled") == 0)
	    return (FillOpaqueStippled);
	break;
    default:
	break;
    }

    return (FillSolid);
}


static double
gm_getfloat (value, type)
    XtArgVal value;
    char *type;
{
    switch (gm_gettype (type)) {
    case Gt_Bool:
    case Gt_Int:
	return ((int)value);
    case Gt_DFloatP:
	return (*(double *)value);
    case Gt_String:
	return (atof((char *)value));
    default:
	return (0);
    }
}


static char *
gm_getstring (value, type)
    XtArgVal value;
    char *type;
{
    if (strcmp (type, XtRString) == 0)
	return ((char *)value);
    else
	return ("");
}


static int
gm_putint (ival, value, type)
    int ival;
    XtArgVal value;
    char *type;
{
    switch (gm_gettype (type)) {
    case Gt_Bool:
    case Gt_Int:
	*(int *)value = ival;
	break;
    case Gt_DFloatP:
	*(double *)value = (double) ival;
	break;
    case Gt_String:
	sprintf ((char *)value, "%d", ival);
	break;
    default:
	return (ERR);
    }
    return (OK);
}


static int
gm_putfloat (fval, value, type)
    double fval;
    XtArgVal value;
    char *type;
{
    switch (gm_gettype (type)) {
    case Gt_Bool:
    case Gt_Int:
	*(int *)value = (int) fval;
	break;
    case Gt_DFloatP:
	*(double *)value = fval;
	break;
    case Gt_String:
	sprintf ((char *)value, "%g", fval);
	break;
    default:
	return (ERR);
    }
    return (OK);
}


static int
gm_gettype (type)
    char *type;
{
    if (strcmp (type, XtRBool) == 0)
	return (Gt_Int);
    else if (strcmp (type, XtRInt) == 0)
	return (Gt_Int);
    else if (strcmp (type, XtRFloat) == 0)
	return (Gt_DFloatP);
    else if (strcmp (type, XtRPointer) == 0)
	return (Gt_Pointer);
    else if (strcmp (type, XtRString) == 0)
	return (Gt_String);
    else
	return (ERR);
}


static int
gm_getattribute (attribute)
    char *attribute;
{
    if (strcmp (attribute, GmType) == 0)
	return (Ga_Type);
    else if (strcmp (attribute, GmActivated) == 0)
	return (Ga_Activated);
    else if (strcmp (attribute, GmVisible) == 0)
	return (Ga_Visible);
    else if (strcmp (attribute, GmSensitive) == 0)
	return (Ga_Sensitive);
    else if (strcmp (attribute, GmAutoRedraw) == 0)
	return (Ga_AutoRedraw);
    else if (strcmp (attribute, GmTranslations) == 0)
	return (Ga_Translations);
    else if (strcmp (attribute, GmX) == 0)
	return (Ga_X);
    else if (strcmp (attribute, GmY) == 0)
	return (Ga_Y);
    else if (strcmp (attribute, GmWidth) == 0)
	return (Ga_Width);
    else if (strcmp (attribute, GmHeight) == 0)
	return (Ga_Height);
    else if (strcmp (attribute, GmRotangle) == 0)
	return (Ga_Rotangle);
    else if (strcmp (attribute, GmHighlightColor) == 0)
	return (Ga_HighlightColor);
    else if (strcmp (attribute, GmLineColor) == 0)
	return (Ga_LineColor);
    else if (strcmp (attribute, GmLineWidth) == 0)
	return (Ga_LineWidth);
    else if (strcmp (attribute, GmLineStyle) == 0)
	return (Ga_LineStyle);
    else if (strcmp (attribute, GmKnotColor) == 0)
	return (Ga_KnotColor);
    else if (strcmp (attribute, GmKnotSize) == 0)
	return (Ga_KnotSize);
    else if (strcmp (attribute, GmFill) == 0)
	return (Ga_Fill);
    else if (strcmp (attribute, GmFillColor) == 0)
	return (Ga_FillColor);
    else if (strcmp (attribute, GmFillBgColor) == 0)
	return (Ga_FillBgColor);
    else if (strcmp (attribute, GmFillPattern) == 0)
	return (Ga_FillPattern);
    else if (strcmp (attribute, GmFillStyle) == 0)
	return (Ga_FillStyle);
    else if (strcmp (attribute, GmTextColor) == 0)
	return (Ga_TextColor);
    else if (strcmp (attribute, GmTextBgColor) == 0)
	return (Ga_TextBgColor);
    else if (strcmp (attribute, GmTextBorder) == 0)
	return (Ga_TextBorder);
    else if (strcmp (attribute, GmImageText) == 0)
	return (Ga_ImageText);
    else if (strcmp (attribute, GmFont) == 0)
	return (Ga_Font);
    else if (strcmp (attribute, GmText) == 0)
	return (Ga_Text);
    else if (strcmp (attribute, GmRotIndicator) == 0)		/* MF020 */
	return (Ga_RotIndicator);
    else
	return (ERR);
}

static void
gm_linkafter (gm, prev)
    register Marker gm;
    register Marker prev;
{
    register GtermWidget w = gm->w;

    gm->prev = prev;
    gm->next = prev ? prev->next : NULL;
    if (prev)
	prev->next = gm;

    if (!w->gterm.gm_tail || prev == w->gterm.gm_tail)
	w->gterm.gm_tail = gm;
    if (!w->gterm.gm_head)
	w->gterm.gm_head = gm;

    w->gterm.preserve_screen++;
}


static void
gm_unlink (gm)
    register Marker gm;
{
    register GtermWidget w = gm->w;

    if (gm->prev)
	gm->prev->next = gm->next;
    if (gm->next)
	gm->next->prev = gm->prev;
    if (w->gterm.gm_head == gm)
	w->gterm.gm_head = gm->next;
    if (w->gterm.gm_tail == gm)
	w->gterm.gm_tail = gm->prev;

    gm->prev = gm->next = NULL;
    if (!w->gterm.gm_head)
	w->gterm.preserve_screen = 0;
}


/* gm_do_callbacks -- Call any client callbacks registered for the given
 * event type.
 */
static int
gm_do_callbacks (gm, events, event, params, nparams)
    Marker gm;
    register int events;
    XEvent *event;
    String *params;
    Cardinal nparams;
{
    register int n;
    register struct markerCallback *cb;
    struct markerCallback callback[GM_MAXCALLBACKS];
    int ncallbacks, status;

    /* Copy the callbacks list into local memory to ensure that it is not
     * changed by executing a callback.
     */
    ncallbacks = gm->ncallbacks;
    memmove ((char *)callback, (char *)gm->callback,
	sizeof (struct markerCallback) * GM_MAXCALLBACKS);

    for (n = ncallbacks, cb = callback;  --n >= 0;  cb++)
	if (cb->events & events) {
	    status = cb->func (cb->client_data,
		gm, events, event, params, nparams);
	    if (status)
		return (status);
	}

    return (0);
}


/* gm_constraint -- Handle the constraint callback.  This is a client
 * callback called when a marker position or size attribute is changed
 * interactively at runtime.  The purpose of the callback is to allow the
 * client to apply any constraints, e.g. to keep the marker within a
 * certain area or range of sizes, to forbid rotation, and so on.
 */
static void
gm_constraint (gm, new_gm, what)
    register Marker gm, new_gm;
    register int what;
{
    register char *ip, *op;
    char argbuf[2048];
    char *argv[30];
    int argc = 0;

    /* Return immediately if there are no constraint callbacks. */
    if (!gm->constraints)
	return;

    /* Prepare an argument list listing the marker attributes being changed
     * and their old and new values.  Each attribute is passed as three
     * arg strings: name old-value new-value.  Each argument string is
     * allocated a fixed amount of space of SZ_NUMBER characters.
     */
    op = argbuf;
    if (what & Gb_X) {
	strcpy (argv[argc++]=op, "x");			   op += SZ_NUMBER;
	sprintf (argv[argc++]=op, "%d", gm->x);		   op += SZ_NUMBER;
	sprintf (argv[argc++]=op, "%d", new_gm->x);	   op += SZ_NUMBER;
    }
    if (what & Gb_Y) {
	strcpy (argv[argc++]=op, "y");			   op += SZ_NUMBER;
	sprintf (argv[argc++]=op, "%d", gm->y);		   op += SZ_NUMBER;
	sprintf (argv[argc++]=op, "%d", new_gm->y);	   op += SZ_NUMBER;
    }
    if (what & Gb_Width) {
	strcpy (argv[argc++]=op, "width");		   op += SZ_NUMBER;
	sprintf (argv[argc++]=op, "%d", gm->width);	   op += SZ_NUMBER;
	sprintf (argv[argc++]=op, "%d", new_gm->width);	   op += SZ_NUMBER;
    }
    if (what & Gb_Height) {
	strcpy (argv[argc++]=op, "height");		   op += SZ_NUMBER;
	sprintf (argv[argc++]=op, "%d", gm->height);	   op += SZ_NUMBER;
	sprintf (argv[argc++]=op, "%d", new_gm->height);   op += SZ_NUMBER;
    }
    if (what & Gb_Rotangle) {					/* MF022 */
	double	rot = (gm->rotangle * ((double)180.0 / M_PI));
	double	new_rot = (new_gm->rotangle * ((double)180.0 / M_PI));
	strcpy (argv[argc++]=op, "rotangle");		   op += SZ_NUMBER;
	sprintf (argv[argc++]=op, "%g", rot);	   	   op += SZ_NUMBER;
	sprintf (argv[argc++]=op, "%g", new_rot); 	   op += SZ_NUMBER;
    }

    /* Call any constraint callbacks.  The argv value strings are modified
     * in place.
     */
    gm_do_callbacks (gm, GmEvConstraint, NULL, argv, argc);

    /* Copy the possibly edited values back into the new_gm struct.
     */
    ip = argbuf + SZ_NUMBER * 2;
    if (what & Gb_X) {
	new_gm->x = atoi (ip);			ip += SZ_NUMBER*3;
    }
    if (what & Gb_Y) {
	new_gm->y = atoi (ip);			ip += SZ_NUMBER*3;
    }
    if (what & Gb_Width) {
	new_gm->width = atoi (ip);		ip += SZ_NUMBER*3;
    }
    if (what & Gb_Height) {
	new_gm->height = atoi (ip);		ip += SZ_NUMBER*3;
    }
    if (what & Gb_Rotangle) {
	new_gm->rotangle = atof (ip);		ip += SZ_NUMBER*3;

	/* Convert back to radians.... */
	new_gm->rotangle *= (M_PI / (double)180.0);		/* MF022 */
    }
}


static void
gm_erase (gm)
    register Marker gm;
{
    register GtermWidget w = gm->w;
    register XRectangle *r = &gm->old_rect;

    if (!w || !XtIsRealized ((Widget)w))
	return;

    /* Any clipping to the marker border is set outside this routine. */
    if ((gm->flags & Gm_Visible) && !NullRect(r))
	XCopyArea (w->gterm.display, w->gterm.pixmap, w->gterm.window,
	    w->gterm.exposeGC, r->x, r->y, r->width, r->height, r->x, r->y);
}


/* Marker actions.
 * -------------------------
 */


/* M_create -- Create a marker.
 */
static void
M_create (widget, event, params, nparams)
    Widget widget;
    XEvent *event;
    String *params;
    Cardinal *nparams;
{
    register GtermWidget w = (GtermWidget)widget;
    register XButtonEvent *ev = (XButtonEvent *) event;
    register Marker gm;
    int interactive, type;
    gmSelection what;

    savepos (w, event);

    /* If the marker has already been created in interactive mode the event
     * merely initializes the marker, otherwise we create and initialize a
     * new marker.
     */
    if (!(gm = w->gterm.gm_create)) {
	type = w->gterm.gm_defaultType;
	if (*nparams == 1) {
	    if (!(type = GmStrToType (params[0])))
		type = w->gterm.gm_defaultType;
	}
	gm = GmCreate (w, type, interactive=True);
    }

    gm->x = ev->x;
    gm->y = ev->y;
    gm->flags |= Gm_Activated;
    w->gterm.gm_create = NULL;

    what.type = (gm->type == Gm_Polygon) ? Ge_Marker : Ge_Point;
    what.vertex = 0;
    gm_focusin (w, gm, &what);
}


/* M_destroy -- Destroy a marker.
 */
static void
M_destroy (widget, event, params, nparams)
    Widget widget;
    XEvent *event;
    String *params;
    Cardinal *nparams;
{
    register GtermWidget w = (GtermWidget)widget;
    register XButtonEvent *ev = (XButtonEvent *) event;
    register Marker gm;

    savepos (w, event);

    /* Determine which marker gets this event. */
    if (!(gm = w->gterm.gm_active))
	if (!(gm = GmSelect (w, ev->x, ev->y, NULL)))
	    return;

    GmDestroy (gm);
}


/* M_destroyNull -- Destroy a marker if it is null sized.
 */
static void
M_destroyNull (widget, event, params, nparams)
    Widget widget;
    XEvent *event;
    String *params;
    Cardinal *nparams;
{
    register GtermWidget w = (GtermWidget)widget;
    register XButtonEvent *ev = (XButtonEvent *) event;
    register Marker gm;

    savepos (w, event);

    /* Determine which marker gets this event. */
    if (!(gm = w->gterm.gm_active))
	if (!(gm = GmSelect (w, ev->x, ev->y, NULL)))
	    return;

    if (gm && gm->width <= 2 && gm->height <= 2)
	GmDestroy (gm);
}


/* M_set -- Set a marker attribute.
 */
static void
M_set (widget, event, params, nparams)
    Widget widget;
    XEvent *event;
    String *params;
    Cardinal *nparams;
{
    register GtermWidget w = (GtermWidget)widget;
    register XButtonEvent *ev = (XButtonEvent *) event;
    register Marker gm;
    int i;

    savepos (w, event);

    if (!(gm = w->gterm.gm_active))
	if (!(gm = GmSelect (w, ev->x, ev->y, NULL)))
	    return;

    for (i=0;  i < *nparams;  i += 2)
	GmSetAttribute (gm,
	    params[i], (XtArgVal)params[i+1], XtRString); 	/* MF010 */
}


/* M_raise -- Raise a marker to the top of the display list.
 */
static void
M_raise (widget, event, params, nparams)
    Widget widget;
    XEvent *event;
    String *params;
    Cardinal *nparams;
{
    register GtermWidget w = (GtermWidget)widget;
    register XButtonEvent *ev = (XButtonEvent *) event;
    register Marker gm;
    int i;

    savepos (w, event);

    if (!(gm = w->gterm.gm_active))
	if (!(gm = GmSelect (w, ev->x, ev->y, NULL)))
	    return;

    GmRaise (gm, NULL);
}


/* M_lower -- Lower a marker to the bottom of the display list.
 */
static void
M_lower (widget, event, params, nparams)
    Widget widget;
    XEvent *event;
    String *params;
    Cardinal *nparams;
{
    register GtermWidget w = (GtermWidget)widget;
    register XButtonEvent *ev = (XButtonEvent *) event;
    register Marker gm;
    int i;

    savepos (w, event);

    if (!(gm = w->gterm.gm_active))
	if (!(gm = GmSelect (w, ev->x, ev->y, NULL)))
	    return;

    GmLower (gm, NULL);
}


/* M_notify -- Notify any clients that have registered callbacks for the
 * specified type of events.
 */
static void
M_notify (widget, event, params, nparams)
    Widget widget;
    XEvent *event;
    String *params;
    Cardinal *nparams;
{
    register GtermWidget w = (GtermWidget)widget;
    register XButtonEvent *ev = (XButtonEvent *) event;
    register Marker gm;
    int events, i;

    savepos (w, event);

    if (!(gm = w->gterm.gm_active))
	if (!(gm = GmSelect (w, ev->x, ev->y, NULL)))
	    return;

    for (i=0, events=0;  i < *nparams;  i++)
	if (strcmp (params[i], "notify") == 0)
	    events |= GmEvNotify;
	else if (strcmp (params[i], "moveResize") == 0)
	    events |= GmEvMoveResize;
	else if (strcmp (params[i], "modify") == 0)
	    events |= GmEvModify;
	else if (strcmp (params[i], "redraw") == 0)
	    events |= GmEvRedraw;
	else if (strcmp (params[i], "destroy") == 0)
	    events |= GmEvDestroy;
	else if (strcmp (params[i], "input") == 0)
	    events |= GmEvInput;
	else if (strcmp (params[i], "focusIn") == 0)
	    events |= GmEvFocusIn;
	else if (strcmp (params[i], "focusOut") == 0)
	    events |= GmEvFocusOut;

    GmNotify (gm, events, event, params + 1, *nparams - 1);
}


/* M_input -- Notify any clients that have registered a input callback
 * that a input event has occurred.
 */
static void
M_input (widget, event, params, nparams)
    Widget widget;
    XEvent *event;
    String *params;
    Cardinal *nparams;
{
    register GtermWidget w = (GtermWidget)widget;
    register XKeyEvent *ev = (XKeyEvent *) event;
    register Marker gm;

    savepos (w, event);

    if (!(gm = w->gterm.gm_active))
	if (!(gm = GmSelect (w, ev->x, ev->y, NULL)))
	    return;

    GmNotify (gm, GmEvInput, event, params, *nparams);
}


/* M_markpos -- Mark the current position of the marker, e.g., so that it
 * can later be erased.
 */
static void
M_markpos (widget, event, params, nparams)
    Widget widget;
    XEvent *event;
    String *params;
    Cardinal *nparams;
{
    register GtermWidget w = (GtermWidget)widget;
    register XButtonEvent *ev = (XButtonEvent *) event;
    register Marker gm;

    savepos (w, event);

    /* Determine which marker gets this event. */
    if (!(gm = w->gterm.gm_active))
	if (!(gm = GmSelect (w, ev->x, ev->y, NULL)))
	    return;

    GmMarkpos (gm);
}


/* M_markposAdd -- Execute either the markpos or add action, depending upon
 * the pointer location.  If the pointer is over an active marker at a
 * location where the add action can be executed this is done, otherwise the
 * markpos action is executed.
 */
static void
M_markposAdd (widget, event, params, nparams)
    Widget widget;
    XEvent *event;
    String *params;
    Cardinal *nparams;
{
    register GtermWidget w = (GtermWidget)widget;
    register XButtonEvent *ev = (XButtonEvent *) event;
    GmSelection what = &w->gterm.gm_selection;
    register Marker gm;

    savepos (w, event);

    /* Get marker and type of active portion of marker. */
    if (!(gm = w->gterm.gm_active))
	if (!(gm = GmSelect (w, ev->x, ev->y, what)))
	    return;

    /* Always do a markpos whether we Add or not. */
    GmMarkpos (gm);

    /* Add a point if possible for the given marker and pointer location. */
    if (what->type == Ge_Edge &&
	    (gm->type == Gm_Polyline || gm->type == Gm_Polygon))
	GmAddPt (gm, ev->x, ev->y);
}


/* M_redraw -- Redraw a marker.
 */
static void
M_redraw (widget, event, params, nparams)
    Widget widget;
    XEvent *event;
    String *params;
    Cardinal *nparams;
{
    register GtermWidget w = (GtermWidget)widget;
    register XButtonEvent *ev = (XButtonEvent *) event;
    register Marker gm;
    int erase;

    savepos (w, event);

    /* Determine which marker gets this event. */
    if (!(gm = w->gterm.gm_active))
	if (!(gm = GmSelect (w, ev->x, ev->y, NULL)))
	    return;

    /* This redraw undoes the final Xor draw. */
    GmRedraw (gm, GXxor, erase=False);

    /* Redraw the full marker. */
    GmRedraw (gm, GXcopy, erase=True);
}


/* M_addPt -- Add a point.
 */
static void
M_addPt (widget, event, params, nparams)
    Widget widget;
    XEvent *event;
    String *params;
    Cardinal *nparams;
{
    register GtermWidget w = (GtermWidget)widget;
    register XButtonEvent *ev = (XButtonEvent *) event;
    GmSelection what = &w->gterm.gm_selection;
    register Marker gm;

    savepos (w, event);

    /* Determine which marker gets this event. */
    if (!(gm = w->gterm.gm_active))
	if (!(gm = GmSelect (w, ev->x, ev->y, what)))
	    return;

    /* Add a point if possible for the given marker and pointer location. */
    if (what->type == Ge_Edge &&
	    (gm->type == Gm_Polyline || gm->type == Gm_Polygon))
	GmAddPt (gm, ev->x, ev->y);
}


/* M_deletePt -- Delete a point.
 */
static void
M_deletePt (widget, event, params, nparams)
    Widget widget;
    XEvent *event;
    String *params;
    Cardinal *nparams;
{
    register GtermWidget w = (GtermWidget)widget;
    register XButtonEvent *ev = (XButtonEvent *) event;
    GmSelection what = &w->gterm.gm_selection;
    register Marker gm;

    savepos (w, event);

    /* Determine which marker gets this event. */
    if (!(gm = w->gterm.gm_active))
	if (!(gm = GmSelect (w, ev->x, ev->y, what)))
	    return;

    if (what->type == Ge_Point)
	GmDeletePt (gm, ev->x, ev->y);
}


/* M_movePt -- Move a point.
 */
static void
M_movePt (widget, event, params, nparams)
    Widget widget;
    XEvent *event;
    String *params;
    Cardinal *nparams;
{
    register GtermWidget w = (GtermWidget)widget;
    register XButtonEvent *ev = (XButtonEvent *) event;
    GmSelection what = &w->gterm.gm_selection;
    register Marker gm;

    savepos (w, event);

    /* Determine which marker gets this event. */
    if (!(gm = w->gterm.gm_active))
	if (!(gm = GmSelect (w, ev->x, ev->y, what)))
	    return;

    /* Move a point (vertex) if supported by marker type. */
    if (what->type == Ge_Point &&
	    (gm->type == Gm_Polyline || gm->type == Gm_Polygon))
	GmMovePt (gm, ev->x, ev->y);
}


/* M_deleteDestroy -- Delete a point or destroy a marker, depending upon the
 * pointer position.
 */
static void
M_deleteDestroy (widget, event, params, nparams)
    Widget widget;
    XEvent *event;
    String *params;
    Cardinal *nparams;
{
    register GtermWidget w = (GtermWidget)widget;
    register XButtonEvent *ev = (XButtonEvent *) event;
    GmSelection what = &w->gterm.gm_selection;
    register Marker gm;

    savepos (w, event);

    /* Determine which marker gets this event. */
    if (!(gm = w->gterm.gm_active))
	if (!(gm = GmSelect (w, ev->x, ev->y, what)))
	    return;

    switch (what->type) {
    case Ge_Point:
	GmDeletePt (gm, ev->x, ev->y);
	break;
    case Ge_Marker:
	GmDestroy (gm);
	break;
    }
}


/* M_move -- Move a marker.
 */
static void
M_move (widget, event, params, nparams)
    Widget widget;
    XEvent *event;
    String *params;
    Cardinal *nparams;
{
    register GtermWidget w = (GtermWidget)widget;
    register XButtonEvent *ev = (XButtonEvent *) event;
    register Marker gm;

    savepos (w, event);

    /* Determine which marker gets this event. */
    if (!(gm = w->gterm.gm_active))
	if (!(gm = GmSelect (w, ev->x, ev->y, NULL)))
	    return;

    if (ev->time - gm->time > GM_UPDATE) {
	GmMove (gm, ev->x, ev->y);
	XFlush (w->gterm.display);
	gm->time = ev->time;
    }
}


/* M_resize -- Resize a marker.
 */
static void
M_resize (widget, event, params, nparams)
    Widget widget;
    XEvent *event;
    String *params;
    Cardinal *nparams;
{
    register GtermWidget w = (GtermWidget)widget;
    register XButtonEvent *ev = (XButtonEvent *) event;
    register Marker gm;

    savepos (w, event);

    /* Determine which marker gets this event. */
    if (!(gm = w->gterm.gm_active))
	if (!(gm = GmSelect (w, ev->x, ev->y, NULL)))
	    return;

    if (ev->time - gm->time > GM_UPDATE) {
	GmResize (gm, ev->x, ev->y);
	XFlush (w->gterm.display);
	gm->time = ev->time;
    }
}


/* M_moveResize -- Move a point or marker, or resize a marker, depending
 * upon the pointer position.
 */
static void
M_moveResize (widget, event, params, nparams)
    Widget widget;
    XEvent *event;
    String *params;
    Cardinal *nparams;
{
    register GtermWidget w = (GtermWidget)widget;
    register XButtonEvent *ev = (XButtonEvent *) event;
    GmSelection what = &w->gterm.gm_selection;
    register Marker gm;

    savepos (w, event);

    /* Determine which marker gets this event. */
    if (!(gm = w->gterm.gm_active))
	if (!(gm = GmSelect (w, ev->x, ev->y, what)))
	    return;

    if (ev->time - gm->time > GM_UPDATE) {
	switch (what->type) {
	case Ge_Marker:
	    GmMove (gm, ev->x, ev->y);
	    break;
	case Ge_Point:
	    if (gm->type == Gm_Polygon || gm->type == Gm_Polyline)
		GmMovePt (gm, ev->x, ev->y);
	    else
		goto resize;
	    break;
	case Ge_Edge:
resize:	    GmResize (gm, ev->x, ev->y);
	    break;
	}
	XFlush (w->gterm.display);
	gm->time = ev->time;
    }
}


/* M_rotate -- Rotate a marker.
 */
static void
M_rotate (widget, event, params, nparams)
    Widget widget;
    XEvent *event;
    String *params;
    Cardinal *nparams;
{
    register GtermWidget w = (GtermWidget)widget;
    register XButtonEvent *ev = (XButtonEvent *) event;
    register Marker gm;

    savepos (w, event);

    /* Determine which marker gets this event. */
    if (!(gm = w->gterm.gm_active))
	if (!(gm = GmSelect (w, ev->x, ev->y, NULL)))
	    return;

    if (ev->time - gm->time > GM_UPDATE) {
	GmRotate (gm, ev->x, ev->y);
	XFlush (w->gterm.display);
	gm->time = ev->time;
    }
}


/* M_rotateResize -- Rotate or resize a marker.
 */
static void
M_rotateResize (widget, event, params, nparams)
    Widget widget;
    XEvent *event;
    String *params;
    Cardinal *nparams;
{
    register GtermWidget w = (GtermWidget)widget;
    register XButtonEvent *ev = (XButtonEvent *) event;
    GmSelection what = &w->gterm.gm_selection;
    register Marker gm;

    savepos (w, event);

    /* Determine which marker gets this event. */
    if (!(gm = w->gterm.gm_active))
	if (!(gm = GmSelect (w, ev->x, ev->y, NULL)))
	    return;

    if (ev->time - gm->time > GM_UPDATE) {
	switch (what->type) {
	case Ge_Point:
	    GmRotate (gm, ev->x, ev->y);
	    break;
	case Ge_Edge:
	    if (gm->flags & Gm_Smooth)
		GmRotate (gm, ev->x, ev->y);
	    else
		GmResize (gm, ev->x, ev->y);
	    break;
	default:
	    GmResize (gm, ev->x, ev->y);
	    break;
	}

	XFlush (w->gterm.display);
	gm->time = ev->time;
    }
}


/*
 * Marker class code.
 * ---------------------
 * Each marker class implements a subset of the following procedures.  The
 * first set of procedures are required.  The second set are optional and
 * may be set to NULL in the marker descriptor if not implemented by the
 * marker class.
 *
 *	       gm_xxxx_init (gm, interactive)
 *    bool = gm_xxxx_select (gm, x, y, &what)
 *	    gm_xxxx_markpos (gm)
 *	     gm_xxxx_redraw (gm, func)
 *	     gm_xxxx_update (gm)
 *
 *	      gm_xxxx_addPt (gm, x, y)
 *	   gm_xxxx_deletePt (gm, x, y)
 *	     gm_xxxx_movePt (gm, x, y)
 *	       gm_xxxx_move (gm, x, y)
 *	     gm_xxxx_resize (gm, x, y)
 *	     gm_xxxx_rotate (gm, x, y)
 *
 * where xxxx is the 4 character marker class name.
 */

/* Marker class TEXT.
 */
static int gm_text_select();
static void gm_text_move(), gm_text_resize();
static void gm_text_markpos(), gm_text_redraw();
static void gm_text_update(), gm_text_updatePolygon();

static void
gm_text_init (gm, interactive)
    register Marker gm;
    int interactive;
{
    register GtermWidget w = gm->w;

    gm->type = Gm_Text;
    if (!(gm->flags & Gm_Activated)) {
	gm->highlightColor = w->gterm.gm_highlightColor;
	gm->lineColor = w->gterm.gm_TextLineColor;
	gm->lineWidth = w->gterm.gm_lineWidth;
	gm->lineStyle = w->gterm.gm_lineStyle;
	gm->textColor = w->gterm.gm_TextColor;
	gm->textBgColor = w->gterm.gm_TextBgColor;
	gm->textBorder = w->gterm.gm_TextBorder;
	gm->fill = w->gterm.gm_fill;
	gm->fillStyle = w->gterm.gm_fillStyle;
	gm->fillColor = w->gterm.gm_fillColor;
	gm->fillBgColor = w->gterm.gm_fillBgColor;
	gm->fillStyle = w->gterm.gm_fillStyle;
	gm->font = w->gterm.gm_TextFont;
	gm->imageText = False;
    }

    if (gm->points && gm->npoints > GM_MAXVERTICES)
	XtFree ((char *)gm->points);
    gm->npoints = 4 + 1;
    gm->points = gm->point_data;

    gm->select   = gm_text_select;
    gm->markpos  = gm_text_markpos;
    gm->redraw   = gm_text_redraw;
    gm->update   = gm_text_update;
    gm->addPt    = NULL;
    gm->deletePt = NULL;
    gm->movePt   = NULL;
    gm->move     = gm_text_move;
    gm->resize   = gm_text_resize;
    gm->rotate   = NULL;

    if (w->gterm.gm_TextString) {
	if (gm->text)
	    XtFree (gm->text);
	gm->text = (char *) XtMalloc (strlen(w->gterm.gm_TextString)+1);
	strcpy (gm->text, w->gterm.gm_TextString);
    } else
	gm->text = NULL;
}

static int
gm_text_select (gm, x, y, what)
    register Marker gm;
    int x, y;
    GmSelection what;
{
    if (gm_select (gm, x, y, what)) {
	if (what && what->type == Ge_Edge)
	    what->type = Ge_Marker;
	return (1);
    } else
	return (0);
}


static void
gm_text_markpos (gm)
    register Marker gm;
{
    gm_markpos (gm);
}


static void
gm_text_redraw (gm, function)
    register Marker gm;
    int function;
{
    register GtermWidget w = gm->w;
    int flags = (Gm_Activated|Gm_Visible);
    int char_width, char_height, xsize, ysize;
    int breakline, l_pix, r_pix, maxch, x, y;
    XFontStruct *fp = gm->font;
    char *ip, *op, *otop;
    char *l_ip, *l_op;
    char line[1024];

    if (!((gm->flags & flags) == flags))
	return;
    if (gm->width <= 0 || gm->height <= 0)
	return;

    /* In rubber-band mode just draw the outline of the text region. */
    if (function == GXxor) {
	int save_lineWidth = gm->lineWidth;

	if (gm->lineWidth <= 0)
	    gm->lineWidth = 1;
	gm_redraw (gm, function);
	gm->lineWidth = save_lineWidth;
	return;
    }

    /* General case.  First draw the text box. */
    gm_redraw (gm, function);

    /* Now draw the text. */
    if (!gm->text)
	return;

    char_width  = fp->max_bounds.width;
    char_height = fp->max_bounds.ascent + fp->max_bounds.descent;
    xsize = gm->width;
    ysize = gm->height;

    l_pix = (gm->lineWidth + 1) / 2 + gm->textBorder - 1;
    r_pix = (gm->lineWidth + 1) / 2 + gm->textBorder - 1 -
	(fp->max_bounds.width - fp->max_bounds.rbearing);
    if ((maxch = (xsize - l_pix - r_pix) / char_width) < 1)
	return;

    x = gm->x + (gm->lineWidth + 1) / 2 + gm->textBorder + 1;
    y = gm->y + (gm->lineWidth + 1) / 2 + gm->textBorder +
	fp->max_bounds.ascent;

    XSetForeground (w->gterm.display, w->gterm.gm_drawGC, gm->textColor);
    XSetBackground (w->gterm.display, w->gterm.gm_drawGC, gm->textBgColor);
    XSetFont (w->gterm.display, w->gterm.gm_drawGC, fp->fid);

    /* Fill lines in a multiline text box.
     */
    l_ip = l_op = NULL;
    otop = line + maxch;
    breakline = 0;

    for (ip = gm->text, op=line;  *ip || op > line;  ) {
	if (! *ip) {
	    breakline++;
	} else if (*ip == ' ' || *ip == '\t') {
	    l_ip = ip;
	    l_op = op;
	    *op++ = ' ';
	    ip++;
	} else if (*ip == '\n') {
	    ip++;
	    breakline++;
	} else
	    *op++ = *ip++;

	if (breakline || op > otop) {
	    if (op > otop) {
		if (l_ip && l_op) {
		    ip = l_ip + 1;
		    *l_op = '\0';
		} else {
		    while (op > otop) {
			if (ip > gm->text && isprint (*(ip-1)))
			    --ip;
			--op;
		    }
		    *op = '\0';
		}
	    } else
		*op = '\0';

	    if (gm->imageText) {
		while (op < otop)
		    *op++ = ' ';
		*op = '\0';
		XDrawImageString (w->gterm.display, w->gterm.window,
		    w->gterm.gm_drawGC, x, y, line, strlen(line));
	    } else {
		XDrawString (w->gterm.display, w->gterm.window,
		    w->gterm.gm_drawGC, x, y, line, strlen(line));
	    }

	    y += char_height;
	    if (breakline)
		y += gm->textBorder;
	    if (y + fp->max_bounds.descent > gm->y + ysize)
		break;

	    op = line;
	    l_ip = l_op = NULL;
	    breakline = 0;
	}
    }
}


static void
gm_text_update (gm)
    register Marker gm;
{
    register GtermWidget w = gm->w;
    int flags = (Gm_Activated|Gm_Visible);

    if (!((gm->flags & flags) == flags))
	return;
    if (gm->width <= 0 || gm->height <= 0)
	return;

    if (gm->flags & Gm_Modified) {
	gm_text_updatePolygon (gm);
	gm_setCurRect (gm);
	gm->flags &= ~Gm_Modified;
    }
}

static void
gm_text_move (gm, x, y)
    register Marker gm;
    int x, y;
{
    struct marker new_gm;
    new_gm.x = max (0, x - gm->width / 2);
    new_gm.y = max (0, y - gm->height / 2);
    gm_constraint (gm, &new_gm, Gb_X|Gb_Y);	/* corner */

    gm->x = new_gm.x;
    gm->y = new_gm.y;
    gm_text_updatePolygon (gm);
    gm_setCurRect (gm);
}

static void
gm_text_resize (gm, x, y)
    register Marker gm;
    int x, y;
{
    struct marker new_gm;
    new_gm.width = abs (x - gm->x);
    new_gm.height = abs (y - gm->y);
    gm_constraint (gm, &new_gm, Gb_Width|Gb_Height);

    gm->width = new_gm.width;
    gm->height = new_gm.height;
    gm_text_updatePolygon (gm);
    gm_setCurRect (gm);
}

static void
gm_text_updatePolygon (gm)
    register Marker gm;
{
    register XPoint *p = gm->points;
    int xsize = gm->width;
    int ysize = gm->height;

    p[0].x = gm->x;		p[0].y = gm->y;
    p[1].x = gm->x;		p[1].y = gm->y + ysize;
    p[2].x = gm->x + xsize;	p[2].y = gm->y + ysize;
    p[3].x = gm->x + xsize;	p[3].y = gm->y;
    p[4].x = gm->x;		p[4].y = gm->y;
}


/* Marker class LINE.
 */
static void
gm_line_init (gm, interactive)
    register Marker gm;
    int interactive;
{
    gm->type = Gm_Line;
    /* stub out for now */
}


/* Marker class POLYLINE.
 */
static void
gm_plin_init (gm, interactive)
    register Marker gm;
    int interactive;
{
    gm->type = Gm_Polyline;
    /* stub out for now */
}


/* Marker class RECTANGLE.
 */
static int gm_rect_select();
static void gm_rect_move(), gm_rect_resize(), gm_rect_rotate();
static void gm_rect_update(), gm_rect_updatePolygon();

static void
gm_rect_init (gm, interactive)
    register Marker gm;
    int interactive;
{
    register GtermWidget w = gm->w;

    gm->type = Gm_Rectangle;
    if (!(gm->flags & Gm_Activated)) {
	gm->lineWidth = w->gterm.gm_lineWidth;
	gm->lineStyle = w->gterm.gm_lineStyle;
	gm->highlightColor = w->gterm.gm_highlightColor;
	gm->lineColor = w->gterm.gm_RectLineColor;
	gm->knotColor = w->gterm.gm_RectKnotColor;
	gm->knotSize  = w->gterm.gm_RectKnotSize;
	gm->fill = w->gterm.gm_fill;
	gm->fillStyle = w->gterm.gm_fillStyle;
	gm->fillColor = w->gterm.gm_fillColor;
	gm->fillBgColor = w->gterm.gm_fillBgColor;
    }

    if (gm->points && gm->npoints > GM_MAXVERTICES)
	XtFree ((char *)gm->points);
    gm->points = gm->point_data;
    gm->npoints = 4 + 1;

    gm->select   = gm_rect_select;
    gm->markpos  = gm_markpos;
    gm->update   = gm_rect_update;
    gm->redraw   = gm_redraw;
    gm->addPt    = NULL;
    gm->deletePt = NULL;
    gm->movePt   = NULL;
    gm->move     = gm_rect_move;
    gm->resize   = gm_rect_resize;
    gm->rotate   = gm_rect_rotate;
}

static void
gm_rect_update (gm)
    register Marker gm;
{
    if (gm->flags & Gm_Modified) {
	gm_rect_updatePolygon (gm);
	gm_setCurRect (gm);
	gm->flags &= ~Gm_Modified;
    }
}

static int
gm_rect_select (gm, x, y, what)
    register Marker gm;
    int x, y;
    GmSelection what;
{
    if (gm_select (gm, x, y, what)) {
	if (what && what->type == Ge_Edge)
	    what->type = Ge_Marker;
	return (1);
    } else
	return (0);
}

static void
gm_rect_move (gm, x, y)
    register Marker gm;
    int x, y;
{
    struct marker new_gm;
    new_gm.x = x;  new_gm.y = y;
    gm_constraint (gm, &new_gm, Gb_X|Gb_Y);

    gm->x = new_gm.x;  gm->y = new_gm.y;
    gm_rect_updatePolygon (gm);
    gm_setCurRect (gm);
}

static void
gm_rect_resize (gm, x, y)
    register Marker gm;
    int x, y;
{
/*  double cos_rotangle = cos (-(gm->rotangle));
    double sin_rotangle = sin (-(gm->rotangle)); */
    double cos_rotangle = cos ((gm->rotangle));			/* MF019 */
    double sin_rotangle = sin ((gm->rotangle));			/* MF019 */
    struct marker new_gm;
    int rx, ry;
    int ox = x, oy = y;

    /* Translate to the marker reference frame. */
    x = x - gm->x;
    y = y - gm->y;

    /* Rotate back to the unrotated reference frame. */
    rx = x * cos_rotangle - y * sin_rotangle;
    ry = x * sin_rotangle + y * cos_rotangle;

    /* Compute new width and height. */
    if (rx < 0)
	new_gm.x = gm->x - (-rx - gm->width) / 2;
    else
	new_gm.x = gm->x + (rx - gm->width) / 2;

    if (ry < 0)
	new_gm.y = gm->y - (-ry - gm->height) / 2;
    else
	new_gm.y = gm->y + (ry - gm->height) / 2;

    new_gm.width = gm->width + (abs(rx) - gm->width) / 2;
    new_gm.height = gm->height + (abs(ry) - gm->height) / 2;

    gm_constraint (gm, &new_gm, Gb_X|Gb_Y|Gb_Width|Gb_Height);
    gm->x = new_gm.x;
    gm->y = new_gm.y;
    gm->width = new_gm.width;
    gm->height = new_gm.height;

    gm_rect_updatePolygon (gm);
    gm_setCurRect (gm);
}

static void
gm_rect_rotate (gm, x, y)
    register Marker gm;
    int x, y;
{
    double alpha, theta;
    struct marker new_gm;

    if (x == gm->x && y == gm->y)
	gm->rotangle = 0;
    else {

    /*  V1.1  These eqns have the effect of allowing a marker to be grabbed by
     *  any corner but doing so resets the rotation angle the first time the
     *  marker is rotated.

	theta = atan2 ((double)(y - gm->y), (double)(x - gm->x));
	alpha = atan2 ((double)gm->height, (double)gm->width);
 	new_gm.rotangle = gm_niceAngle (theta + alpha);
     */

	theta = atan2 ((double)(gm->y - y), (double)(x - gm->x)); /* MF019 */
	new_gm.rotangle = gm_niceAngle (theta);			  /* MF019 */
	gm_constraint (gm, &new_gm, Gb_Rotangle);
	gm_rotate_indicator (gm, GXxor);			  /* MF020 */
	gm->rotangle =  new_gm.rotangle;
	gm_rotate_indicator (gm, GXxor);			  /* MF020 */
	gm_rect_updatePolygon (gm);
	gm_setCurRect (gm);
    }
}

static void
gm_rect_updatePolygon (gm)
    Marker gm;
{
    register int x, y;
    register XPoint *p = gm->points;
    double cos_rotangle, sin_rotangle;

/*  cos_rotangle = cos (gm->rotangle);
    sin_rotangle = sin (gm->rotangle);*/
    cos_rotangle = cos (-gm->rotangle);				/* MF019 */
    sin_rotangle = sin (-gm->rotangle);				/* MF019 */
    x = gm->width;
    y = gm->height;

    p[0].x = x * cos_rotangle - y * sin_rotangle + gm->x + 0.5;
    p[0].y = x * sin_rotangle + y * cos_rotangle + gm->y + 0.5;

    x = -x;
    p[1].x = x * cos_rotangle - y * sin_rotangle + gm->x + 0.5;
    p[1].y = x * sin_rotangle + y * cos_rotangle + gm->y + 0.5;

    y = -y;
    p[2].x = x * cos_rotangle - y * sin_rotangle + gm->x + 0.5;
    p[2].y = x * sin_rotangle + y * cos_rotangle + gm->y + 0.5;

    x = -x;
    p[3].x = x * cos_rotangle - y * sin_rotangle + gm->x + 0.5;
    p[3].y = x * sin_rotangle + y * cos_rotangle + gm->y + 0.5;

    p[4] = p[0];
}


/* Marker class BOX.  A box marker is like a rectangle except that it is
 * described and resized by the center and radius (width/height), like
 * the other "centered" marker types (circle, ellipse, etc.).
 */
static int gm_boxx_select();
static void gm_boxx_move(), gm_boxx_resize(), gm_boxx_rotate();
static void gm_boxx_update(), gm_boxx_updatePolygon();

static void
gm_boxx_init (gm, interactive)
    register Marker gm;
    int interactive;
{
    register GtermWidget w = gm->w;

    gm->type = Gm_Box;
    if (!(gm->flags & Gm_Activated)) {
	gm->lineWidth = w->gterm.gm_lineWidth;
	gm->lineStyle = w->gterm.gm_lineStyle;
	gm->highlightColor = w->gterm.gm_highlightColor;
	gm->lineColor = w->gterm.gm_RectLineColor;
	gm->knotColor = w->gterm.gm_RectKnotColor;
	gm->knotSize  = w->gterm.gm_RectKnotSize;
	gm->fill = w->gterm.gm_fill;
	gm->fillStyle = w->gterm.gm_fillStyle;
	gm->fillColor = w->gterm.gm_fillColor;
	gm->fillBgColor = w->gterm.gm_fillBgColor;
    }

    if (gm->points && gm->npoints > GM_MAXVERTICES)
	XtFree ((char *)gm->points);
    gm->points = gm->point_data;
    gm->npoints = 4 + 1;

    gm->select   = gm_boxx_select;
    gm->markpos  = gm_markpos;
    gm->update   = gm_boxx_update;
    gm->redraw   = gm_redraw;
    gm->addPt    = NULL;
    gm->deletePt = NULL;
    gm->movePt   = NULL;
    gm->move     = gm_boxx_move;
    gm->resize   = gm_boxx_resize;
    gm->rotate   = gm_boxx_rotate;
}

static void
gm_boxx_update (gm)
    register Marker gm;
{
    if (gm->flags & Gm_Modified) {
	gm_boxx_updatePolygon (gm);
	gm_setCurRect (gm);
	gm->flags &= ~Gm_Modified;
    }
}

static int
gm_boxx_select (gm, x, y, what)
    register Marker gm;
    int x, y;
    GmSelection what;
{
    if (gm_select (gm, x, y, what)) {
	if (what && what->type == Ge_Edge)
	    what->type = Ge_Marker;
	return (1);
    } else
	return (0);
}

static void
gm_boxx_move (gm, x, y)
    register Marker gm;
    int x, y;
{
    struct marker new_gm;
    new_gm.x = x;  new_gm.y = y;
    gm_constraint (gm, &new_gm, Gb_X|Gb_Y);

    gm->x = new_gm.x;  gm->y = new_gm.y;
    gm_boxx_updatePolygon (gm);
    gm_setCurRect (gm);
}

static void
gm_boxx_resize (gm, x, y)
    register Marker gm;
    int x, y;
{
/*  double cos_rotangle = cos (-(gm->rotangle));
    double sin_rotangle = sin (-(gm->rotangle)); */
    double cos_rotangle = cos ((gm->rotangle));			/* MF019 */
    double sin_rotangle = sin ((gm->rotangle));			/* MF019 */
    struct marker new_gm;
    int rx, ry;

    /* Translate to the marker reference frame. */
    x = x - gm->x;
    y = y - gm->y;

    /* Rotate back to the unrotated reference frame. */
    rx = x * cos_rotangle - y * sin_rotangle;
    ry = x * sin_rotangle + y * cos_rotangle;

    /* Compute new width and height. */
    new_gm.width = abs(rx);
    new_gm.height = abs(ry);
    gm_constraint (gm, &new_gm, Gb_Width|Gb_Height);
    gm->width  = new_gm.width;
    gm->height = new_gm.height;

    gm_boxx_updatePolygon (gm);
    gm_setCurRect (gm);
}

static void
gm_boxx_rotate (gm, x, y)
    register Marker gm;
    int x, y;
{
    double alpha, theta;
    struct marker new_gm;

    if (x == gm->x && y == gm->y)
	gm->rotangle = 0;
    else {
    /* V1.1
	theta = atan2 ((double)(y - gm->y), (double)(x - gm->x));
	alpha = atan2 ((double)gm->height, (double)gm->width);
	new_gm.rotangle = gm_niceAngle (theta + alpha);
     */
	theta = atan2 ((double)(gm->y - y), (double)(x - gm->x)); /* MF019 */
	new_gm.rotangle = gm_niceAngle (theta);			  /* MF019 */
	gm_constraint (gm, &new_gm, Gb_Rotangle);
	gm_rotate_indicator (gm, GXxor);			  /* MF020 */
	gm->rotangle = new_gm.rotangle;
	gm_rotate_indicator (gm, GXxor);			  /* MF020 */
	gm_boxx_updatePolygon (gm);
	gm_setCurRect (gm);
    }
}

static void
gm_boxx_updatePolygon (gm)
    Marker gm;
{
    register int x, y;
    register XPoint *p = gm->points;
    double cos_rotangle, sin_rotangle;
	
    double alpha = atan2 ((double)gm->height, (double)gm->width);

/*  cos_rotangle = cos (gm->rotangle);
    sin_rotangle = sin (gm->rotangle); */
    cos_rotangle = cos (-gm->rotangle);				/* MF019 */
    sin_rotangle = sin (-gm->rotangle);				/* MF019 */
    x = gm->width;
    y = gm->height;

    p[0].x = x * cos_rotangle - y * sin_rotangle + gm->x + 0.5;
    p[0].y = x * sin_rotangle + y * cos_rotangle + gm->y + 0.5;

    x = -x;
    p[1].x = x * cos_rotangle - y * sin_rotangle + gm->x + 0.5;
    p[1].y = x * sin_rotangle + y * cos_rotangle + gm->y + 0.5;

    y = -y;
    p[2].x = x * cos_rotangle - y * sin_rotangle + gm->x + 0.5;
    p[2].y = x * sin_rotangle + y * cos_rotangle + gm->y + 0.5;

    x = -x;
    p[3].x = x * cos_rotangle - y * sin_rotangle + gm->x + 0.5;
    p[3].y = x * sin_rotangle + y * cos_rotangle + gm->y + 0.5;

    p[4] = p[0];
}


/* Marker class CIRCLE.
 */
static int gm_circ_select();
static void gm_circ_move(), gm_circ_resize(), gm_circ_rotate();
static void gm_circ_update(), gm_circ_updatePolygon();

static void
gm_circ_init (gm, interactive)
    register Marker gm;
    int interactive;
{
    register GtermWidget w = gm->w;

    gm->type = Gm_Circle;
    if (!(gm->flags & Gm_Activated)) {
	gm->lineWidth = w->gterm.gm_lineWidth;
	gm->lineStyle = w->gterm.gm_lineStyle;
	gm->highlightColor = w->gterm.gm_highlightColor;
	gm->lineColor = w->gterm.gm_CircleLineColor;
	gm->knotColor = w->gterm.gm_CircleKnotColor;
	gm->knotSize  = w->gterm.gm_CircleKnotSize;
	gm->fill = w->gterm.gm_fill;
	gm->fillStyle = w->gterm.gm_fillStyle;
	gm->fillColor = w->gterm.gm_fillColor;
	gm->fillBgColor = w->gterm.gm_fillBgColor;
	gm->flags |= Gm_Smooth;
    }

    gm->width = gm->height = (gm->width + gm->height) / 2.0;

    if (gm->points && gm->npoints > GM_MAXVERTICES)
	XtFree ((char *)gm->points);
    gm->points = gm->point_data;
    gm->npoints = GM_NPTSCIRCLE;				/* MF015 */

    gm->select   = gm_circ_select;
    gm->markpos  = gm_markpos;
    gm->update   = gm_circ_update;
    gm->redraw   = gm_redraw;
    gm->addPt    = NULL;
    gm->deletePt = NULL;
    gm->movePt   = NULL;
    gm->move     = gm_circ_move;
    gm->resize   = gm_circ_resize;
    gm->rotate   = NULL;
}

static void
gm_circ_update (gm)
    register Marker gm;
{
    if (gm->flags & Gm_Modified) {
	gm_circ_updatePolygon (gm);
	gm_setCurRect (gm);
	gm->flags &= ~Gm_Modified;
    }
}

static int
gm_circ_select (gm, x, y, what)
    register Marker gm;
    int x, y;
    GmSelection what;
{
    if (gm_select (gm, x, y, what)) {
	if (what && what->type == Ge_Point)
	    what->type = Ge_Edge;
	return (1);
    } else
	return (0);
}

static void
gm_circ_move (gm, x, y)
    register Marker gm;
    int x, y;
{
    struct marker new_gm;
    new_gm.x = x;  new_gm.y = y;
    gm_constraint (gm, &new_gm, Gb_X|Gb_Y);

    gm->x = new_gm.x;  gm->y = new_gm.y;
    gm_circ_updatePolygon (gm);
    gm_setCurRect (gm);
}

static void
gm_circ_resize (gm, x, y)
    register Marker gm;
    int x, y;
{
    struct marker new_gm;
    new_gm.width = new_gm.height =
	sqrt ((double)(SQR(x - gm->x) + SQR(y - gm->y)));
    gm_constraint (gm, &new_gm, Gb_Width|Gb_Height);

    gm->width = gm->height = new_gm.width;
    gm_circ_updatePolygon (gm);
    gm_setCurRect (gm);
}

static void
gm_circ_updatePolygon (gm)
    Marker gm;
{
    register XPoint *p = gm->points;
    register int npts, i, j;
    double theta, x, y;

    /*npts = (gm->npoints - 1) / 4;*/
    npts = gm->npoints / 4;					/* MF028 */

    for (i=0;  i < npts;  i++) {
	theta = PI_2 / npts * i;
	x = gm->width  * cos(theta);
	y = gm->height * sin(theta);
	
	j = i;
	p[npts*0+j].x = x + gm->x;
	p[npts*0+j].y = y + gm->y;
	
	x = -x;  j = npts-1 - i;
	p[npts*1+j].x = x + gm->x;
	p[npts*1+j].y = y + gm->y;
	
	y = -y;  j = i;
	p[npts*2+j].x = x + gm->x;
	p[npts*2+j].y = y + gm->y;
	
	x = -x;  j = npts-1 - i;
	p[npts*3+j].x = x + gm->x;
	p[npts*3+j].y = y + gm->y;
    }

    /*p[gm->npoints-1] = p[0];*/				/* MF015 */
}


/* Marker class ELLIPSE.
 */
static int gm_elip_select();
static void gm_elip_move(), gm_elip_resize(), gm_elip_rotate();
static void gm_elip_update(), gm_elip_updatePolygon();

static void
gm_elip_init (gm, interactive)
    register Marker gm;
    int interactive;
{
    register GtermWidget w = gm->w;

    gm->type = Gm_Ellipse;
    if (!(gm->flags & Gm_Activated)) {
	gm->lineWidth = w->gterm.gm_lineWidth;
	gm->lineStyle = w->gterm.gm_lineStyle;
	gm->highlightColor = w->gterm.gm_highlightColor;
	gm->lineColor = w->gterm.gm_EllipseLineColor;
	gm->knotColor = w->gterm.gm_EllipseKnotColor;
	gm->knotSize  = w->gterm.gm_EllipseKnotSize;
	gm->fill = w->gterm.gm_fill;
	gm->fillStyle = w->gterm.gm_fillStyle;
	gm->fillColor = w->gterm.gm_fillColor;
	gm->fillBgColor = w->gterm.gm_fillBgColor;
	gm->flags |= Gm_Smooth;
    }

    if (gm->points && gm->npoints > GM_MAXVERTICES)
	XtFree ((char *)gm->points);
    gm->points = gm->point_data;
/*    gm->npoints = GM_NPTSCIRCLE + 1;*/
    gm->npoints = GM_NPTSCIRCLE;				/* MF015 */

    gm->select   = gm_elip_select;
    gm->markpos  = gm_markpos;
    gm->update   = gm_elip_update;
    gm->redraw   = gm_redraw;
    gm->addPt    = NULL;
    gm->deletePt = NULL;
    gm->movePt   = NULL;
    gm->move     = gm_elip_move;
    gm->resize   = gm_elip_resize;
    gm->rotate   = gm_elip_rotate;
}

static void
gm_elip_update (gm)
    register Marker gm;
{
    if (gm->flags & Gm_Modified) {
	gm_elip_updatePolygon (gm);
	gm_setCurRect (gm);
	gm->flags &= ~Gm_Modified;
    }
}

static int
gm_elip_select (gm, x, y, what)
    register Marker gm;
    int x, y;
    GmSelection what;
{
    if (gm_select (gm, x, y, what)) {
	if (what && what->type == Ge_Point)
	    what->type = Ge_Edge;
	return (1);
    } else
	return (0);
}

static void
gm_elip_move (gm, x, y)
    register Marker gm;
    int x, y;
{
    struct marker new_gm;
    new_gm.x = x;  new_gm.y = y;
    gm_constraint (gm, &new_gm, Gb_X|Gb_Y);

    gm->x = new_gm.x;  gm->y = new_gm.y;
    gm_elip_updatePolygon (gm);
    gm_setCurRect (gm);
}

static void
gm_elip_resize (gm, x, y)
    register Marker gm;
    int x, y;
{
    struct marker new_gm;
/*  double theta = -(gm->rotangle);*/
    double theta = (gm->rotangle);				/* MF019 */
    int rx, ry;

    /* Translate to the marker reference frame. */
    x = x - gm->x;
    y = y - gm->y;

    /* Rotate back to the unrotated reference frame. */
    rx = x * cos(theta) - y * sin(theta);
    ry = x * sin(theta) + y * cos(theta);

    /* Compute new width and height. */
    new_gm.width  = abs(rx);
    new_gm.height = abs(ry);

    gm_constraint (gm, &new_gm, Gb_Width|Gb_Height);
    gm->width  = new_gm.width;
    gm->height = new_gm.height;

    gm_elip_updatePolygon (gm);
    gm_setCurRect (gm);
}

static void
gm_elip_rotate (gm, x, y)
    register Marker gm;
    int x, y;
{
    struct marker new_gm;
    double theta;

    if (x == gm->x && y == gm->y)
	gm->rotangle = 0;
    else {
/*	theta = atan2 ((double)(y - gm->y), (double)(x - gm->x));*/
	theta = atan2 ((double)(gm->y - y), (double)(x - gm->x)); /* MF019 */
	new_gm.rotangle = gm_niceAngle (theta);
	gm_constraint (gm, &new_gm, Gb_Rotangle);
	gm_rotate_indicator (gm, GXxor);			  /* MF020 */
	gm->rotangle = new_gm.rotangle;
	gm_rotate_indicator (gm, GXxor);			  /* MF020 */
	gm_elip_updatePolygon (gm);
	gm_setCurRect (gm);
    }
}

static void
gm_elip_updatePolygon (gm)
    Marker gm;
{
    register XPoint *p = gm->points;
    register int npts, i, j;
    double cos_rotangle, sin_rotangle;
    double theta, x, y;

    npts = (gm->npoints - 1) / 4 + 1;				/* MF017 */
/*  cos_rotangle = cos (gm->rotangle);
    sin_rotangle = sin (gm->rotangle); */
    cos_rotangle = cos (-gm->rotangle);				/* MF019 */
    sin_rotangle = sin (-gm->rotangle);				/* MF019 */

    for (i=0;  i < npts;  i++) {
	theta = PI_2 / npts * i;
	x = gm->width  * cos(theta);
	y = gm->height * sin(theta);
	
	j = i;
	p[npts*0+j].x = x * cos_rotangle - y * sin_rotangle + gm->x + 0.5;
	p[npts*0+j].y = x * sin_rotangle + y * cos_rotangle + gm->y + 0.5;
	
	x = -x;  j = npts-1 - i;
	p[npts*1+j].x = x * cos_rotangle - y * sin_rotangle + gm->x + 0.5;
	p[npts*1+j].y = x * sin_rotangle + y * cos_rotangle + gm->y + 0.5;
	
	y = -y;  j = i;
	p[npts*2+j].x = x * cos_rotangle - y * sin_rotangle + gm->x + 0.5;
	p[npts*2+j].y = x * sin_rotangle + y * cos_rotangle + gm->y + 0.5;
	
	x = -x;  j = npts-1 - i;
	p[npts*3+j].x = x * cos_rotangle - y * sin_rotangle + gm->x + 0.5;
	p[npts*3+j].y = x * sin_rotangle + y * cos_rotangle + gm->y + 0.5;
    }

    /*p[gm->npoints-1] = p[0];*/				/* MF015 */
}


/* Marker class POLYGON.
 */
static int gm_pgon_select();
static void gm_pgon_addPt(), gm_pgon_deletePt(), gm_pgon_movePt();
static void gm_pgon_move(), gm_pgon_resize(), gm_pgon_rotate();
static void gm_pgon_redraw(), gm_pgon_update(), gm_pgon_updatePolygon();

static void
gm_pgon_init (gm, interactive)
    register Marker gm;
    int interactive;
{
    register GtermWidget w = gm->w;
    register DPoint *p;

    gm->type = Gm_Polygon;
    if (!(gm->flags & Gm_Activated)) {
	gm->lineWidth = w->gterm.gm_lineWidth;
	gm->lineStyle = w->gterm.gm_lineStyle;
	gm->highlightColor = w->gterm.gm_highlightColor;
	gm->lineColor = w->gterm.gm_PgonLineColor;
	gm->knotColor = w->gterm.gm_PgonKnotColor;
	gm->knotSize  = w->gterm.gm_PgonKnotSize;
	gm->fill = w->gterm.gm_fill;
	gm->fillStyle = w->gterm.gm_fillStyle;
	gm->fillColor = w->gterm.gm_fillColor;
	gm->fillBgColor = w->gterm.gm_fillBgColor;

	gm->npoints = gm->pgon_npts = 4 + 1;
	gm->points = gm->point_data;
	if (gm->pgon)
	    XtFree ((char *)gm->pgon);

	/* Start out with a small square polygon. */
	gm->pgon = p = (DPoint *) XtMalloc (5 * sizeof (DPoint));
	gm->x = w->gterm.last_x;
	gm->y = w->gterm.last_y;

	if (p) {
	    p[0].x = -1;  p[0].y = -1;
	    p[1].x = -1;  p[1].y =  1;
	    p[2].x =  1;  p[2].y =  1;
	    p[3].x =  1;  p[3].y = -1;
	    p[4].x = -1;  p[4].y = -1;

	    gm_pgon_updatePolygon (gm);
	    gm_setCurRect (gm);
	}

	if (interactive)
	    gm->flags |= Gm_PgonInit;
    }

    if (gm->points && gm->npoints > GM_MAXVERTICES)
	XtFree ((char *)gm->points);
    gm->points = gm->point_data;

    /* The following gets executed when an existing non-polygon marker is
     * turned into a polygon marker.
     */
    if (gm->pgon && gm->pgon_npts)
	gm->npoints = gm->pgon_npts;
    else {
	gm->npoints = gm->pgon_npts = 4 + 1;

	/* Start out with a small square polygon. */
	gm->pgon = p = (DPoint *) XtMalloc (5 * sizeof (DPoint));

	if (p) {
	    p[0].x = -gm->width;  p[0].y = -gm->height;
	    p[1].x = -gm->width;  p[1].y =  gm->height;
	    p[2].x =  gm->width;  p[2].y =  gm->height;
	    p[3].x =  gm->width;  p[3].y = -gm->height;
	    p[4].x = -gm->width;  p[4].y = -gm->height;

	    gm_pgon_updatePolygon (gm);
	    gm_setCurRect (gm);
	}
    }

    gm->select   = gm_select;
    gm->markpos  = gm_markpos;
    gm->update   = gm_pgon_update;
    gm->redraw   = gm_pgon_redraw;
    gm->addPt    = gm_pgon_addPt;
    gm->deletePt = gm_pgon_deletePt;
    gm->movePt   = gm_pgon_movePt;
    gm->move     = gm_pgon_move;
    gm->resize   = gm_pgon_resize;
    gm->rotate   = gm_pgon_rotate;
}

static void
gm_pgon_redraw (gm, function)
    register Marker gm;
    int function;
{
    /* The PgonInit flag is set when a polygon marker is interactively created
     * to cause any pointer motion event to resize the marker.  The first
     * pointer up causes a redraw which clears the flag.
     */
    if (function != GXxor && gm->width > 1 && gm->height > 1)
	gm->flags &= ~Gm_PgonInit;

    gm_redraw (gm, function);
}

static void
gm_pgon_update (gm)
    register Marker gm;
{
    if (gm->flags & Gm_Modified) {
	gm_pgon_updatePolygon (gm);
	gm_setCurRect (gm);
	gm->flags &= ~Gm_Modified;
    }
}

static void
gm_pgon_addPt (gm, x, y)
    register Marker gm;
    int x, y;
{
    register DPoint *pv;
    register GtermWidget w = gm->w;
/*  double cos_rotangle = cos (-(gm->rotangle));
    double sin_rotangle = sin (-(gm->rotangle)); */
    double cos_rotangle = cos ((gm->rotangle));			/* MF019 */
    double sin_rotangle = sin ((gm->rotangle));			/* MF019 */
    int vertex, nbytes;
    double rx, ry;

    if (gm->flags & Gm_PgonInit) {
	gm_pgon_resize (gm, x, y);
	return;
    }

    /* Translate to the marker reference frame. */
    x = x - gm->x;
    y = y - gm->y;

    /* Rotate back to the unrotated reference frame. */
    rx = x * cos_rotangle - y * sin_rotangle;
    ry = x * sin_rotangle + y * cos_rotangle;

    /* Add the point. */
    vertex = w->gterm.gm_selection.vertex;
    if (vertex < 0 || vertex >= gm->npoints)
	return;
    nbytes = (gm->npoints + 1) * sizeof (DPoint);
    if ((pv = (DPoint *)
	    XtRealloc ((char *)gm->pgon, nbytes)) == (DPoint *)NULL)
	return;

    gm->pgon = pv;
    memmove (&pv[vertex+2], &pv[vertex+1],
	(gm->npoints - vertex - 1) * sizeof(DPoint));
    pv[vertex+1].x = rx;
    pv[vertex+1].y = ry;
    gm->npoints++;

    nbytes = gm->npoints * sizeof (XPoint);
    if (gm->npoints > GM_MAXVERTICES) {
	if (gm->points != gm->point_data)
	    gm->points = (XPoint *) XtRealloc ((char *)gm->points, nbytes);
	else
	    gm->points = (XPoint *) XtMalloc (nbytes);
    } else
	gm->points = gm->point_data;

    gm_pgon_updatePolygon (gm);
    gm_setCurRect (gm);
}

static void
gm_pgon_deletePt (gm, x, y)
    register Marker gm;
    int x, y;
{
    register DPoint *pv;
    register GtermWidget w = gm->w;
    int vertex, nbytes;

    if (gm->npoints <= 2)
	return;

    if (gm->flags & Gm_PgonInit) {
	gm_pgon_resize (gm, x, y);
	return;
    }

    /* Delete the point. */
    vertex = w->gterm.gm_selection.vertex;
    if (vertex < 0 || vertex >= gm->npoints)
	return;
    pv = gm->pgon;

    memmove (&pv[vertex], &pv[vertex+1],
	(gm->npoints - vertex - 1) * sizeof(DPoint));
    gm->npoints--;

    nbytes = gm->npoints * sizeof (DPoint);
    if ((pv = (DPoint *)
	    XtRealloc ((char *)gm->pgon, nbytes)) == (DPoint *)NULL)
	return;
    gm->pgon = pv;

    if (gm->npoints <= GM_MAXVERTICES && gm->points != gm->point_data)
	XtFree ((char *)gm->points);
    gm->points = gm->point_data;

    gm_pgon_updatePolygon (gm);
    gm_setCurRect (gm);
}

static void
gm_pgon_movePt (gm, x, y)
    register Marker gm;
    int x, y;
{
    register DPoint *p;
    register GtermWidget w = gm->w;
/*  double cos_rotangle = cos (-(gm->rotangle));
    double sin_rotangle = sin (-(gm->rotangle)); */
    double cos_rotangle = cos ((gm->rotangle));			/* MF019 */
    double sin_rotangle = sin ((gm->rotangle));			/* MF019 */
    double rx, ry;
    int vertex;

    if (gm->flags & Gm_PgonInit) {
	gm_pgon_resize (gm, x, y);
	return;
    }

    /* Translate to the marker reference frame. */
    x = x - gm->x;
    y = y - gm->y;

    /* Rotate back to the unrotated reference frame. */
    rx = x * cos_rotangle - y * sin_rotangle;
    ry = x * sin_rotangle + y * cos_rotangle;

    /* Get vertex. */
    vertex = w->gterm.gm_selection.vertex;
    if (vertex < 0 || vertex >= gm->npoints)
	return;
    p = &gm->pgon[vertex];

    /* Edit point. */
    p->x = rx;
    p->y = ry;

    gm_pgon_updatePolygon (gm);
    gm_setCurRect (gm);
}

static void
gm_pgon_move (gm, x, y)
    register Marker gm;
    int x, y;
{
    struct marker new_gm;

    if (gm->flags & Gm_PgonInit) {
	gm_pgon_resize (gm, x, y);
	return;
    }

    new_gm.x = x;  new_gm.y = y;
    gm_constraint (gm, &new_gm, Gb_X|Gb_Y);
    gm->x = new_gm.x;  gm->y = new_gm.y;
    gm_pgon_updatePolygon (gm);
    gm_setCurRect (gm);
}

static void
gm_pgon_resize (gm, x, y)
    Marker gm;
    int x, y;
{
    register DPoint *p, *q;
    GtermWidget w = gm->w;
    double cos_rotangle = cos (-(gm->rotangle));
    double sin_rotangle = sin (-(gm->rotangle));
    double theta, scale, slope, rx, ry, x1, y1, x2, y2, xi;
    int vertex, i;

    /* Translate to the marker reference frame. */
    x = x - gm->x;
    y = y - gm->y;

    /* Rotate back to the unrotated reference frame. */
    rx = x * cos_rotangle - y * sin_rotangle;
    ry = x * sin_rotangle + y * cos_rotangle;

    /* Get first vertex of nearest edge. */
    vertex = w->gterm.gm_selection.vertex;
    if (vertex < 0 || vertex >= gm->npoints)
	return;
    p = &gm->pgon[vertex];
    q = p + 1;

    /* Rotate reference frame so that intercept is at y=0. */
    if (abs(rx) + abs(ry) < 1.0)
	scale = 1.0;
    else {
	theta = atan2 (ry, rx);
	cos_rotangle = cos (-theta);
	sin_rotangle = sin (-theta);

	x1 = p->x * cos_rotangle - p->y * sin_rotangle;
	y1 = p->x * sin_rotangle + p->y * cos_rotangle;
	x2 = q->x * cos_rotangle - q->y * sin_rotangle;
	y2 = q->x * sin_rotangle + q->y * cos_rotangle;

	/* Compute scale factor. */
	if (y1 == y2 || x1 == x2)
	    scale = 1.0;
	else {
	    slope = (y2 - y1) / (x2 - x1);
	    xi = x1 - y1 / slope;
	    scale = sqrt (SQR(rx) + SQR(ry)) / xi;
	}
    }

    /* Rescale the polygon. */
    for (i=0, p=gm->pgon;  i < gm->npoints;  i++, p++) {
	p->x *= scale;
	p->y *= scale;
    }

    gm_pgon_updatePolygon (gm);
    gm_setCurRect (gm);
}

static void
gm_pgon_rotate (gm, x, y)
    register Marker gm;
    int x, y;
{
    register DPoint *p;
    register GtermWidget w = gm->w;
    double cos_rotangle = cos (-(gm->rotangle));
    double sin_rotangle = sin (-(gm->rotangle));
    double alpha, beta, rx, ry;
    double theta = atan2 ((double)(gm->y - y), (double)(x - gm->x));/* MF019 */
    struct marker new_gm;
    int vertex;

    if (gm->flags & Gm_PgonInit) {
	gm_pgon_resize (gm, x, y);
	return;
    }

    if (x == gm->x && y == gm->y)
	return;

    /* Translate to the marker reference frame. */
    x = x - gm->x;
    y = y - gm->y;

    /* Rotate back to the unrotated reference frame. */
    rx = x * cos_rotangle - y * sin_rotangle;
    ry = x * sin_rotangle + y * cos_rotangle;
    if (abs(rx) + abs(ry) < 1.0)
	return;

    vertex = w->gterm.gm_selection.vertex;
    if (vertex < 0 || vertex >= gm->npoints)
	return;

    p = &gm->pgon[vertex];
    alpha = atan2 (p->y, p->x);	/* angle btw origin & selected vertex */
    beta  = atan2 (ry, rx);	/* angle btw origin & cursor position */

    new_gm.rotangle = gm_niceAngle (gm->rotangle + (beta - alpha));

    new_gm.rotangle = gm_niceAngle (theta);			/* MF019 */

    gm_constraint (gm, &new_gm, Gb_Rotangle);
    gm_rotate_indicator (gm, GXxor);			  	/* MF020 */
    gm->rotangle = new_gm.rotangle;
    gm_rotate_indicator (gm, GXxor);			  	/* MF020 */

    gm_pgon_updatePolygon (gm);
    gm_setCurRect (gm);
}

static void
gm_pgon_updatePolygon (gm)
    Marker gm;
{
    register int npts, i;
    register DPoint *ip = gm->pgon;
    register XPoint *op = gm->points;
    double cos_rotangle, sin_rotangle;
    int width, height, xp, xn, yp, yn;

    npts = gm->npoints;
/*  cos_rotangle = cos (gm->rotangle);
    sin_rotangle = sin (gm->rotangle); */
    cos_rotangle = cos (-gm->rotangle);				/* MF019 */
    sin_rotangle = sin (-gm->rotangle);				/* MF019 */
    xp = xn = yp = yn = 0;

    for (i=0;  i < npts;  i++, ip++, op++) {
	/* Compute the rotated point. */
	op->x = ip->x * cos_rotangle - ip->y * sin_rotangle + gm->x + 0.5;
	op->y = ip->x * sin_rotangle + ip->y * cos_rotangle + gm->y + 0.5;

	/* Compute a width/height estimate for the polygon.
	 */
	if (ip->x > xp)
	    xp = ip->x;
	else if (ip->x < xn)
	    xn = ip->x;

	if (ip->y > yp)
	    yp = ip->y;
	else if (ip->y < yn)
	    yn = ip->y;

	gm->width  = (xp + -xn) / 2;
	gm->height = (yp + -yn) / 2;
    }

    gm->points[npts-1] = gm->points[0];
    gm->pgon_npts = gm->npoints;
}


/* Internal procedures for above code.
 * -----------------------------------
 */

/* gm_select -- Determine if a point is within or near a marker, and if so,
 * determine whether the point selects a vertex, edge, or the entire marker.
 */
static int
gm_select (gm, x, y, what)
    Marker gm;
    register int x, y;
    GmSelection what;
{
    register XPoint *p, *ptop;
    GtermWidget w = gm->w;
    int v_dist = w->gterm.gm_nearVertex;
    int e_dist = w->gterm.gm_nearEdge;
    double seglen, d1, d2, s, K, frac;
    int ncrossings, x0, y0;
    XPoint *q;
    int n;
    int use_old_method = 0;

    /* Determine if the point is near a vertex.  */
    for (p = gm->points, n = gm->npoints - 1;  --n >= 0;  p++)
	if (abs (x - p->x) < v_dist && abs (y - p->y) < v_dist) {
	    if (what) {
		what->type = Ge_Point;
		what->vertex = p - gm->points;
	    }
	    return (1);
	}

    /* Determine if the point is near an edge.  The test is based on the
     * observation that when a point is near a line segment, the sum of the
     * distances from the point to either end-point of the line segment is
     * nearly the same as the length of the line segment.
     */
    p = gm->points;

    ptop = p + (gm->npoints - 1);  				/* MF014 */
    x0 = p->x;  y0 = p->y;
    d1 = sqrt ((double)(SQR(x - x0) + SQR(y - y0)));

    for (p++;  p < ptop;  p++) {
	seglen = sqrt ((double)(SQR(p->x - x0) + SQR(p->y - y0)));
	d2 = sqrt ((double)(SQR(x - p->x) + SQR(y - p->y)));

	if (abs(d1 + d2 - seglen) < e_dist) {			/* MF028 */
	    if (what) {
		what->type = Ge_Edge;
		what->vertex = (p - 1) - gm->points;
	    }
	    return (1);
	}

	d1 = d2;
	x0 = p->x;  y0 = p->y;
    }

    /* If the marker is one of the closed polygon types, determine if the
     * point is inside the marker.
     */
    switch (gm->type) {
    case Gm_Line:
    case Gm_Polyline:
	return (0);
	break;
    case Gm_Circle:
        d1 = sqrt ((double)(SQR(x - gm->x) + SQR(y - gm->y)));
	if (d1 < gm->width) {
	    if (what) what->type = Ge_Marker;
	    return (1);
	} else
	    return (0);
	break;
    }

    if (use_old_method) {
        for (p = gm->points, ncrossings=0;  p < ptop;  p++) {
	    /* Scan forward until we find a line segment that crosses Y.
	     */
	    if (p->y > y) {
	        for (p++;  p < ptop && p->y >= y;  p++)
		    ;
	        --p;
	    } else if (p->y < y) {
	        for (p++;  p < ptop && p->y <= y;  p++)
		    ;
	        --p;
	    }

	    /* The line segment p[0]:p[1] crosses the Y plane.  If this lies
	     * entirely to the left of the X plane we can ignore it.  If any
	     * portion of the line segment lies to the right of X we compute
	     * the point where the line intersects the Y plane.  If this point
	     * is to the right of the X plane we have a crossing.
	     */
	    q = p + 1;
	    if (q < ptop && p->x > x || q->x > x) {
                if (q->y == p->y)
                     frac = (double) 0.0;
                else
                     frac = (double)(y - p->y) / (double)(q->y - p->y);
	        if ((frac * (q->x - p->x) + p->x) >= x)
		    ncrossings++;
	    }
        }

    } else {
        float xp[64], yp[64];
        int i;

        for (i=0, p=gm->points, ncrossings=0;  p <= ptop;  p++, i++) {
	    xp[i] = (float) p->x;
	    yp[i] = (float) p->y;
        }
        ncrossings = point_in_poly (gm->npoints, xp, yp, (float)x, (float)y);
    }

    if (ncrossings & 1) {
	if (what)
	    what->type = Ge_Marker;
	return (1);
    }

    return (0);
}

static int
point_in_poly (npol, xp, yp, x, y)
int 	npol;
float 	*xp, *yp, x, y;
{
      int i, j, c = 0;

      for (i = 0, j = npol-1; i < npol; j = i++) {
        if ((((yp[i] <= y) && (y < yp[j])) ||
             ((yp[j] <= y) && (y < yp[i]))) &&
            (x < (xp[j] - xp[i]) * (y - yp[i]) / (yp[j] - yp[i]) + xp[i]))

          c = !c;
      }
      return c;
}




/* gm_markpos -- Mark the current position of a marker.
 */
static void
gm_markpos (gm)
    register Marker gm;
{
    gm->old_rect = gm->cur_rect;
    XUnionRegion (gm->cur_region, null_region, gm->old_region);
}


/* gm_redraw -- Redraw a marker expressed as a list of vertices.
 */
static void
gm_redraw (gm, function)
    register Marker gm;
    int function;
{
    GtermWidget w = gm->w;
    Display *display = w->gterm.display;
    Window window = w->gterm.window;
    int flags = (Gm_Activated|Gm_Visible);
    GC gc = (function == GXxor) ? w->gterm.gm_rubberGC : w->gterm.gm_drawGC;

    if (!w || !XtIsRealized ((Widget)w))
	return;
    if (!((gm->flags & flags) == flags))
	return;

    /* Fill the polygon area if indicated. */
    if (gm->fill && function != GXxor) {
	if (gm->fillPattern) {
	    XSetStipple (display, gc, gm->fillPattern);
	    XSetForeground (display, gc, gm->fillColor);
	    XSetBackground (display, gc, gm->fillBgColor);
	    XSetFillStyle (display, gc, gm->fillStyle);
	} else {
	    XSetForeground (display, gc, gm->fillColor);
	    XSetFillStyle (display, gc, FillSolid);
	}

	XFillPolygon (display, window, gc,
	    gm->points, gm->npoints, Nonconvex, CoordModeOrigin);
    }

    /* Set up the drawing GC. */
    if (function != GXxor) {
	XSetFunction (display, gc, function);
	XSetFillStyle (display, gc, FillSolid);
	XSetForeground (display, gc, (gm == w->gterm.gm_active) ?
	    gm->highlightColor : gm->lineColor);

	XSetLineAttributes (display, gc,
	    gm->lineWidth +
		((gm == w->gterm.gm_active) ? w->gterm.gm_highlightWidth : 0),
	    gm->lineStyle,
	    CapButt,
	    (gm->type == Gm_Polygon || gm->type == Gm_Polyline) ?
		JoinBevel : JoinMiter);
    }

    /* Draw the marker outline. */
    if (gm->lineWidth > 0) {
	if (gm->type == Gm_Circle ||
		(gm->type == Gm_Ellipse && abs(gm->rotangle) < 0.01)) {

	    /* Special case - use X arc drawing primitive.  We could use the
	     * gm->points polygon instead, as this outline polygon is
	     * maintained for all classes of marker.
	     */
	    if (w->gterm.gm_xorFill && function == GXxor) {
		XFillArc (display, window, gc,
		    gm->x - gm->width, gm->y - gm->height,
		    gm->width * 2, gm->height * 2, 0, 360 * 64);
	    }
	    XDrawArc (display, window, gc,
		gm->x - gm->width, gm->y - gm->height,
		gm->width * 2, gm->height * 2, 0, 360 * 64);

	} else {
	    /* Draw marker expressed as a polygon. */
	    if (w->gterm.gm_xorFill && function == GXxor) {
		XFillPolygon (display, window, gc,
		    gm->points, gm->npoints, Convex, CoordModeOrigin);
	    }
	    XDrawLines (display, window, gc,
		gm->points, gm->npoints, CoordModeOrigin);
	}
    }

    /* Draw the knots if enabled. */
    if (function != GXxor && gm->knotSize > 0) {
	int knotsize = gm->knotSize;
	int halfsize = gm->knotSize / 2;
	int i;

	XSetForeground (display, gc, gm->knotColor);
	for (i=0;  i < gm->npoints;  i++) {
	    XFillRectangle (display, window, gc,
		gm->points[i].x - halfsize, gm->points[i].y - halfsize,
		gm->knotSize, gm->knotSize);
	}
    }
}


/* gm_rotate_indicator -- Draw a line indicating the rotation angle.
 */
static void
gm_rotate_indicator (gm, function)				/* MF020 */
Marker gm;
int    function;
{
    GtermWidget w = gm->w;
    Display *display = w->gterm.display;
    Window window = w->gterm.window;
    GC gc = (function == GXxor) ? w->gterm.gm_rubberGC : w->gterm.gm_drawGC;

    if (!gm->rotIndicator)
	return ;

    if (function == GXxor) {
        if (gm->type == Gm_Polygon ||
            gm->type == Gm_Ellipse ||
            gm->type == Gm_Box ||
            gm->type == Gm_Rectangle) {
    		int	x, y, x2, y2;
    		double  ar, cos_rotangle, sin_rotangle;
    		double  alpha = atan2 ((double)gm->height,(double)gm->width);

	        cos_rotangle = cos ((double)(-gm->rotangle - alpha));
    	        sin_rotangle = sin ((double)(-gm->rotangle - alpha));
		ar = (double) gm->height / (double) gm->width;
    	        x = (int) (ar * (gm->width / 2));
	        y = (int) (ar * (gm->height / 2));
    	        x2 = x * cos_rotangle - y * sin_rotangle + gm->x;
    	        y2 = x * sin_rotangle + y * cos_rotangle + gm->y;

  	        XDrawLine (display, window, gc, gm->x, gm->y, x2, y2); 
        }
    } else {
	; 	/* no-op at present */
    }
}


/* gm_setCurRect -- Compute a bounding rectangle which completely encloses
 * a marker (assumes that the marker is expressed as list of points).
 */
static void
gm_setCurRect (gm)
Marker gm;
{
    int border;

    XDestroyRegion (gm->cur_region);
    gm->cur_rect = null_rect;

    if (gm->npoints <= 0)
	gm->cur_region = XCreateRegion();
    else {
	gm->cur_region = XPolygonRegion (gm->points, gm->npoints, EvenOddRule);
	border = (max (gm->lineWidth, gm->knotSize) + 1) / 2;
	border = max (border, BORDER);
	XShrinkRegion (gm->cur_region, -border, -border);
	XClipBox (gm->cur_region, &gm->cur_rect);
    }
}


/* gm_niceAngle -- Round a rotation angle to a "nice" value.
 */
static double
gm_niceAngle (alpha)
    double alpha;
{
    double tol = 0.003;
    double beta;

    if (     abs (alpha - PI_2*0) < tol)
	beta = PI_2*0;
    else if (abs (alpha - PI_2*1) < tol)
	beta = PI_2*1;
    else if (abs (alpha - PI_2*2) < tol)
	beta = PI_2*2;
    else if (abs (alpha - PI_2*3) < tol)
	beta = PI_2*3;
    else if (abs (alpha - PI_2*4) < tol)
	beta = PI_2*0;
    else
	beta = alpha;

    return (beta);
}


