[comp.sources.amiga] v02i078: gimme.lib - misc library routines, Part05/07

page@swan.ulowell.edu (Bob Page) (12/02/88)

Submitted-by: oscvax!jan@uunet.UU.NET (Jan Sven Trabandt)
Posting-number: Volume 2, Issue 78
Archive-name: libraries/gimme.5

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	globals.c
#	graph.c
#	inputhand.c
#	intuistuff.c
#	keyboard.c
#	memchain.c
#	menu.c
#	menustuff.c
#	message.c
# This archive created: Thu Dec  1 19:52:10 1988
cat << \SHAR_EOF > globals.c
/*
 *  FILE: globals.c
 *  Some global declarations.
 *  This allows inclusion of <gimmelib/gimmeall.h> without linking in entire
 *  files containing global variables unnecessarily.
 *
 *  Public Domain, but keep my name in it as the original author.
 *  31-Aug-88	Jan Sven Trabandt   first release version
 */


#define I_AM_GIM_GLOBALS
#include "gimmelib/requester.h"
#include "gimmelib/scrollbar.h"

/* generally usefull stuff */
struct TextAttr gimMyFont = {
    (STRPTR) "topaz.font",
    TOPAZ_EIGHTY,
    FS_NORMAL,
    FPF_ROMFONT
  };


/* for requester.c */
UBYTE *gimAutReqPostext = (UBYTE *) GAR_POSTEXT;
UBYTE *gimAutReqNegtext = (UBYTE *) GAR_NEGTEXT;


/* for scrollbar.c */
USHORT gimDataOneScrollUp[] = {
    0xffff,
    0xffff,
    0xfe7f,
    0xfdbf,
    0xfbdf,
    0xf7ef,
    0xeff7,
    0xdffb,
    0xffff
  };
USHORT gimDataOneScrollDown[] = {
    0xffff, 0xdffb, 0xeff7, 0xf7ef, 0xfbdf,
    0xfdbf, 0xfe7f, 0xffff, 0xffff
  };
USHORT gimDataOneScrollLeft[] = {
    0xffff, 0xffcf, 0xff3f, 0xfcff, 0xf3ff,
    0xfcff, 0xff3f, 0xffcf, 0xffff
  };
USHORT gimDataOneScrollRight[] = {
    0xffff, 0xf3ff, 0xfcff, 0xff3f, 0xffcf,
    0xff3f, 0xfcff, 0xf3ff, 0xffff
  };
SHAR_EOF
cat << \SHAR_EOF > graph.c
/*
 *  FILE: graph.c
 *  Support routines for creating and manipulating graphs.
 *
 *  Public Domain, but keep my name in it as the original author.
 *  31-Oct-88	Jan Sven Trabandt   added to gimme.lib
 */


#define I_AM_GRAPH
#include "gimmelib/gimmefuncs.h"
#include "gimmelib/graph.h"
#include "gimmelib/postext.h"
#include <clib/macros.h>


/* macros for internal scaling */
#define SCALEUP(x)  ((long)(x) << SCALE)        /* x * 2^SCALE */
#define SCALEDN(x)  ((long)(x) >> SCALE)        /* x / 2^SCALE */

/* macros to translate pixel offset from origin to pixel offset in rastport */
#define TRANSX(gr,off)  ((short)((gr)->X.origin + (off)))
#define TRANSY(gr,off)  ((short)((gr)->Y.origin - (off)))

/* leeways for titles */
#define XLEEWAY     4
#define YLEEWAY     3

#define TOLERANCE   2		/* pixel tolerance for fancy auto label */


/* internal flags for graphWriteLabel */
#define GWL_RESERVED_VAL    (0x0000ffffL)   /* lower short reserved for value */
#define GWL_NO_HIGH	    (1L << 16)      /* for internal use only!! */


/* forward declarations */
static SHORT coordToOffset();
static SHORT offsetToCoord();
static VOID  initAxis();
static SHORT updateCoord();
static VOID  doBlock();


GRAPH *gimmeGraph( newgr, bm, areainfo, tmpras )
    register NEWGRAPH	*newgr;
    struct BitMap	*bm;
    struct AreaInfo	*areainfo;
    struct TmpRas	*tmpras;
{
    register GRAPH  *gr;
    void	    *mymh = NULL;
    UBYTE	    *raster;
    SHORT	    width, height;

#ifdef GIMME_WIMPY
    if( !newgr ) {
	return( NULL );
    }
#endif
    if( !bm && (!newgr->rp || !newgr->rp->BitMap) ) {
	return( NULL );
    }
    gr = (GRAPH *) chainAllocMem( &mymh, (ULONG)sizeof(GRAPH),
				    MEMF_PUBLIC | MEMF_CLEAR );
    if( !gr ) {
	return( NULL );
    }
    if( !newgr->rp ) {
	InitRastPort( &gr->rp );
    } else {
	gr->rp = *newgr->rp;	    /* copy struct */
	if( newgr->rp->Layer ) {
	    gr->rp.Layer = chainAllocMem( &mymh, (ULONG)sizeof(struct Layer),
					    MEMF_PUBLIC | MEMF_CLEAR );
	    if( !gr->rp.Layer ) {
		chainFreeMem( mymh );
		return( NULL );
	    }
	    *gr->rp.Layer = *newgr->rp->Layer;	/* copy struct */
	    gr->rp.Layer->rp = &gr->rp; 	/* make this layer come back */
	}
    }
    if( bm ) {
	gr->rp.BitMap = bm;
    }

    gr->title  = newgr->title;
    gr->xtitle = newgr->xtitle;
    gr->ytitle = newgr->ytitle;
    gr->FPen = newgr->FPen;
    gr->BPen = newgr->BPen;
    gr->AxesPen = newgr->AxesPen;
    gr->XlabPen = gr->YlabPen = gr->AxesPen;
    gr->TitlePen = newgr->TitlePen;
    gr->XtitlePen = gr->YtitlePen = gr->TitlePen;
    gr->flags = newgr->flags;
    gr->ctlflags = newgr->ctlflags;
    if( newgr->titleta ) {
	gr->titletf = gimmeFont( newgr->titleta );
    }
    if( newgr->xta ) {
	gr->xlabtf = gr->xtf = gimmeFont( newgr->xta );
    }
    if( newgr->yta ) {
	gr->ylabtf = gr->ytf = gimmeFont( newgr->yta );
    }

    if( !(gr->ctlflags & GGR_NOCLEARSTART) ) {
	SetRast( &gr->rp, (long) gr->BPen );
    }

    initAxis( gr, &gr->X, &newgr->X );
    initAxis( gr, &gr->Y, &newgr->Y );
    SetAPen( &gr->rp, (long) gr->FPen );
    SetBPen( &gr->rp, (long) gr->BPen );
    SetDrMd( &gr->rp, (ULONG) JAM1 );

    if( newgr->flags & (GGR_FILLTOX | GGR_FILLTOY) ) {
	if( areainfo ) {
	    gr->rp.AreaInfo = areainfo;
	} else if( !gr->rp.AreaInfo ) {
	    gr->rp.AreaInfo = chainAllocMem( &mymh,
			(ULONG)sizeof(struct AreaInfo) + 5 * 2 * sizeof(LONG),
			MEMF_PUBLIC | MEMF_CLEAR );
	    if( !gr->rp.AreaInfo ) {
		chainFreeMem( mymh );
		return( NULL );
	    }
	    InitArea( gr->rp.AreaInfo, (SHORT *)(gr->rp.AreaInfo + 1), 5L );
	}
	if( tmpras ) {
	    gr->rp.TmpRas = tmpras;
	} else if( !gr->rp.TmpRas ) {
	    gr->rp.TmpRas = chainAllocMem( &mymh, (ULONG)sizeof(struct TmpRas),
					    MEMF_PUBLIC | MEMF_CLEAR );
	    if( !gr->rp.TmpRas ) {
		chainFreeMem( mymh );
		return( NULL );
	    }
	    width = gr->rp.BitMap->BytesPerRow << 3;
	    height = gr->rp.BitMap->Rows;
	    raster = chainAllocMem( &mymh,
			(long)RASSIZE((long)width, (long)height),
			    MEMF_CHIP | MEMF_CLEAR );
	    if( !raster ) {
		chainFreeMem( mymh );
		return( NULL );
	    }
	    InitTmpRas( gr->rp.TmpRas, raster,
			    (long)RASSIZE((long)width, (long)height) );
	}
    }

