/*
 *  FILE: gadget.c
 *  Support routines for creating and dealing with gadgets.
 *
 *  Public Domain, but keep my name in it as the original author.
 *  31-Aug-88	Jan Sven Trabandt   first release version
 *  30-Sep-88	Jan Sven Trabandt   enhanced routines
 *				    renamed getRidOfGadget to getRidOfGadgets
 *  31-Oct-88	Jan Sven Trabandt   keep up with clearGadgets change
 *				    moved toggleBoolGadget to gadgstuff.c
 */


#define I_AM_GADGET
#include "gimmelib/gimmefuncs.h"
#include "gimmelib/gadget.h"
#include "gimmelib/postext.h"
#include <graphics/gfxbase.h>
#define GIM_BUILTIN
#include "gimmelib/macros.h"

extern struct GfxBase *GfxBase;

/* one undo buffer is safe for several string gadgets since under Intuition
 * only one string gadget can be truly active at any time (see manual)
static UBYTE defaultUndoBuffer[GIM_DFLT_UNDO_BUF_SIZE];


short getRidOfGadgets( gp )
    register struct Gadget  *gp;
{
    struct Gadget   *temp;

    while( gp ) {
	temp = gp->NextGadget;
	if( gp->UserData ) {
	    chainFreeMem( gp->UserData );
	}
	gp = temp;
    } /* while */
    return( 0 );
} /* getRidOfGadgets */


