/* ============================================================================ *
                              Talin's Application Shell
                      box.c -- an image class for beveled boxes
                                      By Talin.
                               ©1991 The Dreamers Guild
 * ============================================================================ *
       This material is confidential -- do not distribute without authorization
 * ============================================================================ */

#include <exec/types.h>
#include <graphics/gfxmacros.h>
#include <intuition/cghooks.h>
#include <intuition/classusr.h>
#include <intuition/classes.h>
#include <intuition/imageclass.h>

#include <clib/exec_protos.h>
#include <clib/graphics_protos.h>
#include <clib/intuition_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/utility_protos.h>
#include <clib/realtime_protos.h>
#include <clib/alib_protos.h>
#include <clib/macros.h>

#include <pragmas/exec_pragmas.h>
#include <pragmas/graphics_pragmas.h>
#include <pragmas/intuition_pragmas.h>
#include <pragmas/gadtools_pragmas.h>
#include <pragmas/utility_pragmas.h>
#include <pragmas/realtime_pragmas.h>

#include <stdlib.h>
#include <string.h>

#include "boximage.h"

#define	IM(o)	((struct Image *)(o))	/* transparent base	class */

extern struct Library	*IntuitionBase,
						*GfxBase,
						*UtilityBase;

struct BoxData {
	UWORD				*glyph_data;			/* data for glyph in center		*/
	WORD				flags;					/* flags for this image			*/
};

static drawFrame1( Class *cl, struct Image	*img, struct impDraw *msg );

static struct TagItem maskmap[] = {				/* used by PackBoolTags			*/
	BOX_Beveled,	BOX_BEVELED,
	BOX_Filled,		BOX_FILLED,
	BOX_ThickBevel,	BOX_THICK_BEVEL,
	BOX_PushesIn,	BOX_PUSHES_IN,
	BOX_LightFill,	BOX_LIGHT_FILL,
	BOX_BorderFill, BOX_IN_BORDER,
	TAG_DONE,		0
};

static int setBoxAttrs( Class *cl, Object *o, struct opSet *msg )
{	struct TagItem		*tags = msg->ops_AttrList,
						*tlist = tags;
	struct Image		*im = (struct Image *)o,
						old_im;
	struct BoxData		*bd = INST_DATA( cl, o ),
						old_bd;
	int					changes = 0;

	old_bd = *bd;								/* save old state				*/
	old_im = *im;

		/* pack all the boolean tags into the flags field */

	bd->flags = PackBoolTags(bd->flags,tags,maskmap);

		/* iterate through the other tags and setup what values are needed */

	while (tags = NextTagItem(&tlist))
	{	switch (tags->ti_Tag) {
		case BOX_GlyphData: bd->glyph_data = (void *)tags->ti_Data; break;
		case BOX_GlyphWidth: im->PlanePick = tags->ti_Data; break;
		case BOX_GlyphHeight: im->PlaneOnOff = tags->ti_Data; break;
		}
	}

	if ( memcmp(bd,&old_bd,sizeof *bd) || memcmp(im,&old_im,sizeof *im) )
	{	changes	|= 1;
	}
	return changes;
}

static long __asm __saveds ourhook (
	register __a0 Class *cl,
	register __a1 struct impDraw *msg,
	register __a2 struct Image *img )
{	struct Image		*ni;
	struct BoxData		*bd;

	switch ( msg->MethodID ) {
	case IM_DRAW:								/* draw	with state				*/
	case IM_DRAWFRAME:							/* special case	of draw			*/
		return ( (long)drawFrame1( cl, img, (struct impDraw *)msg ) );

	case IM_HITTEST:							/* return "TRUE" for hit test	*/
	case IM_HITFRAME:
		return TRUE;

#if 0
	case IM_FRAMEBOX:
	case IM_ERASE:
	case IM_ERASEFRAME:
#endif

	default:
		return (long) DoSuperMethodA( cl, (Object *)img, (Msg)msg );

	case OM_NEW:
		if (!(ni = (struct Image *) DoSuperMethodA( cl, (Object *)img, (Msg)msg )))
			return NULL;
		bd = INST_DATA( cl, (Object *)ni );
		bd->flags = BOX_BEVELED;
		setBoxAttrs( cl, (Object *)ni, (struct opSet *)msg );
		return (long)ni;
	}
	return 0;
}