    if( !(gr->ctlflags & GGR_NOLABELS) ) {
	drawGraphAxes( gr );
    }

    if( gr->ctlflags & (GGR_INITPOINT | GGR_INITORIGIN) ) {
	gr->points = 1;
    } else {
	gr->points = 0;
    }
    gr->memhead = mymh;
    return( gr );
} /* gimmeGraph */


short getRidOfGraph( gr )
    GRAPH   *gr;
{
#ifdef GIMME_WIMPY
    if( !gr ) {
	return( -1 );
    }
#endif
    if( gr->ctlflags & GGR_CLEARONEND ) {
	clearGraph( gr );
    }
    if( gr->ytf ) {
	getRidOfFont( gr->ytf );
    }
    if( gr->xtf ) {
	getRidOfFont( gr->xtf );
    }
    if( gr->titletf ) {
	getRidOfFont( gr->titletf );
    }
    chainFreeMem( gr->memhead );
    return( 0 );
} /* getRidOfGraph */


VOID clearGraph( gr )
    register GRAPH  *gr;
{
    BYTE  fpen, bpen, axespen, titlepen;
    BYTE  xlabpen, ylabpen, xtitlepen, ytitlepen;

    if( !(gr->ctlflags & GGR_NOCLEARSTART) ) {
	SetRast( &gr->rp, (long) gr->BPen );
    } else {
	fpen = gr->FPen;
	bpen = gr->BPen;
	axespen = gr->AxesPen;
	titlepen = gr->TitlePen;
	xlabpen = gr->XlabPen;
	ylabpen = gr->YlabPen;
	xtitlepen = gr->XtitlePen;
	ytitlepen = gr->YtitlePen;
	gr->FPen = bpen;
	gr->AxesPen = bpen;
	gr->TitlePen = bpen;
	gr->XlabPen = bpen;
	gr->YlabPen = bpen;
	gr->XtitlePen = bpen;
	gr->YtitlePen = bpen;
	SetAPen( &gr->rp, (long) bpen );

	RectFill( &gr->rp, (long) TRANSX(gr,1), (long) TRANSY(gr,gr->Y.size),
		    (long) TRANSX(gr,gr->X.size), (long) TRANSY(gr,1) );
	if( !(gr->ctlflags & GGR_NOLABELS) ) {
	    drawGraphAxes( gr );
	}

	gr->FPen = fpen;
	gr->AxesPen = axespen;
	gr->TitlePen = titlepen;
	gr->XlabPen = xlabpen;
	gr->YlabPen = ylabpen;
	gr->XtitlePen = xtitlepen;
	gr->YtitlePen = ytitlepen;
    }
} /* clearGraph */


VOID resetGraph( gr )
    register GRAPH  *gr;
{
    clearGraph( gr );

    gr->X.low  = gr->X.flow;
    gr->X.high = gr->X.fhigh;
    gr->X.lastdata = gr->X.fdata;
    gr->X.lastoff  = gr->X.foff;

    gr->Y.low  = gr->Y.flow;
    gr->Y.high = gr->Y.fhigh;
    gr->Y.lastdata = gr->Y.fdata;
    gr->Y.lastoff  = gr->Y.foff;

    if( gr->ctlflags & (GGR_INITPOINT | GGR_INITORIGIN) ) {
	gr->points = 1;
    } else {
	gr->points = 0;
    }

    if( !(gr->ctlflags & GGR_NOLABELS) ) {
	drawGraphAxes( gr );
    }
} /* resetGraph */


VOID drawGraphAxesOnly( gr )
    register GRAPH  *gr;
{
    SHORT   maxrastlen, maxlen;

    SetAPen( &gr->rp, (long) gr->AxesPen );
    Move( &gr->rp, (long) TRANSX(gr,0), (long) TRANSY(gr,gr->Y.size) );
    Draw( &gr->rp, (long) TRANSX(gr,0), (long) TRANSY(gr,0) );
    Draw( &gr->rp, (long) TRANSX(gr,gr->X.size), (long) TRANSY(gr,0) );
    SetAPen( &gr->rp, (long) gr->FPen );
} /* drawGraphAxesOnly */


SHORT drawGraphAxes( gr )
    register GRAPH  *gr;
{
    SHORT   maxrastlen, maxlen;
    SHORT   yoff;
    ULONG   flags;

    drawGraphAxesOnly( gr );
    maxrastlen = graphWriteLabel( gr, GWL_XAXIS, gr->X.low, gr->X.high,
				    gr->X.step );
    maxlen = graphWriteLabel( gr, GWL_YAXIS, gr->Y.low, gr->Y.high,
				gr->Y.step );

    drawGraphTitle( gr, gr->X.size >> 1, gr->Y.size + YLEEWAY,
			GPT_XCENTRE | GPT_YBOTTOM );

    yoff = - (YLEEWAY << 1);
    if( !(gr->ctlflags & GGR_NOLABELS) ) {
	yoff -= (gr->xlabtf ? gr->xlabtf->tf_YSize : gr->rp.TxHeight);
    }
    drawGraphXtitle( gr, gr->X.size >> 1, yoff, GPT_XCENTRE | GPT_YTOP );

    flags = GPT_XCENTRE | GPT_YBOTTOM | GPT_YUPWARDS;
    if( (gr->ctlflags & GGR_HIRES) && !(gr->ctlflags & GGR_INTERLACE) ) {
	flags |= GPT_XTHICKEN;
    }
    drawGraphYtitle( gr, -maxlen - XLEEWAY*2, gr->Y.size >> 1, flags );

    if( maxlen > maxrastlen ) {
	maxrastlen = maxlen;
    }
    return( maxrastlen );
} /* drawGraphAxes */


VOID drawGraphTitle( gr, xoff, yoff, myflags )
    register GRAPH  *gr;
    SHORT	    xoff, yoff;
    ULONG	    myflags;
{
    struct TextFont *tf;

    if( gr->title ) {
	tf = gr->rp.Font;
	if( gr->titletf ) {
	    SetFont( &gr->rp, gr->titletf );
	}
	SetAPen( &gr->rp, (long) gr->TitlePen );
	positionText( &gr->rp, myflags, gr->title, 0L,
			TRANSX(gr,xoff), TRANSY(gr,yoff) );
	SetAPen( &gr->rp, (long) gr->FPen );
	if( gr->titletf && tf ) {
	    SetFont( &gr->rp, tf );
	}
    }
} /* drawGraphTitle */


VOID drawGraphXtitle( gr, xoff, yoff, myflags )
    register GRAPH  *gr;
    SHORT	    xoff, yoff;
    ULONG	    myflags;
{
    struct TextFont *tf;

    if( gr->xtitle ) {
	tf = gr->rp.Font;
	if( gr->xtf ) {
	    SetFont( &gr->rp, gr->xtf );
	}
	SetAPen( &gr->rp, (long) gr->XtitlePen );
	positionText( &gr->rp, myflags, gr->xtitle, 0L,
			TRANSX(gr,xoff), TRANSY(gr,yoff) );
	SetAPen( &gr->rp, (long) gr->FPen );
	if( gr->xtf && tf ) {
	    SetFont( &gr->rp, tf );
	}
    }
} /* drawGraphXtitle */