struct Gadget *gimmeBoolGadget( window, id, left, top, xsize, ysize, s, s2,
				    textattr, myflags )
    struct Window   *window;
    USHORT	    id;
    SHORT	    left, top;
    register SHORT  xsize, ysize;
    UBYTE	    *s, *s2;
    struct TextAttr *textattr;
    ULONG	    myflags;
{
    register struct Gadget  *gp;
    struct IntuiText	    *itp, *spitp;
    struct Border	    *bp;
    UBYTE   *spstr;
    void    *gadgmh = NULL;
    SHORT   wid, tempwid, maxlen;
    BYTE    fpen, bpen, drawmode, pad;
    SHORT   i;

    GUESS
	QUIF( !id );
	QUIF( !s );

	gp = chainAllocMem( &gadgmh, (ULONG)sizeof(struct Gadget),
				MEMF_CLEAR | MEMF_PUBLIC );
	QUIF( !gp );
	itp = gimmeIntuiText( &gadgmh, s, textattr, 0 );
	QUIF( !itp );
	if( window ) {
	    if( (fpen = window->RPort->FgPen) == (BYTE)(-1) ) {
		fpen = 1;
	    }
	    if( (bpen = window->RPort->BgPen) == (BYTE)(-1) ) {
		bpen = 0;
	    }
	    drawmode = window->RPort->DrawMode;
	    itp->FrontPen = fpen;
	    itp->BackPen = bpen;
	    itp->DrawMode = drawmode;
	}
	wid = IntuiTextLength( itp ) + (HORIZ_SPACE << 1);
	itp->LeftEdge = HORIZ_SPACE - AROUND_SPACE;
	itp->TopEdge = AROUND_SPACE;
	gp->GadgetText = itp;
	if( s2 ) {                      /* toggle type desired */
	    itp = gimmeIntuiText( &gadgmh, s2, textattr, 0 );
	    QUIF( !itp );
	    tempwid = IntuiTextLength( itp ) + (HORIZ_SPACE << 1);
	    itp->LeftEdge = HORIZ_SPACE - AROUND_SPACE;
	    itp->TopEdge = AROUND_SPACE;
	    if( window ) {
		itp->FrontPen = fpen;
		itp->BackPen = bpen;
		itp->DrawMode = drawmode;
	    }
	    spitp = gimmeIntuiText( &gadgmh, " ", textattr, 0 );
	    QUIF( !spitp );
	    if( window ) {
		spitp->FrontPen = fpen;
		spitp->BackPen = bpen;
		spitp->DrawMode = drawmode;
	    }
	    if( tempwid > wid ) {           /* second string is longer */
		gp->GadgetText->LeftEdge += (tempwid - wid) >> 1;
		wid = tempwid;
		maxlen = (wid - (HORIZ_SPACE << 1)) / IntuiTextLength( spitp );
		*spitp = *itp;		/* struct copy */
		spitp->NextText = gp->GadgetText;
		gp->GadgetText = spitp;
		gp->SelectRender = (APTR) itp;
	    } else {
		itp->LeftEdge += (wid - tempwid) >> 1;
		maxlen = (wid - (HORIZ_SPACE << 1)) / IntuiTextLength( spitp );
		*spitp = *(gp->GadgetText);         /* struct copy */
		spitp->NextText = itp;
		gp->SelectRender = (APTR) spitp;
	    }
	    spstr = (UBYTE *) chainAllocMem( &gadgmh,
			    (LONG)((maxlen+1) * sizeof(UBYTE)), MEMF_PUBLIC );
	    QUIF( !spstr );
	    spstr[maxlen] = '\0';
	    for( i = maxlen; --i >= 0; ) {
		spstr[i] = ' ';
	    } /* for */
	    spitp->IText = spstr;
	    /* use SelectRender since not using GADGHIMAGE so it's safe */
	    gp->Flags = GADGHNONE;
	    gp->Activation = GADGIMMEDIATE | TOGGLESELECT;
	} else {
	    gp->Flags = GADGHCOMP;
	    gp->Activation = RELVERIFY;
	}
	if( xsize <= 0 ) {
	    xsize = wid;
	}
	if( ysize <= 0 ) {
	    if( textattr ) {
		ysize = textattr->ta_YSize;
	    } else if( window ) {
		ysize = window->RPort->TxHeight;
	    } else {
		ysize = GfxBase->DefaultFont->tf_YSize;
	    }
	    ysize += VERT_SPACE << 1;
	}

	if( xsize == wid ) {            /* if xsize was exact or <= 0 */
	    tempwid = -wid;
	} else {
	    tempwid = xsize - wid;
	    if( !(myflags & GPT_FULLWIDTH) ) {
		xsize -= tempwid;
	    }
	}
	switch( myflags & GPT_XFLAGS ) {
	  case GPT_XCENTRE:
	    left += tempwid >> 1;
	    break;
	  case GPT_XRIGHT:	    /* right-align if width was given */
	    left += tempwid;
	  case GPT_XLEFT:
	  default:
	    break;
	} /* switch */

	switch( myflags & GPT_YFLAGS ) {
	  case GPT_YBOTTOM:
	    top -= ysize;
	    break;
	  case GPT_YCENTRE:
	  case GPT_YCENTREBASE:     /* approx */
	    top -= ysize >> 1;
	    break;
	  case GPT_YBASELINE:	    /* approx */
	    top -= ysize - (VERT_SPACE << 1);
	    break;
	  case GPT_YTOP:
	  default:
	    break;
	} /* switch */
	gp->LeftEdge = left + AROUND_SPACE;
	gp->TopEdge = top + AROUND_SPACE;
	gp->Width = xsize - (AROUND_SPACE << 1);
	gp->Height = ysize - (AROUND_SPACE << 1);
	bp = gimmeBorder( &gadgmh, xsize, ysize );
	QUIF( !bp );
	bp->LeftEdge = - AROUND_SPACE;
	bp->TopEdge = - AROUND_SPACE;
	if( window ) {
	    bp->FrontPen = fpen;
	    bp->BackPen = bpen;
	    bp->DrawMode = drawmode;
	}
	gp->GadgetType = BOOLGADGET;
	gp->GadgetRender = (APTR) bp;
	if( window ) {
	    if( window->Flags & GIMMEZEROZERO ) {
		gp->GadgetType |= GZZGADGET;
	    }
	}
	gp->GadgetID = id;
	gp->UserData = gadgmh;
	return( gp );
    ENDGUESS
    if( gadgmh ) {
	chainFreeMem( gadgmh );
    }
    return( NULL );
} /* gimmeBoolGadget */


struct Gadget *gimmeBoolImageGadget( window, id, left, top, depth, width,
					height, myflags, dep2, wid2, ht2 )
    struct Window   *window;
    USHORT	    id;
    SHORT	    left, top;
    SHORT	    depth, width, height;
    ULONG	    myflags;
    SHORT	    dep2, wid2, ht2;
{
    register struct Gadget  *gp;
    UBYTE   *spstr;
    void    *gadgmh = NULL;

    GUESS
	QUIF( !id );

	gp = chainAllocMem( &gadgmh, (ULONG)sizeof(struct Gadget),
				MEMF_CLEAR | MEMF_PUBLIC );
	QUIF( !gp );
	gp->GadgetRender = (APTR) gimmeImage( &gadgmh, depth, width, height );
	QUIF( !gp->GadgetRender );
	if( dep2 > 0 && wid2 > 0 && ht2 > 0 ) {
	    gp->SelectRender = (APTR) gimmeImage( &gadgmh, dep2, wid2, ht2 );
	    QUIF( !gp->SelectRender );
	    gp->Flags = GADGIMAGE | GADGHIMAGE;
	    gp->Activation = GADGIMMEDIATE | TOGGLESELECT;
	} else {
	    gp->Flags = GADGIMAGE | GADGHCOMP;
	    gp->Activation = RELVERIFY;
	}
	switch( myflags & GPT_XFLAGS ) {
	  case GPT_XCENTRE:
	    left -= width >> 1;
	    break;
	  case GPT_XRIGHT:
	    left -= width;
	  case GPT_XLEFT:
	  default:
	    break;
	} /* switch */
	switch( myflags & GPT_YFLAGS ) {
	  case GPT_YBOTTOM:
	  case GPT_YBASELINE:	    /* no "real" meaning, so use bottom */
	    top -= height;
	    break;
	  case GPT_YCENTRE:
	  case GPT_YCENTREBASE:     /* no "real" meaning, so use centre */
	    top -= height >> 1;
	    break;
	  case GPT_YTOP:
	  default:
	    break;
	} /* switch */
	gp->LeftEdge = left;
	gp->TopEdge = top;
	gp->Width = width;
	gp->Height = height;
	gp->GadgetType = BOOLGADGET;
	if( window && (window->Flags & GIMMEZEROZERO) ) {
	    gp->GadgetType |= GZZGADGET;
	}
	gp->GadgetID = id;
	gp->UserData = gadgmh;
	return( gp );
    ENDGUESS
    if( gadgmh ) {
	chainFreeMem( gadgmh );
    }
    return( NULL );
} /* gimmeBoolImageGadget */