static drawFrame1( Class *cl, struct Image	*img, struct impDraw *msg )
{	struct BoxImage		*bi = (struct BoxImage *)_OBJECT(img);
	struct IBox			box;
	UWORD				*pens;					/* pen spec	array				*/
	UWORD				ulpen,					/* upper left					*/
						lrpen,					/* lower right					*/
						fillpen,				/* filled area					*/
						glyphpen;				/* glyph 						*/
	WORD				hthick;
	WORD				flags = bi->flags;
	struct RastPort		*rp = msg->imp_RPort;

	hthick = (flags & BOX_THICK_BEVEL) ? 2 : 1;

	/* let's be sure that we were passed a DrawInfo */

	pens = msg->imp_DrInfo->dri_Pens;
	SetupImageIBox(img,(struct impDraw *)msg,&box);
	glyphpen = pens[ textPen ];

	switch ( msg->imp_State	) {
	case IDS_SELECTED:
	case IDS_INACTIVESELECTED:
		if (flags & BOX_PUSHES_IN)
		{	ulpen   = pens[	shadowPen ];
			lrpen   = pens[	shinePen ];
		}
		else
		{	ulpen   = pens[	shinePen ];
			lrpen   = pens[	shadowPen ];
		}
		if (flags & BOX_LIGHT_FILL) fillpen = pens[shinePen];
		else fillpen = pens[ !(flags & BOX_IN_BORDER) || msg->imp_State >=IDS_INACTIVENORMAL ? hifillPen : backgroundPen ];
		break;

	case IDS_NORMAL:
	case IDS_DISABLED:
	case IDS_INACTIVENORMAL:
	case IDS_INACTIVEDISABLED:
	default:
		ulpen   = pens[	shinePen ];
		lrpen   = pens[	shadowPen ];
		fillpen = pens[ !(flags & BOX_IN_BORDER) || msg->imp_State >= IDS_INACTIVENORMAL ? backgroundPen : hifillPen ];
		break;
	}

	rp->Mask = (UBYTE)~0;
	SetDrMd(rp,	JAM1);

	if (hthick > 1) DrawBevel(rp,&box,ulpen,lrpen);
	else DrawThinBevel(rp,&box,ulpen,lrpen);

	if (flags & BOX_FILLED && box.Width > (hthick<<1) && (box.Height > 2))
	{
		BNDRYOFF( rp );
		SetAfPt(rp,	NULL, 0);
		SetAPen(rp,	fillpen);

		RectFill( rp, box.Left+hthick, box.Top+1,
			box.Left+box.Width-1-hthick, box.Top+box.Height-2 );
	}

		/* if the plane pointer is filled in, then it means that there is
			a Glyph to be drawn. PlanePick and PlaneOnOff are used to
			indicate the size of the glyph
		*/

	if (bi->glyph_data)
	{	int				w = img->PlanePick,
						h = img->PlaneOnOff;

		SetAPen(rp,glyphpen);
		BltTemplate(
			(void *)bi->glyph_data,0,((w+15)>>4)<<1,
			rp,box.Left + (box.Width - w)/2,box.Top + (box.Height - h)/2,
			w,h );
	}

	return NULL;
}

/* ============================================================================ *
 *	initBoxClass: create data used by all bevel boxes
 * ============================================================================ */

Class *initBoxClass(void)
{	Class *cl;

	if (cl = MakeClass(							/* Ask Intuition to make it		*/
		NULL, 									/* Class ID	(none)				*/
		"imageclass", NULL,						/* superclass is public, tho...	*/
		sizeof (struct BoxData),				/* Size of instance data		*/
		0 ))									/* No flags...					*/
	{	cl->cl_Dispatcher.h_Entry =	(void *)ourhook;
		cl->cl_Dispatcher.h_SubEntry = NULL;
		cl->cl_Dispatcher.h_Data = 0L;
	}
	return cl;
}

	/* This undoes initBoxClass */

LONG freeBoxClass(Class *cl)
{	return (LONG)FreeClass(cl);
}