VOID drawGraphYtitle( gr, xoff, yoff, myflags )
    register GRAPH  *gr;
    SHORT	    xoff, yoff;
    ULONG	    myflags;
{
    SHORT   destx, desty;
    struct TextFont *tf;

    if( gr->ytitle ) {
	tf = gr->rp.Font;
	if( gr->ytf ) {
	    SetFont( &gr->rp, gr->ytf );
	}
	SetAPen( &gr->rp, (long) gr->YtitlePen );
	positionText( &gr->rp, myflags, gr->ytitle, 0L,
			TRANSX(gr,xoff), TRANSY(gr,yoff) );
	SetAPen( &gr->rp, (long) gr->FPen );
	if( gr->ytf && tf ) {
	    SetFont( &gr->rp, tf );
	}
    }
} /* drawGraphYtitle */


static SHORT coordToOffset( axis, coord )
    register AXIS   *axis;
    SHORT	    coord;
{
    if( axis->scale <= 0 ) {
	return( SCALEUP(coord - axis->low) / -axis->scale );
    } else {
	return( SCALEDN((coord - axis->low) * axis->scale) );
    }
} /* coordToOffset */


static SHORT offsetToCoord( axis, offset )
    register AXIS   *axis;
    SHORT	    offset;
{
    SHORT   coord;

    if( axis->scale <= 0 ) {
	coord = SCALEDN(offset * -axis->scale);
    } else {
	/* offset in "window" belongs to next higher coordinate */
	coord = (SCALEUP(offset) + axis->scale - 1) / axis->scale;
    }
    return( axis->low + coord );
} /* offsetToCoord */


/*  internal use only!!
 *  initialize a graph's axis given a new axis structure
static VOID initAxis( gr, axis, nax )
    GRAPH	    *gr;
    register AXIS   *axis;
    register NEWAXIS *nax;
{
    SHORT   temp;
    SHORT   amt;
    ULONG   flags;

    if( axis == &gr->X ) {
	flags = gr->flags & GGR_X_FLAGS;
    } else {
	flags = gr->flags & GGR_Y_FLAGS;
    }

    axis->origin = nax->origin;
    axis->size = nax->size;
    axis->flow = axis->low = nax->low;
    axis->step = nax->step;
    axis->labnum = nax->labnum;
    if( !nax->labdenom ) {              /* to avoid divide by zero!! */
	axis->labnum = 1;
	axis->labdenom = 1;
    } else {
	axis->labdenom = nax->labdenom;
    }
    amt = nax->amt;
    if( flags & (GGR_X_SPACING | GGR_Y_SPACING) ) {
	if( !amt ) {
	    ++amt;
	}
	axis->scale = SCALEUP(amt);
	temp = axis->size;
	do {
	    axis->high = offsetToCoord( axis, temp );
	    axis->usesize = coordToOffset( axis, axis->high );
	} while( axis->usesize > temp-- );
    } else {
	if( flags & (GGR_X_INTERVALS | GGR_Y_INTERVALS) ) {
	    if( amt <= 0 ) {
		amt = 5;
	    }
	    axis->high = axis->low + amt;
	} else { /* if( flags & (GGR_X_MAX | GGR_Y_MAX) ) */
	    if( amt < axis->low ) {
		amt = axis->low + 5;
	    }
	    axis->high = amt;
	}

	temp = axis->high - axis->low;
	if( temp >= nax->size ) {
	    axis->scale = - ( SCALEUP(temp) / axis->size );
	    temp = SCALE - 1;
	    while( (axis->usesize = coordToOffset(axis, axis->high))
		    > axis->size ) {
		if( temp < 0 ) break;
		axis->scale -= SCALEUP(5) >> temp;
		--temp;
	    } /* while */

	    if( flags & (GGR_X_INTEGRAL | GGR_DELTAX |
			    GGR_Y_INTEGRAL | GGR_DELTAY) ) {
		if( (temp = SCALEUP(SCALEDN(-axis->scale))) < -axis->scale ) {
		    axis->scale = -SCALEUP( SCALEDN(-axis->scale) + 1 );
		} else {
		    axis->scale = -temp;
		}
	    }
	} else {
	    axis->scale = SCALEUP(axis->size) / temp;
	    if( flags & (GGR_X_INTEGRAL | GGR_DELTAX |
			    GGR_Y_INTEGRAL | GGR_DELTAY) ) {
		axis->scale = SCALEUP( SCALEDN(axis->scale) );
	    }
	    axis->usesize = coordToOffset( axis, axis->high );
	}
    }

    axis->fhigh = axis->high;
    if( gr->ctlflags & GGR_INITPOINT ) {
	axis->lastdata = nax->initpt;
	axis->lastoff = coordToOffset( axis, axis->lastdata );
    } else {	/* if( gr->ctlflags & GGR_INITORIGIN ) */
	axis->lastdata = axis->low;
	axis->lastoff = 1;
    }
    axis->fdata = axis->lastdata;
    axis->foff = axis->lastoff;
    if( !axis->scale ) {
	axis->scale = 1;	    /* to avoid divide by zero!! */
    }
} /* initAxis */


SHORT graphWriteLabel( gr, myflags, first, last, step )
    register GRAPH  *gr;
    ULONG	    myflags;
    SHORT	    first, last;
    SHORT	    step;
{
    AXIS    *axis;
    SHORT   full, spacing;
    SHORT   low, high;
    SHORT   maxrlen = 0, len;
    SHORT   coord, loop;
    SHORT   x, y;
    struct TextFont *tf;
    BYTE    xpen, ypen;
    BYTE    bpen, dmode;

    if( !(myflags & GWL_NO_HIGH) ) {
	if( myflags & (GWL_CLEAR_OLD | GWL_CLEAR_ONLY) ) {
	    if( myflags & GWL_XAXIS ) {
		xpen = gr->XlabPen;
		gr->XlabPen = gr->BPen;
	    } else {
		ypen = gr->YlabPen;
		gr->YlabPen = gr->BPen;
	    }
	    maxrlen = graphWriteLabel( gr,
			    myflags & ~(GWL_CLEAR_OLD | GWL_CLEAR_ONLY),
			    first, last, step );
	    if( myflags & GWL_XAXIS ) {
		gr->XlabPen = xpen;
	    } else {
		gr->YlabPen = ypen;
	    }
	    if( myflags & GWL_CLEAR_ONLY ) {
		return( maxrlen );
	    }
	}
	tf = gr->rp.Font;
	bpen = gr->rp.BgPen;
	dmode = gr->rp.DrawMode;
	if( myflags & GWL_XAXIS ) {
	    SetAPen( &gr->rp, (long) gr->XlabPen );
	    if( gr->xlabtf ) {
		SetFont( &gr->rp, gr->xlabtf );
	    }
	} else {			/* else YAXIS */
	    SetAPen( &gr->rp, (long) gr->YlabPen );
	    if( gr->ylabtf ) {
		SetFont( &gr->rp, gr->ylabtf );
	    }
	}
    }

    if( step > 0 ) {                /* if we want even-spaced labels */
	for( loop = first; loop <= last; loop += step ) {
	    if( myflags & GWL_YAXIS) {
		y = coordToOffset(&gr->Y, loop);
		WritePixel( &gr->rp, (long)TRANSX(gr,-1), (long)TRANSY(gr,y) );
		len = positionText( &gr->rp, GPT_XRIGHT | GPT_YCENTREBASE, NULL,
				(long) (loop) * gr->Y.labnum / gr->Y.labdenom,
				TRANSX(gr,-XLEEWAY), TRANSY(gr,y) );
	    } else {	/* x-axis */
		x = coordToOffset(&gr->X, loop);
		WritePixel( &gr->rp, (long)TRANSX(gr,x), (long)TRANSY(gr,-1) );
		len = positionText( &gr->rp, GPT_XCENTRE | GPT_YTOP, NULL,
				(long) (loop) * gr->X.labnum / gr->X.labdenom,
				TRANSX(gr,x), TRANSY(gr,-YLEEWAY) );
	    }
	    maxrlen = MAX(maxrlen, len);
	} /* for */
    } else {
	if( myflags & GWL_YAXIS) {
	    high = coordToOffset(&gr->Y, last);
	    if( !(myflags & GWL_NO_HIGH) ) {
		WritePixel( &gr->rp, (long)TRANSX(gr,-1),
				(long)TRANSY(gr,high) );
		maxrlen = positionText(&gr->rp, GPT_XRIGHT | GPT_YCENTREBASE,
			    NULL, (long) (last) * gr->Y.labnum / gr->Y.labdenom,
			    TRANSX(gr,-XLEEWAY), TRANSY(gr,high) );
	    }
	    low = coordToOffset(&gr->Y, first);
	    WritePixel( &gr->rp, (long)TRANSX(gr,-1), (long)TRANSY(gr,low) );
	    positionText( &gr->rp, GPT_XRIGHT | GPT_YCENTREBASE, NULL,
			    (long) (first) * gr->Y.labnum / gr->Y.labdenom,
			    TRANSX(gr,-XLEEWAY), TRANSY(gr,low) );
	    full = gr->rp.TxHeight;
	    axis = &gr->Y;
	} else {	   /* x-axis */
	    high = coordToOffset(&gr->X, last);
	    if( !(myflags & GWL_NO_HIGH) ) {
		WritePixel( &gr->rp, (long)TRANSX(gr,high),
				(long)TRANSY(gr,-1) );
		maxrlen = positionText( &gr->rp, GPT_XCENTRE| GPT_YTOP, NULL,
				(long) (last) * gr->X.labnum / gr->X.labdenom,
				TRANSX(gr,high), TRANSY(gr,-YLEEWAY) );
		full = maxrlen;
		myflags = (myflags & ~0xFFFFL) | full;
	    } else {
		full = myflags & 0xFFFFL;
	    }
	    low = coordToOffset(&gr->X, first);
	    WritePixel( &gr->rp, (long)TRANSX(gr,low), (long)TRANSY(gr,-1) );
	    positionText( &gr->rp, GPT_XCENTRE | GPT_YTOP, NULL,
			    (long) (first) * gr->X.labnum / gr->X.labdenom,
			    TRANSX(gr,low), TRANSY(gr,-YLEEWAY) );
	    axis = &gr->X;
	}
	spacing = full << 1;
	low += spacing;
	coord = offsetToCoord( axis, low );
	low = coordToOffset( axis, coord );
	if( high - low >= spacing - TOLERANCE ) {
	    graphWriteLabel( gr, myflags | GWL_NO_HIGH, coord, last, step );
	}
    }

    if( !(myflags & GWL_NO_HIGH) ) {
	SetAPen( &gr->rp, (long) gr->FPen );
	if( tf ) {
	    SetFont( &gr->rp, tf );
	}
    }
    return( maxrlen );
} /* graphWriteLabel */