struct Gadget *gimmePropGadget( window, id, left, top, xsize, ysize, label,
				textattr, activflags, propflags )
    struct Window   *window;
    USHORT	    id;
    SHORT	    left, top, xsize, ysize;
    UBYTE	    *label;
    struct TextAttr *textattr;
    ULONG	    activflags, propflags;
{
    register struct Gadget  *gp;
    SHORT   len;
    BYTE    fpen, bpen;
    void    *gadgmh = NULL;

    GUESS
	QUIF( !id );
	gp = chainAllocMem( &gadgmh, (ULONG)sizeof(struct Gadget),
				MEMF_CLEAR | MEMF_PUBLIC );
	QUIF( !gp );
	gp->SpecialInfo = (APTR) gimmePropInfo( &gadgmh, propflags );
	QUIF( !gp->SpecialInfo );
	gp->LeftEdge = left;
	gp->TopEdge = top;
	if( label ) {
	    gp->GadgetText = gimmeIntuiText( &gadgmh, label, textattr, 0 );
	    QUIF( !gp->GadgetText );
	    len = IntuiTextLength( gp->GadgetText ) + HORIZ_SPACE;
	    gp->GadgetText->LeftEdge = -len;
	    gp->LeftEdge += len;
	    gp->GadgetText->FrontPen = 1;
	    gp->GadgetText->BackPen = 0;
	    if( window ) {
		if( (fpen = window->RPort->FgPen) != (BYTE)(-1) ) {
		    gp->GadgetText->FrontPen = fpen;
		}
		if( (bpen = window->RPort->BgPen) != (BYTE)(-1) ) {
		    gp->GadgetText->BackPen = bpen;
		}
		gp->GadgetText->DrawMode = window->RPort->DrawMode;
	    }
	}
	gp->Width = xsize;
	gp->Height = ysize;
	gp->Flags = GADGHCOMP | GADGIMAGE;
	gp->Activation = RELVERIFY | activflags;
	gp->GadgetType = PROPGADGET;
	gp->GadgetRender = chainAllocMem( &gadgmh, (ULONG)sizeof(struct Image),
					    MEMF_CLEAR | MEMF_PUBLIC );
	QUIF( !gp->GadgetRender );
	if( window && (window->Flags & GIMMEZEROZERO) ) {
	    gp->GadgetType |= GZZGADGET;
	}
	gp->GadgetID = id;
	gp->UserData = gadgmh;
	return( gp );
    ENDGUESS
    if( gadgmh ) {
	chainFreeMem( gadgmh );
    }
    return( NULL );
} /* gimmePropGadget */


struct PropInfo *gimmePropInfo( mh, flags )
    void    **mh;
    ULONG   flags;
{
    register struct PropInfo	*pip;

    pip = chainAllocMem( mh, (ULONG)sizeof(struct PropInfo),
			    MEMF_PUBLIC | MEMF_CLEAR );
    if( pip ) {
	pip->Flags = AUTOKNOB | flags;
	pip->HorizBody = 1;
	pip->VertBody = 1;
    }
    return( pip );
} /* gimmePropInfo */