VOID addToGraph( gr, x, y )
    register GRAPH  *gr;
    SHORT	    x, y;
{
    SHORT   amt;
    SHORT   newX, newY;

    newX = updateCoord( gr, &gr->X, x );
    newY = updateCoord( gr, &gr->Y, y );
    SetAPen( &gr->rp, (long) gr->FPen );
    if( !(gr->ctlflags & GGR_NOCONNECTLINE) ) {
	Move( &gr->rp, (long) TRANSX(gr,gr->X.lastoff),
				(long) TRANSY(gr,gr->Y.lastoff) );
	Draw( &gr->rp, (long)TRANSX(gr,newX), (long)TRANSY(gr,newY) );
    } else {
	Move( &gr->rp, (long)TRANSX(gr,newX), (long)TRANSY(gr,newY) );
    }

    if( gr->flags & GGR_LINETOX ) {
	Move( &gr->rp, (long)TRANSX(gr,newX), (long)TRANSY(gr,newY) );
	Draw( &gr->rp, (long)TRANSX(gr,newX), (long)TRANSY(gr,1) );
    }
    if( gr->flags & GGR_LINETOY ) {
	Move( &gr->rp, (long)TRANSX(gr,newX), (long)TRANSY(gr,newY) );
	Draw( &gr->rp, (long)TRANSX(gr,1), (long)TRANSY(gr,newY) );
    }

    if( gr->flags & GGR_FILLTOX ) {
	doBlock( &gr->rp, TRANSX(gr, gr->X.lastoff), TRANSY(gr, 1),
		    TRANSX(gr, gr->X.lastoff), TRANSY(gr, gr->Y.lastoff),
		    TRANSX(gr, newX), TRANSY(gr, newY),
		    TRANSX(gr, newX), TRANSY(gr, 1) );
    }
    if( gr->flags & GGR_FILLTOY ) {
	doBlock( &gr->rp, TRANSX(gr, 1), TRANSY(gr, gr->Y.lastoff),
		    TRANSX(gr,gr->X.lastoff), TRANSY(gr, gr->Y.lastoff),
		    TRANSX(gr, newX), TRANSY(gr, newY),
		    TRANSX(gr, 1), TRANSY(gr, newY) );
    }

    if( gr->flags & GGR_BLACKTOX ) {
	SetAPen( &gr->rp, (long) gr->BPen );
	Move( &gr->rp, (long) TRANSX(gr, gr->X.lastoff),(long)TRANSY(gr, 1) );
	Draw( &gr->rp, (long) TRANSX(gr, gr->X.lastoff),
			(long) TRANSY(gr, gr->Y.lastoff) );
	SetAPen( &gr->rp, (long) gr->FPen );
    }
    if( gr->flags & GGR_BLACKTOY ) {
	SetAPen( &gr->rp, (long) gr->BPen );
	Move(&gr->rp, (long) TRANSX(gr, 1),(long)TRANSY(gr, gr->Y.lastoff) );
	Draw(&gr->rp, (long) TRANSX(gr, gr->X.lastoff),
			  (long) TRANSY(gr, gr->Y.lastoff) );
	SetAPen( &gr->rp, (long) gr->FPen );
    }

    gr->X.lastoff = newX;
    gr->Y.lastoff = newY;
    ++gr->points;
} /* addToGraph */


/*  internal use only!!
 *  add a coordinate to an axis, doing the actual scrolling if necessary
 *  and updating some AXIS fields
 *
 *  note: truncates to the rectangle defined by the origin and the axis' size.
 *
 *  returns the standard pixel offset for the coordinate
static SHORT updateCoord( gr, axis, coord )
    GRAPH	    *gr;
    register AXIS   *axis;
    SHORT	    coord;
{
    SHORT   offset;
    SHORT   amt;
    LONG    Xamt, Yamt;
    ULONG   flags, awlflags;

    if( axis == &gr->X ) {
	flags = gr->flags & GGR_X_FLAGS;
    } else {
	flags = gr->flags & GGR_Y_FLAGS;
    }
    if( flags & (GGR_DELTAX | GGR_DELTAY) ) {
	axis->lastdata += coord;
	offset = coordToOffset( axis, axis->lastdata );
	if( offset > axis->usesize ) {
	    if( axis == &gr->X ) {
		Xamt = amt = offset - axis->lastoff;
		Yamt = 0L;
	    } else {
		Xamt = 0L;
		Yamt = amt = offset - axis->lastoff;
	    }
	    ScrollRaster( &gr->rp, (long) Xamt, (long) Yamt,
		(long) TRANSX(gr,1), (long) TRANSY(gr,gr->Y.size),
		(long) TRANSX(gr,axis->size), (long) TRANSY(gr,1) );
	    awlflags = (axis == &gr->X) ? GWL_XAXIS : GWL_YAXIS;
	    if( !(gr->ctlflags & GGR_NOLABELS) ) {
		graphWriteLabel( gr, awlflags | GWL_CLEAR_ONLY, axis->low,
				    axis->high, axis->step );
	    }
	    axis->low += coord;
	    axis->high += coord;
	    /* drawGraphAxesOnly( gr ); */
	    if( !(gr->ctlflags & GGR_NOLABELS) ) {
		graphWriteLabel( gr, awlflags, axis->low, axis->high,
					axis->step );
	    }
	    offset = axis->lastoff;
	    axis->lastoff -= amt;
	}
    } else {
	offset = coordToOffset( axis, coord );
	if( offset < 0 ) {
	    offset = 0;
	} else if( offset > axis->size ) {
	    offset = axis->size;
	}
	axis->lastdata = coord;
    }

    return( offset );
} /* updateCoord */


/*  internal use only!!
 *  do an area fill on the 4 pairs of coordinates,
 *  clipped to the rastport's bitmap size
 *  NOTE: the TmpRas should be (at least) the same size as this bitmap
static VOID doBlock( rp, x1, y1, x2, y2, x3, y3, x4, y4 )
    register struct RastPort	*rp;
    SHORT			x1, y1, x2, y2, x3, y3, x4, y4;
{
    SHORT   width, height;

    width = rp->BitMap->BytesPerRow << 3;
    height = rp->BitMap->Rows;

    if( x1 < 0 ) x1 = 0;
    else if( x1 > width ) x1 = width;
    if( x2 < 0 ) x2 = 0;
    else if( x2 > width ) x2 = width;
    if( x3 < 0 ) x3 = 0;
    else if( x3 > width ) x3 = width;
    if( x4 < 0 ) x4 = 0;
    else if( x4 > width ) x4 = width;

    if( x1 == x3 ) return;

    if( y1 < 0 ) y1 = 0;
    else if( y1 > height ) y1 = height;
    if( y2 < 0 ) y2 = 0;
    else if( y2 > height ) y2 = height;
    if( y3 < 0 ) y3 = 0;
    else if( y3 > height ) y3 = height;
    if( y4 < 0 ) y4 = 0;
    else if( y4 > height ) y4 = height;

    if( y1 == y3 ) return;

    AreaMove( rp, (long) x1, (long) y1 );
    AreaDraw( rp, (long) x2, (long) y2 );
    AreaDraw( rp, (long) x3, (long) y3 );
    AreaDraw( rp, (long) x4, (long) y4 );
    AreaEnd( rp );
} /* doBlock */
SHAR_EOF
cat << \SHAR_EOF > inputhand.c
/*
 *  FILE: inputhand.c
 *  Support routines for installing/removing an input handler.
 *
 *  Public Domain, but keep my name in it as the original author.
 *  31-Oct-88	Jan Sven Trabandt   added to gimme.lib
 */


#define I_AM_INPUTHAND
#include "gimmelib/gimmefuncs.h"


struct IOStdReq *addInputHandler( handler, data, pri, name )
    void    (*handler)();
    APTR    data;
    BYTE    pri;
    UBYTE   *name;
{
    struct MsgPort	*port;
    struct IOStdReq	*ioreq;
    struct Interrupt	*handint;

#ifdef GIMME_WIMPY
    if( !handler ) {
	return( NULL );
    }
#endif
    if( !(port = CreatePort(NULL, 0L)) ) {
	return( NULL );
    }
    if( !(ioreq = CreateStdIO(port)) ) {
	DeletePort( port );
	return( NULL );
    }
    if( !(handint = AllocMem((ULONG)sizeof(struct Interrupt),
				MEMF_PUBLIC | MEMF_CLEAR)) ) {
	DeleteStdIO( ioreq );
	DeletePort( port );
	return( NULL );
    }
    handint->is_Data = data;
    handint->is_Code = handler;
    handint->is_Node.ln_Type = NT_MESSAGE;
    handint->is_Node.ln_Pri = pri;
    handint->is_Node.ln_Name = (char *) name;
    if( OpenDevice("input.device", 0L, ioreq, 0L) ) {
	FreeMem( handint, (ULONG)sizeof(struct Interrupt) );
	DeleteStdIO( ioreq );
	DeletePort( port );
	return( NULL );
    }
    ioreq->io_Command = IND_ADDHANDLER;
    ioreq->io_Data = (APTR) handint;
    ioreq->io_Length = sizeof(struct Interrupt);
    if( DoIO(ioreq) ) {
	CloseDevice( ioreq );
	FreeMem( handint, (ULONG)sizeof(struct Interrupt) );
	DeleteStdIO( ioreq );
	DeletePort( port );
	return( NULL );
    }
    return( ioreq );
} /* addInputHandler */


short removeInputHandler( ioreq )
    struct IOStdReq *ioreq;
{
#ifdef GIMME_WIMPY
    if( !ioreq ) {
	return( -1 );
    }
#endif
    ioreq->io_Command = IND_REMHANDLER;
    ioreq->io_Message.mn_Node.ln_Type = NT_MESSAGE;
    if( DoIO(ioreq) ) {
	return( -1 );
    }
    CloseDevice( ioreq );
    FreeMem( ioreq->io_Data, (ULONG)sizeof(struct Interrupt) );
    DeletePort( ioreq->io_Message.mn_ReplyPort );
    DeleteStdIO( ioreq );
    return( 0 );
} /* removeInputHandler */
SHAR_EOF
cat << \SHAR_EOF > intuistuff.c
/*
 *  FILE: intuistuff.c
 *  Support routines for creating some useful intuition-type structures.
 *
 *  Public Domain, but keep my name in it as the original author.
 *  31-Aug-88	Jan Sven Trabandt   first release version
 *  31-Oct-88	Jan Sven Trabandt   made gimmeBorder more flexible/efficient
 */


#define I_AM_INTUISTUFF
#include "gimmelib/gimmefuncs.h"
#include "gimmelib/intuistuff.h"
#include "gimmelib/macros.h"


/* WSL-type construct */
#define GUESS		do {
#define QUIF( cond )    if( cond ) break;
#define ENDGUESS	} while( 0 );


struct Border *gimmeBorder( mh, xsize, ysize )
    void    **mh;
    SHORT   xsize, ysize;
{
    register struct Border  *bp;
    register SHORT	    *r;
    ULONG		    size;
    void		    *mymh = NULL;
    void		    **mhdr = &mymh;

    GUESS
	size = sizeof(struct Border);
	if( !mh ) {
	    r = AllocMem( (ULONG)sizeof(SHORT) * 2 * 5, MEMF_PUBLIC );
	    QUIF( !r );
	    mhdr = NULL;
	} else {
	    if( xsize > 0 && ysize > 0 ) {
		/* need room for 5 pairs of shorts as well */
		size = sizeof(struct Border) + sizeof(SHORT) * 2 * 5;
		r = NULL;
	    }
	}
	bp = chainAllocMem( mhdr, (ULONG) size, MEMF_CLEAR | MEMF_PUBLIC );
	QUIF( !bp );
	bp->FrontPen = 1;
	bp->DrawMode = JAM1;
	if( xsize > 0 && ysize > 0 ) {
	    if( !r ) {
		r = (SHORT *) (bp + 1);
	    }
	    bp->XY = r;
	    r[2] = r[4] = xsize - 1;
	    r[5] = r[7] = ysize - 1;
	    bp->Count = 5;
	}
	linkChainMem( mh, mymh );
	return( bp );
    ENDGUESS
    if( mhdr ) {
	chainFreeMem( mymh );
    } else {
	if( r ) {
	    FreeMem( r, (ULONG) sizeof(SHORT) * 2 * 5 );
	}
    }
    return( NULL );
} /* gimmeBorder */


struct Image *gimmeImage( mh, depth, width, height )
    void    **mh;
    SHORT   depth, width, height;
{
    register struct Image   *ip;
    ULONG   size;		    /* image datasize (bytes) for allocation */
    void    *mymh = NULL;
    void    **mhdr = &mymh;
    UBYTE   planepick;

    GUESS
	if( !mh ) {
	    mhdr = NULL;
	}
	ip = chainAllocMem( mhdr, (ULONG)sizeof(struct Image),
				MEMF_CLEAR | MEMF_PUBLIC );
	QUIF( !ip );
	ip->Width = width;
	ip->Height = height;
	ip->Depth = depth;
	if( depth > 0 && width > 0 && height > 0 ) {
	    size = GIM_IMAGESIZE(depth, width, height);
	    ip->ImageData = chainAllocMem( mhdr, size, MEMF_CLEAR|MEMF_CHIP );
	    QUIF( !ip->ImageData );
	    planepick = 1;
	    while( --depth > 0 ) {
		planepick = (planepick << 1) + 1;
	    } /* while */
	    ip->PlanePick = planepick;
	}
	linkChainMem( mh, mymh );
	return( ip );
    ENDGUESS
    if( mhdr ) {
	chainFreeMem( mymh );
    } else {
	if( ip ) {
	    FreeMem( ip, (ULONG)sizeof(struct Image) );
	}
    }
    return( NULL );
} /* gimmeImage */