struct Gadget *gimmeStringGadget( window, id, left, top, width, maxbuf,
				    initstr, label, textattr, activflags )
    struct Window   *window;
    USHORT	    id;
    SHORT	    left, top, width;
    SHORT	    maxbuf;
    UBYTE	    *initstr, *label;
    struct TextAttr *textattr;
    ULONG	    activflags;
{
    register struct Gadget  *gp;
    struct Border   *bp;
    SHORT   xsize, ysize;
    SHORT   len;
    BYTE    fpen, bpen, drawmode, pad;
    void    *gadgmh = NULL;

    GUESS
	QUIF( !id );
	gp = chainAllocMem( &gadgmh, (ULONG)sizeof(struct Gadget),
				MEMF_CLEAR | MEMF_PUBLIC );
	QUIF( !gp );
	gp->SpecialInfo = (APTR) gimmeStringInfo( &gadgmh, maxbuf, initstr,
						    activflags );
	QUIF( !gp->SpecialInfo );
	if( window ) {
	    if( (fpen = window->RPort->FgPen) == (BYTE)(-1) ) {
		fpen = 1;
	    }
	    if( (bpen = window->RPort->BgPen) == (BYTE)(-1) ) {
		bpen = 0;
	    }
	    drawmode = window->RPort->DrawMode;
	}
	if( textattr ) {
	    ysize = textattr->ta_YSize;
	} else if( window ) {
	    ysize = window->WScreen->Font->ta_YSize;
	} else {
	    ysize = GfxBase->DefaultFont->tf_YSize;
	}
	ysize += VERT_SPACE << 1;
	gp->LeftEdge = left + HORIZ_SPACE;
	gp->TopEdge = top + VERT_SPACE;
	if( label ) {
	    gp->GadgetText = gimmeIntuiText( &gadgmh, label, textattr, 0 );
	    QUIF( !gp->GadgetText );
	    len = IntuiTextLength( gp->GadgetText ) + (HORIZ_SPACE << 1);
	    gp->GadgetText->LeftEdge = -len;
	    if( window ) {
		gp->GadgetText->FrontPen = fpen;
		gp->GadgetText->BackPen = bpen;
		gp->GadgetText->DrawMode = drawmode;
	    }
	    width -= len;
	    gp->LeftEdge += len - HORIZ_SPACE;
	}
	gp->Width = width - HORIZ_SPACE - 1;
	gp->Height = ysize - VERT_SPACE - 1;
	bp = gimmeBorder( &gadgmh, width, ysize );
	/* QUIF( !bp ); */
	bp->LeftEdge = - HORIZ_SPACE;
	bp->TopEdge = - VERT_SPACE;
	if( window ) {
	    bp->FrontPen = fpen;
	    bp->BackPen = bpen;
	    bp->DrawMode = drawmode;
	}
	gp->Flags = GADGHCOMP;
	gp->Activation = RELVERIFY | activflags;
	gp->GadgetType = STRGADGET;
	gp->GadgetRender = (APTR) bp;
	if( window && (window->Flags & GIMMEZEROZERO) ) {
	    gp->GadgetType |= GZZGADGET;
	}
	gp->GadgetID = id;
	gp->UserData = gadgmh;
	return( gp );
    ENDGUESS
    if( gadgmh ) {
	chainFreeMem( gadgmh );
    }
    return( NULL );
} /* gimmeStringGadget */


struct StringInfo *gimmeStringInfo( mh, bufsize, s, flags )
    void    **mh;
    SHORT   bufsize;
    UBYTE   *s;
    ULONG   flags;
{
    register struct StringInfo	*sip;
    UBYTE   tempchar;
    short   len;
    void    *mymh = NULL;

    GUESS
	sip = chainAllocMem( &mymh, (ULONG)sizeof(struct StringInfo),
				MEMF_PUBLIC | MEMF_CLEAR );
	QUIF( !sip );
	sip->Buffer = chainAllocMem( &mymh, (LONG) bufsize,
					MEMF_PUBLIC | MEMF_CLEAR );
	QUIF( !sip->Buffer );
	if( bufsize <= GIM_DFLT_UNDO_BUF_SIZE ) {
	    sip->UndoBuffer = defaultUndoBuffer;
	} else {
	    sip->UndoBuffer = chainAllocMem( &mymh, (LONG) bufsize,
					MEMF_PUBLIC | MEMF_CLEAR );
	    QUIF( !sip->UndoBuffer );
	}
	sip->MaxChars = bufsize--;
	len = strlen( s );
	--bufsize;
	if( len > bufsize ) {           /* make sure string not too long */
	    tempchar = s[bufsize];
	    s[bufsize] = '\0';
	}
	strcpy( sip->Buffer, s );
	if( len > bufsize ) {
	    s[bufsize] = tempchar;
	}
	if( flags & LONGINT ) {
	    sip->LongInt = atol( s );
	}
	linkChainMem( mh, mymh );
	return( sip );
    ENDGUESS
    if( mymh ) {
	chainFreeMem( mymh );
    }
    return( NULL );
} /* gimmeStringInfo */