struct IntuiText *gimmeIntuiText( mh, s, textattr, width )
    void    **mh;
    UBYTE   *s;
    struct TextAttr *textattr;
    SHORT   width;
{
    register struct IntuiText	*itp;

    GUESS
	itp = chainAllocMem( mh, (ULONG)sizeof(struct IntuiText),
				MEMF_CLEAR | MEMF_PUBLIC );
	QUIF( !itp );
	itp->FrontPen = 1;
	itp->DrawMode = JAM2;
	itp->ITextFont = textattr;
	itp->IText = s;
	if( width > 0 ) {           /* if width given, centre text */
	    itp->LeftEdge = (width - IntuiTextLength(itp)) >> 1;
	}
	return( itp );
    ENDGUESS
    return( NULL );
} /* gimmeIntuiText */
SHAR_EOF
cat << \SHAR_EOF > keyboard.c
/*
 *  FILE: keyboard.c
 *  Support routines for "cooking" raw input.
 *
 *  Public Domain, but keep my name in it as the original author.
 *  31-Aug-88	Jan Sven Trabandt   first release version
 */


#include "gimmelib/gimmefuncs.h"


SHORT deadKeyConvert( imsg, buf, bufsize, keymap )
    struct IntuiMessage *imsg;
    UBYTE		*buf;
    USHORT		bufsize;
    struct KeyMap	*keymap;
{
    struct InputEvent	ievent;

#ifdef GIMME_WIMPY
    if( !imsg || !buf || bufsize < 0 ) {
	return( -3 );
    }
#endif
    if( imsg->Class != RAWKEY ) {
	return( -2 );
    }
    ievent.ie_NextEvent = NULL;
    ievent.ie_Class = IECLASS_RAWKEY;
    ievent.ie_Code = imsg->Code;
    ievent.ie_Qualifier = imsg->Qualifier;
    ievent.ie_position.ie_addr = *((APTR *)imsg->IAddress);
    return( RawKeyConvert(&ievent, buf, (ULONG)bufsize, keymap) );
} /* deadKeyConvert */
SHAR_EOF
cat << \SHAR_EOF > memchain.c
/*
 *  FILE: memchain.c
 *  Support routines for chaining memory, anchored to a head which
 *  points to the latest chunk of memory allocated.
 *
 *  Public Domain, but keep my name in it as the original author.
 *  31-Aug-88	Jan Sven Trabandt   first release version
 *  31-Oct-88	Jan Sven Trabandt   chainAllocMem acts like normal AllocMem
 *				      if no memory-chain pointer given
 *				    parameters checked in all these functions
 */


#include "gimmelib/gimmefuncs.h"


struct memnode {
   struct memnode *next;
   ULONG size;
};


void *chainAllocMem( headptr, size, type )
    struct memnode  **headptr;
    ULONG   size;
    ULONG   type;
{
    register struct memnode *p;

    if( !headptr ) {
	p = AllocMem( size, type );
    } else {
	size += sizeof(struct memnode);
	if( p = AllocMem(size, type) ) {
	    p->size = size;
	    p->next = *headptr;
	    *headptr = p;
	    ++p;		    /* make p point to user's memory */
	}
    }
    return( p );
} /* chainAllocMem */


VOID chainFreeMem( head )
    register struct memnode *head;
{
    struct memnode  *p;

    while( head ) {
	p = head->next;
	FreeMem( head, head->size );
	head = p;
    }
} /* chainFreeMem */


short linkChainMem( headptr, head )
    struct memnode  **headptr;
    struct memnode  *head;
{
    register struct memnode *p;

    if( headptr && head ) {
	p = head;
	if( *headptr ) {
	    for( ; p->next; p = p->next ) {
	    } /* for */
	    p->next = *headptr;
	}
	*headptr = head;
    }
    return( 0 );
} /* linkChainMem */


short pluckChainMem( headptr, mem )
    struct memnode  **headptr;
    void	    *mem;
{
    register struct memnode *p;
    struct memnode	    *mine;

    if( !headptr ) {
	return( -1 );
    }
    if( mem && *headptr ) {
	mine = (struct memnode *)( ((UBYTE *) mem) -
				    (ULONG)sizeof(struct memnode) );
	if( mine == *headptr ) {
	    *headptr = mine->next;
	} else {
	    for( p = *headptr; p->next != mine; p = p->next ) {
		if( !p->next ) {
		    return( -1 );           /* not in this chain */
		}
	    } /* for */
	    p->next = mine->next;
	}
	FreeMem( mine, mine->size );
    }
    return( 0 );
} /* pluckChainMem */
SHAR_EOF
cat << \SHAR_EOF > menu.c
/*
 *  FILE: menu.c
 *  Support routines for creating menu and menu item structures.
 *
 *  Public Domain, but keep my name in it as the original author.
 *  31-Oct-88	Jan Sven Trabandt   added to gimme.lib
 */


#define I_AM_MENU
#include "gimmelib/gimmefuncs.h"
#define GIM_BUILTIN
#include "gimmelib/macros.h"

extern struct GfxBase *GfxBase;

#define XLEEWAY     10	    /* make menu item a little visually appealing */


struct Menu *gimmeMenu( mhptr, left, width, name, flags )
    void    **mhptr;
    SHORT   left, width;
    UBYTE   *name;
    ULONG   flags;
{
    register struct Menu    *menu;

    if( menu = chainAllocMem(mhptr, (ULONG)sizeof(struct Menu),
				    MEMF_PUBLIC | MEMF_CLEAR) ) {
	menu->LeftEdge = left;
	if( width >= 0 ) {
	    menu->Width = width;
	} else {
	    menu->Width = 9 * strlen( name );
	}
	menu->Height = 9;	    /* currently ignored by Intuition */
	menu->Flags = flags;
	menu->MenuName = (BYTE *) name;
    }
    return( menu );
} /* gimmeMenu */


struct MenuItem *gimmeMenuItem( mhptr, left, width, height, command, name,
				    textattr, flags )
    void	    **mhptr;
    SHORT	    left;
    SHORT	    width, height;
    BYTE	    command;
    UBYTE	    *name;
    struct TextAttr *textattr;
    ULONG	    flags;
{
    register struct MenuItem	*item;
    SHORT			temp;
    void			*mymh = NULL;

    GUESS
	QUIF( !mhptr );
	item = chainAllocMem( &mymh, (ULONG)sizeof(struct MenuItem),
				    MEMF_PUBLIC | MEMF_CLEAR );
	QUIF( !item );
	if( flags & ITEMTEXT ) {
	    item->ItemFill = (APTR) gimmeIntuiText( &mymh, name, textattr, 0 );
	    QUIF( !item->ItemFill );
	    ((struct IntuiText *)item->ItemFill)->DrawMode |= INVERSVID;
	    if( width < 0 ) {
		width = IntuiTextLength( item->ItemFill ) + XLEEWAY;
	    }
	    if( height < 0 ) {
		if( textattr ) {
		    height = textattr->ta_YSize;
		} else {
		    height = GfxBase->DefaultFont->tf_YSize;
		}
	    }
	} else {
	    item->ItemFill = (APTR) name;
	    if( flags & HIGHIMAGE ) {
		item->SelectFill = (APTR) textattr;
	    }
	    if( width < 0 ) {
		if( (struct Image *) name ) {
		    width = ((struct Image *)name)->Width;
		}
		if( (struct Image *) item->SelectFill ) {
		    temp = ((struct Image *)item->SelectFill)->Width;
		    if( temp > width ) {
			width = temp;
		    }
		}
	    }
	    if( height < 0 ) {
		if( (struct Image *) name ) {
		    height = ((struct Image *)name)->Height;
		}
		if( (struct Image *) item->SelectFill ) {
		    temp = ((struct Image *)item->SelectFill)->Height;
		    if( temp > height ) {
			height = temp;
		    }
		}
	    }
	}
	if( flags & CHECKIT ) {
	    width += CHECKWIDTH;
	}
	if( flags & COMMSEQ ) {
	    width += COMMWIDTH + 4;	/* separate text/image from commseq */
	}
	item->LeftEdge = left;
	item->Width = width;
	item->Height = height;
	item->Flags = flags;
	item->Command = command;
	linkChainMem( mhptr, mymh );
	return( item );
    ENDGUESS
    if( mymh ) {
	chainFreeMem( mymh );
    }
    return( NULL );
} /* gimmeMenuItem */
SHAR_EOF
cat << \SHAR_EOF > menustuff.c
/*
 *  FILE: menustuff.c
 *  Support routines for dealing with menus and menuitems.
 *
 *  NOTE: these routines should only be used on a menustrip that is
 *  not attached to a window or guaranteed not to be actively used by
 *  Intuition (eg. by using the MENUVERIFY flag).
 *
 *  Public Domain, but keep my name in it as the original author.
 *  31-Oct-88	Jan Sven Trabandt   added to gimme.lib
 */


#define I_AM_MENUSTUFF
#include "gimmelib/gimmefuncs.h"
#include "gimmelib/menustuff.h"


/* internal flags: must stay within defined GMI_RESERVED bits */
#define GMI_SUBITEM	(1L << 30)      /* process subitem, not item */
#define GMI_DO_TOP	(1L << 29)      /* modify top edge */
#define GMI_DO_ME	(1L << 28)      /* modify mutual exclude */
#define GMI_SNEAKY	(1L << 27)      /* sneaky -- don't modify too much */

#define GMI_ADJUST	(GMI_FIXUP | GMI_SHUFFLE)
#define GMI_ADJUSTME	(GMI_FIXUPME | GMI_SHUFFLEME)


ULONG addMenuItem( menu, item, positem, possub, numitem, myflags )
    struct Menu     *menu;
    struct MenuItem *item;
    SHORT	    positem, possub, numitem;
    ULONG	    myflags;
{
    register struct MenuItem	*temp, **old;
    struct MenuItem		*save, *savelast;
    SHORT			pos, sub;
    SHORT			top, topedge;

    if( !menu || !item || !numitem ) {
	return( MENUNULL );
    }
    if( myflags & GMI_SUBITEM ) {
	old = &((struct MenuItem *)menu)->SubItem;
    } else {
	old = &menu->FirstItem;
    }

    if( myflags & GMI_ADDSUBITEM ) {
	if( !*old ) {                   /* if no items, can't put subitem */
	    return( MENUNULL );
	}
    } else {
	/* find last item in list to be added and truncate list appropriately */
	for( temp = item; temp->NextItem; temp = temp->NextItem ) {
	    if( --numitem == 0 ) {
		temp->NextItem = NULL;
		break;
	    }
	} /* for */
	savelast = temp;	    /* save pointer to last item to be added */

	if( positem == 0 || !*old ) {       /* add as first (sub)item */
	    savelast->NextItem = *old;
	    *old = item;
	    if( *old && (myflags & (GMI_ADJUST|GMI_ADJUSTME|GMI_APPEAREND)) ) {
		pos = 0;
		save = item;
		myflags |= GMI_SNEAKY;		/* careful handling */
		goto calculate_adjustment_mi;
	    }
	    adjustMenuItems( item, savelast->NextItem, 0, 0, myflags );
	    return( SHIFTITEM(0) | SHIFTSUB(NOSUB) );
	}
    }

    /* find the item preceding where to insert, and position# for new item */
    pos = 1;
    for( temp = *old; temp->NextItem; temp = temp->NextItem ) {
	if( --positem == 0 ) {
	    break;
	}
	++pos;
    } /* for */

    if( myflags & GMI_ADDSUBITEM ) {
	sub = ITEMNUM( addMenuItem(temp, item, possub, 0, numitem,
				(myflags | GMI_SUBITEM) & ~GMI_ADDSUBITEM) );
    } else {
	savelast->NextItem = temp->NextItem;
	save = temp;		    /* save ptr to item after which to add */
	if( myflags & (GMI_ADJUST | GMI_ADJUSTME) ) {
calculate_adjustment_mi: {}
	    /* calculate top edge for new item */
	    topedge = 0;
	    for( temp = *old; temp; temp = temp->NextItem ) {
		top = temp->TopEdge + temp->Height;
		if( temp == save ) {
		    if( !(myflags & GMI_SNEAKY) ) {
			save->NextItem = item;
			if( top > topedge ) {
			    topedge = top;
			}
		    }
		    if( !(myflags & GMI_APPEAREND) ) {
			break;			/* exit for loop */
		    }
		    temp = savelast;	    /* skip over what we added */
		}
		if( top > topedge ) {
		    topedge = top;
		}
	    } /* for */
	    adjustMenuItems( item, savelast->NextItem, topedge, pos, myflags );
	} else {
	    save->NextItem = item;
	}
	sub = NOSUB;
    }

    return( SHIFTITEM(pos) | SHIFTSUB(sub) );
} /* addMenuItem */


ULONG addMenu( menuptr, menu, posmenu, nummenu, myflags )
    struct Menu     **menuptr;
    struct Menu     *menu;
    SHORT	    posmenu, nummenu;
    ULONG	    myflags;
{
    register struct Menu    *temp, **old;
    struct Menu 	    *save, *savelast;
    SHORT		    pos;
    SHORT		    left, leftedge;

    if( !nummenu || !menuptr || !menu ) {
	return( MENUNULL );
    }
    old = menuptr;

    /* find last menu in list to be added and truncate list appropriately */
    for( temp = menu; temp->NextMenu; temp = temp->NextMenu ) {
	if( --nummenu == 0 ) {
	    temp->NextMenu = NULL;
	    break;
	}
    } /* for */
    savelast = temp;		/* save pointer to last menu to be added */

    if( posmenu == 0 || !*old ) {       /* add to front of menu strip */
	savelast->NextMenu = *old;
	*old = menu;
	if( *old && (myflags & (GMI_ADJUST | GMI_APPEAREND)) ) {
	    pos = 0;
	    save = menu;
	    myflags |= GMI_SNEAKY;		/* careful handling */
	    goto calculate_adjustment_m;
	}
	adjustMenuLefts( menu, savelast->NextMenu, 0, myflags );
	return( SHIFTMENU(0) | SHIFTITEM(NOITEM) | SHIFTSUB(NOSUB) );
    }

    /* find the menu preceding where to insert, and position# for new menu */
    pos = 1;
    for( temp = *old; temp->NextMenu; temp = temp->NextMenu ) {
	if( --posmenu == 0 ) {
	    break;
	}
	++pos;
    } /* for */

    savelast->NextMenu = temp->NextMenu;
    save = temp;		/* save ptr to menu after which to add */
    if( myflags & (GMI_ADJUST | GMI_ADJUSTME) ) {
calculate_adjustment_m: {}
	/* calculate left edge for new menu */
	leftedge = 0;
	for( temp = *old; temp; temp = temp->NextMenu ) {
	    left = temp->LeftEdge + temp->Width;
	    if( temp == save ) {
		if( !(myflags & GMI_SNEAKY) ) {
		    save->NextMenu = menu;
		    if( left > leftedge ) {
			leftedge = left;
		    }
		}
		if( !(myflags & GMI_APPEAREND) ) {
		    break;		    /* exit for loop */
		}
		temp = savelast;	/* skip over what we added */
	    }
	    if( left > leftedge ) {
		leftedge = left;
	    }
	} /* for */
	adjustMenuLefts( menu, savelast->NextMenu, leftedge, myflags );
    } else {
	save->NextMenu = menu;
    }

    return( SHIFTMENU(pos) | SHIFTITEM(NOITEM) | SHIFTSUB(NOSUB) );
} /* addMenu */


struct MenuItem *removeMenuItem( menu, item, positem, possub, numitem, myflags )
    struct Menu     *menu;
    struct MenuItem *item;
    SHORT	    positem, possub, numitem;
    ULONG	    myflags;
{
    register struct MenuItem	*temp, **old;
    struct MenuItem		*save;
    SHORT			pos;	    /* position for mutual exclude */

    if( !menu ) {
	return( NULL );
    }
    if( myflags & GMI_SUBITEM ) {
	old = &((struct MenuItem *)menu)->SubItem;
    } else {
	old = &menu->FirstItem;
    }
    if( !*old || !numitem ) {           /* if no menu items */
	return( NULL );
    }
    if( myflags & GMI_REMSUBITEM ) {
	save = item;		    /* don't want to check item address yet */
	item = NULL;
    } else {
	if( item ) {
	    positem = -1;
	}
    }
    pos = 0;
    temp = *old;
    if( temp != item && positem != 0 && temp->NextItem ) {
	/* find the "item" preceding where to remove */
	for( ; temp->NextItem && --positem != 0; ) {
	    ++pos;
	    if( temp->NextItem == item ) {      /* found the item */
		break;
	    }
	    temp = temp->NextItem;	    /* next in loop */
	    if( !temp->NextItem ) {         /* no more to follow so stop */
		break;
	    }
	} /* for */
	old = &temp->NextItem;
    }
    /* now old points to a MenuItem pointer to item to be removed */
    if( item && *old != item ) {            /* if its not the right item */
	return( NULL );
    }

    if( myflags & GMI_REMSUBITEM ) {
	item = removeMenuItem( temp, save, possub, 0, numitem,
				(myflags | GMI_SUBITEM) & ~GMI_REMSUBITEM );
    } else {
	if( !item ) {
	    item = *old;
	}
	if( myflags & GMI_ADDRONLY ) {
	    return( item );
	}
	/* find last one to be removed */
	for( temp = item; temp->NextItem; temp = temp->NextItem ) {
	    if( --numitem == 0 ) {
		break;
	    }
	} /* for */
	*old = temp->NextItem;
	temp->NextItem = NULL;
	myflags &= ~(GMI_FIXUP | GMI_FIXUPME);
	if( myflags & GMI_SHUFFLE ) {
	    myflags |= GMI_FIXUP;
	}
	if( myflags & GMI_SHUFFLEME ) {
	    myflags |= GMI_FIXUPME;
	}
	adjustMenuItems( *old, NULL, item->TopEdge, pos, myflags );
    }

    return( item );
} /* removeMenuItem */


struct Menu *removeMenu( menuptr, menu, posmenu, nummenu, myflags )
    struct Menu     **menuptr;
    struct Menu     *menu;
    SHORT	    posmenu, nummenu;
    ULONG	    myflags;
{
    register struct Menu    *temp, **old;

    if( !nummenu || !menuptr || !*menuptr ) {
	return( NULL );
    }
    old = menuptr;
    if( menu ) {
	posmenu = -1;
    }
    temp = *old;
    if( temp != menu && posmenu != 0 && temp->NextMenu ) {
	/* find the menu preceding where to remove */
	for( ; temp->NextMenu && --posmenu != 0; ) {
	    if( temp->NextMenu == menu ) {      /* found the menu */
		break;
	    }
	    temp = temp->NextMenu;	    /* next in loop */
	    if( !temp->NextMenu ) {         /* no more to follow so stop */
		break;
	    }
	} /* for */
	old = &temp->NextMenu;
    }
    /* now old points to a Menu pointer to menu to be removed */
    if( menu && *old != menu ) {            /* if its not the right menu */
	return( NULL );
    }

    if( !menu ) {
	menu = *old;
    }
    if( myflags & GMI_ADDRONLY ) {
	return( menu );
    }
    /* find last one to be removed */
    for( temp = menu; temp->NextMenu; temp = temp->NextMenu ) {
	if( --nummenu == 0 ) {
	    break;
	}
    } /* for */
    *old = temp->NextMenu;
    temp->NextMenu = NULL;
    adjustMenuLefts( *old, NULL, menu->LeftEdge, myflags );

    return( menu );
} /* removeMenu */


short adjustMenuItems( item, stop, topedge, meposition, myflags )
    struct MenuItem *item, *stop;
    SHORT	    topedge, meposition;
    register ULONG  myflags;
{
    register struct MenuItem	*temp;

    if( !(myflags & (GMI_ADJUST | GMI_ADJUSTME)) ) {
	return( -1 );
    }
    if( myflags & GMI_FIXUP ) {
	myflags |= GMI_DO_TOP;
    }
    if( myflags & GMI_FIXUPME ) {
	myflags |= GMI_DO_ME;
    }
    for( temp = item; temp; temp = temp->NextItem ) {
	if( temp == stop ) {
	    if( !(myflags & (GMI_SHUFFLE | GMI_SHUFFLEME)) ) {
		break;
	    }
	    myflags &= ~(GMI_DO_TOP | GMI_DO_ME);
	    if( (myflags & GMI_SHUFFLE) && !(myflags & GMI_APPEAREND) ) {
		myflags |= GMI_DO_TOP;
	    }
	    if( myflags & GMI_SHUFFLEME ) {
		myflags |= GMI_DO_ME;
	    }
	}
	if( myflags & GMI_DO_TOP ) {
	    temp->TopEdge = topedge;
	}
	topedge += temp->Height;
	if( myflags & GMI_DO_ME ) {
	    temp->MutualExclude = ~(1L << meposition);
	}
	++meposition;
    } /* for */
    return( 0 );
} /* adjustMenuItems */


short adjustMenuLefts( menu, stop, leftedge, myflags )
    struct Menu     *menu, *stop;
    register SHORT  leftedge;
    ULONG	    myflags;
{
    register struct Menu    *temp;

    if( !(myflags & (GMI_ADJUST)) ) {
	return( -1 );
    }
    for( temp = menu; temp; temp = temp->NextMenu ) {
	if( !(myflags & GMI_SHUFFLE) && temp == stop ) {
	    break;
	}
	temp->LeftEdge = leftedge;
	leftedge += temp->Width;
    } /* for */
    return( 0 );
} /* adjustMenuLefts */
SHAR_EOF
cat << \SHAR_EOF > message.c
/*
 *  FILE: message.c
 *  Support routines for dynamic (de)allocation of [extended] message structs
 *
 *  Public Domain, but keep my name in it as the original author.
 *  31-Aug-88	Jan Sven Trabandt   first release version
 */


#include "gimmelib/gimmefuncs.h"


struct Message *gimmeMessage( size, replyport )
    ULONG	    size;
    struct MsgPort  *replyport;
{
    struct Message  *msg;

#ifdef GIMME_WIMPY
    if( size > 0x0FFFFL ) {
	return( NULL );
    }
#endif
    msg = (struct Message *) AllocMem( size, MEMF_PUBLIC );
    if( msg ) {
	msg->mn_Node.ln_Type = NT_FREEMSG;
	msg->mn_Node.ln_Pri = 0;
	msg->mn_Node.ln_Name = NULL;
	msg->mn_ReplyPort = replyport;
	msg->mn_Length = size;
    }
    return( msg );
} /* gimmeMessage */


short getRidOfMessage( msg )
    struct Message  *msg;
{
#ifdef GIMME_WIMPY
    if( !msg || !msg->mn_Length > 0 ) {
	return( -1 );
    }
#endif
    FreeMem( msg, (ULONG) msg->mn_Length );
} /* getRidOfMessage */
SHAR_EOF
#	End of shell archive
exit 0
-- 
Bob Page, U of Lowell CS Dept.  page@swan.ulowell.edu  ulowell!page
Have five nice days.