
#include <classes/BOOPSI/popupmenuclass.h>

#include <exec/memory.h>
#include <string.h>

#pragma -
#include <pragma/utility_lib.h>
#include <clib/alib_protos.h>
#include <pragma/intuition_lib.h>
#include <pragma/graphics_lib.h>
#include <pragma/exec_lib.h>
#include <pragma/layers_lib.h>
#pragma +

static struct BitMap *AllocBitMap20(ULONG width, ULONG height, ULONG depth,
			ULONG flags, struct BitMap *like);
static VOID FreeBitMap20(struct BitMap *bitmap);
static struct RastPort *CopyRastPort(struct RastPort *rastport);
static VOID FreeRastPort(struct RastPort *rp);

struct InstanceData {
	UWORD active;
	STRPTR *labels;
	struct TextAttr *tattr;
	UWORD maxheight;
// private:
	struct TextFont *font;
	UWORD labelCount;
	UWORD left;	  // die Grösse des Menüs
	UWORD top;    // die Gadgetgröße wird nicht überschrieben, damit
	UWORD width;  // die dort angegeben default Werte bestehen bleiben
	UWORD height;
	struct BitMap *save; // Sicherung des Bildschirmausschnitts
	struct RastPort *rastport; // Kopie des Screenrastports zum Zeichnen
	UWORD toplabel; // das oberste sichtbare Label im Menü
	UWORD labelanz; // die Anzahl der sichtbaren Label
	BOOL highlighted; // TRUE : ein Label ist invertiert
	BOOL arrowup; // TRUE : Hochpfeil gezeichnet
	BOOL arrowdown; // TRUE : Runterpfeil gezeichnet
	UWORD lineHeight; // Höhe einer Menüzeile
	BOOL manualActivated; // ohne Benutzeraktion aktiviert
};

static struct TextAttr topaz8 = { "topaz.font",8,0,0 };

PopupMenuClass::PopupMenuClass()
	: BoopsiClass(sizeof(struct InstanceData),(ULONG (*)()) &dispatcher,GADGETCLASS,NULL)
{
}

ULONG PopupMenuClass::dispatcher(Class *cl, Object *o, Msg msg)
{
	struct InstanceData *data;
	ULONG retval = FALSE;
	switch (msg->MethodID)
	{
		case OM_NEW:
		{
			Object *object;
			if (object = (Object *) DoSuperMethodA(cl,o,msg))
			{
				data = (struct InstanceData *) INST_DATA(cl,object);
				data->active = 0;
				data->labels = NULL;
				data->tattr = NULL;
				TagItemCursorC tic(((struct opSet *) msg)->ops_AttrList);
				while (!tic.isDone()) {
					/* register */ ULONG ticData = tic.itemData();
					switch (tic.itemTag())
					{
						case MA_Active:
							data->active = (UWORD) ticData;
							break;
						case MA_Labels:
							data->labels = (STRPTR *) ticData;
							break;
						case MA_Font:
							data->tattr = (struct TextAttr *) ticData;
							break;
						case MA_MaxHeight:
							data->maxheight = (UWORD) ticData;
							break;
						default:
							break;
					};
					tic.next();
				};
				if (data->tattr)
					data->font = OpenFont(data->tattr)
				else
					data->font = OpenFont(&topaz8);
				data->labelCount = countLabels(data->labels);
				cutActive(data);
				retval = (ULONG) object;
			};
			break;
		};
		case OM_DISPOSE:
		{
			data = (struct InstanceData *) INST_DATA(cl,o);
			if (data->font)
				CloseFont(data->font);
			retval = DoSuperMethodA(cl,o,msg);
			break;
		};
		case OM_GET:
		{
			data = (struct InstanceData *) INST_DATA(cl,o);
			switch (((struct opGet *) msg)->opg_AttrID)
			{
				case GA_Left:
					*(((struct opGet *) msg)->opg_Storage) = ((struct Gadget *) o)->LeftEdge;
					retval = TRUE;
					break;
				case GA_Top:
					*(((struct opGet *) msg)->opg_Storage) = ((struct Gadget *) o)->TopEdge;
					retval = TRUE;
					break;
				case GA_Width:
					*(((struct opGet *) msg)->opg_Storage) = ((struct Gadget *) o)->Width;
					retval = TRUE;
					break;
				case GA_Height:
					*(((struct opGet *) msg)->opg_Storage) = ((struct Gadget *) o)->Height;
					retval = TRUE;
					break;
				case MA_Active:
					*(((struct opGet *) msg)->opg_Storage) = data->active;
					break;
				default:
					retval = DoSuperMethodA(cl,o,msg);
					break;
			};
			break;
		};
		case OM_SET:
		{
			data = (struct InstanceData *) INST_DATA(cl,o);
			TagItemCursorC tic(((struct opSet *) msg)->ops_AttrList);
			while (!tic.isDone()) {
				/* register */ ULONG ticData = tic.itemData();
				switch (tic.itemTag())
				{
					case MA_Active:
						data->active = (UWORD) ticData;
						break;
					case MA_Labels:
						data->labels = (STRPTR *) ticData;
						data->labelCount = countLabels(data->labels);
						break;
					case MA_MaxHeight:
						data->maxheight = (UWORD) ticData;
						break;
					case MA_Font:
						if (data->font)
						{
							CloseFont(data->font);
							data->font = NULL;
						};
						data->tattr = (struct TextAttr *) ticData;
						if (data->tattr)
							data->font = OpenFont(data->tattr);
						break;
					default:
						break;
				};
				tic.next();
			};
			cutActive(data);
			retval = DoSuperMethodA(cl,o,msg);
			break;
		};
		case GM_RENDER:
		{
			data = (struct InstanceData *) INST_DATA(cl,o);
			retval = renderPopupMenu(cl,(struct Gadget *) o,
				(struct gpRender *) msg,data);
			break;
		};
		case GM_HITTEST:
		{
			retval = GMR_GADGETHIT;
			break;
		};
		case GM_GOACTIVE:
		{
			data = (struct InstanceData *) INST_DATA(cl,o);
			if (((struct gpInput *) msg)->gpi_IEvent)
			{
				((struct Gadget *) msg)->Flags |= GFLG_SELECTED;
				renderPopupMenu(cl,(struct Gadget *) o,
					(struct gpRender *) msg,data);
				struct GadgetInfo *ginfo = ((struct gpInput *) msg)->gpi_GInfo;
				LockLayers(&ginfo->gi_Screen->LayerInfo);
				LockLayerInfo(&ginfo->gi_Screen->LayerInfo);
				InitPopupMenu(data, (struct Gadget *) o, ginfo);
				data->manualActivated = FALSE;
				retval = GMR_MEACTIVE;
			}
			else {
				((struct Gadget *) msg)->Flags |= GFLG_SELECTED;
				struct GadgetInfo *ginfo = ((struct gpInput *) msg)->gpi_GInfo;
				LockLayers(&ginfo->gi_Screen->LayerInfo);
				LockLayerInfo(&ginfo->gi_Screen->LayerInfo);
				InitPopupMenu(data, (struct Gadget *) o, ginfo);
				data->manualActivated = TRUE;
				retval = GMR_MEACTIVE;
			};
			break;
		};
		case GM_GOINACTIVE:
		{
			data = (struct InstanceData *) INST_DATA(cl,o);
			FinishPopupMenu(data,((struct gpGoInactive *) msg)->gpgi_GInfo);
			UnlockLayerInfo(&((struct gpGoInactive *) msg)->
				gpgi_GInfo->gi_Screen->LayerInfo);
			UnlockLayers(&((struct gpGoInactive *) msg)->
				gpgi_GInfo->gi_Screen->LayerInfo);
			((struct Gadget *) o)->Flags &= ~GFLG_SELECTED;
			if (!data->manualActivated)
			{
				renderPopupMenu(cl,(struct Gadget *) o,
					(struct gpRender *) msg,data);
			};
			break;
		};
		case GM_HANDLEINPUT:
		{
			data = (struct InstanceData *) INST_DATA(cl,o);
			struct gpInput *gpi = (struct gpInput *) msg;
			struct InputEvent *ie = gpi->gpi_IEvent;
			WORD mouseX = gpi->gpi_Mouse.X + ((struct Gadget *) o)->LeftEdge;
			if (gpi->gpi_GInfo->gi_Window)
				mouseX += gpi->gpi_GInfo->gi_Window->LeftEdge;
			WORD mouseY = gpi->gpi_Mouse.Y + ((struct Gadget *) o)->TopEdge;
			if (gpi->gpi_GInfo->gi_Window)
				mouseY += gpi->gpi_GInfo->gi_Window->TopEdge;
			BOOL mouseOverGadget = (mouseX >= data->left) &&
				(mouseX < data->left + data->width) &&
				(mouseY >= data->top) && (mouseY < data->top + data->height);
			if (mouseOverGadget)
			{
				if (mouseY > data->top+2 && mouseY < data->top + data->height - 2)
				{
					UWORD viewline = (mouseY-(data->top+2)) / (data->font->tf_YSize + 2);
					if	(viewline < data->labelanz)
					{
						UWORD active = data->toplabel + viewline;
						if (active != data->active)
						{
							if (data->highlighted)
								invertActive(data,gpi->gpi_GInfo);
							data->active = active;
							invertActive(data,gpi->gpi_GInfo);
						};
					};
					if (!data->highlighted)
						invertActive(data,gpi->gpi_GInfo);
				};
			}
			else {
				if (data->highlighted)
					invertActive(data,gpi->gpi_GInfo);
			};
			switch (ie->ie_Class)
			{
				case IECLASS_RAWMOUSE:
				{
					switch (ie->ie_Code)
					{
						case SELECTUP:
							if (mouseOverGadget)
							{
								retval = GMR_NOREUSE | GMR_VERIFY;
							}
							else {
								retval = GMR_NOREUSE;
							};
							break;
						case MENUDOWN:
							retval = GMR_REUSE;
							break;
						default:
#if 0
							if (mouseY < data->top + 2 && data->arrowup)
							{
								if (data->highlighted)
									invertActive(data,gpi->gpi_GInfo);
								if (data->toplabel > 0)
									data->toplabel--;
								scrollDown(data,gpi->gpi_GInfo);
								renderArrowUp(data,gpi->gpi_GInfo);
								renderArrowDown(data,gpi->gpi_GInfo);
							}
							else if (mouseY > data->top + data->height - 2 && data->arrowdown)
							{
								if (data->highlighted)
									invertActive(data,gpi->gpi_GInfo);
								if (data->toplabel + data->labelanz < data->labelCount)
									data->toplabel++;
								scrollUp(data,gpi->gpi_GInfo);
								renderArrowUp(data,gpi->gpi_GInfo);
								renderArrowDown(data,gpi->gpi_GInfo);
							};
#endif
							retval = GMR_MEACTIVE;
							break;
					};
					break;
				};
				case IECLASS_TIMER:
				{
					if (mouseY < data->top + 2 && data->arrowup)
					{
						if (data->highlighted)
							invertActive(data,gpi->gpi_GInfo);
						if (data->toplabel > 0)
							data->toplabel--;
						scrollDown(data,gpi->gpi_GInfo);
						renderArrowUp(data,gpi->gpi_GInfo);
						renderArrowDown(data,gpi->gpi_GInfo);
					}
					else if (mouseY > data->top + data->height - 2 && data->arrowdown)
					{
						if (data->highlighted)
							invertActive(data,gpi->gpi_GInfo);
						if (data->toplabel + data->labelanz < data->labelCount)
							data->toplabel++;
						scrollUp(data,gpi->gpi_GInfo);
						renderArrowUp(data,gpi->gpi_GInfo);
						renderArrowDown(data,gpi->gpi_GInfo);
					};
					break;
				};
				default:
					retval = GMR_MEACTIVE;
					break;
			};
			break;
		};
		default:
		{
			retval = DoSuperMethodA(cl,o,msg);
			break;
		};
	};
	return retval;
}

ULONG PopupMenuClass::renderPopupMenu(Class *cl, struct Gadget *g,
	struct gpRender *msg, struct InstanceData *data)
{
	struct RastPort *rp;
	struct GadgetInfo *ginfo;
	if (msg->MethodID == OM_SET)
		ginfo = ((struct opSet *) msg)->ops_GInfo
	else
		ginfo = msg->gpr_GInfo;
	UWORD *pens = ginfo->gi_DrInfo->dri_Pens;
	if (msg->MethodID == GM_RENDER)
		rp = msg->gpr_RPort
	else
		rp = ObtainGIRPort(ginfo);
	if (rp)
	{
		SetAPen(rp,pens[BACKGROUNDPEN]);
		SetDrMd(rp,JAM1);
		RectFill(rp,g->LeftEdge,g->TopEdge,g->LeftEdge+g->Width-1,
			g->TopEdge+g->Height-1);
		SetAPen(rp,pens[SHINEPEN]);
		Move(rp,g->LeftEdge,g->TopEdge+g->Height-1);
		Draw(rp,g->LeftEdge,g->TopEdge);
		Draw(rp,g->LeftEdge+g->Width-1,g->TopEdge);
		SetAPen(rp,pens[SHADOWPEN]);
		Draw(rp,g->LeftEdge+g->Width-1,g->TopEdge+g->Height-1);
		Draw(rp,g->LeftEdge,g->TopEdge+g->Height-1);
		UWORD textwidth = g->Width - 4;
		if (data->labels)
		{
			STRPTR label = data->labels[data->active];
			if (label)
			{
				TextExtent extent;
				SetFont(rp,data->font);
				ULONG c = TextFit(rp,label,strlen(label),&extent,NULL,1,
					textwidth,g->Height);
				if (c > 0)
				{
					SetAPen(rp,pens[TEXTPEN]);
					Move(rp,g->LeftEdge + 2 + extent.te_Extent.MinX +
						((textwidth - extent.te_Extent.MinX - extent.te_Width) >> 1),
						g->TopEdge + ((g->Height - extent.te_Height) >> 1) -
							extent.te_Extent.MinY);
					Text(rp,label,c);
				};
			};
		};

		if (msg->MethodID != GM_RENDER)
			ReleaseGIRPort(rp);
		return TRUE;
	};
	return FALSE;
}

UWORD PopupMenuClass::countLabels(STRPTR *labels)
{
	if (labels)
	{
		UWORD i = 0;
		while (labels[i])
			i++;
		return i;
	};
	return 0;
}

VOID PopupMenuClass::cutActive(struct InstanceData *data)
{
	if (data->active > data->labelCount)
	{
		data->active = 0;
	}
}

BOOL PopupMenuClass::InitPopupMenu(struct InstanceData *data, struct Gadget *g,
			struct GadgetInfo *ginfo)
{
	UWORD lineHeight = data->font->tf_YSize + 2;
	data->lineHeight = lineHeight;

	// Größe des Menüs bestimmen
	ULONG height = data->labelCount * lineHeight + 4;
	if (height > ginfo->gi_Screen->Height)
		height = ginfo->gi_Screen->Height;
	if (data->maxheight >= 2*lineHeight + 4 && height > data->maxheight)
		height = data->maxheight;
	UWORD width = g->Width;
	if (width > ginfo->gi_Screen->Width)
		width = ginfo->gi_Screen->Width;

	// Anzahl der Labels im Menü
	data->labelanz = (height - 4) / lineHeight;
	if (data->labelanz > data->labelCount)
		data->labelanz = data->labelCount;

	// oberstes Label bestimmen
	WORD toplabel = data->active - (data->labelanz >> 1);
	if (toplabel < 0)
		toplabel = 0;
	if (toplabel + data->labelanz > data->labelCount)
		toplabel = data->labelCount - data->labelanz;
	data->toplabel = toplabel;

	// Position des Meüs bestimmen
	WORD left = g->LeftEdge;
	if (ginfo->gi_Window)
		left += ginfo->gi_Window->LeftEdge;
	if (left < 0)
		left = 0
	else if (left+width > ginfo->gi_Screen->Width)
		left = ginfo->gi_Screen->Width - width;

	UWORD gadgettop = g->TopEdge;
	if (ginfo->gi_Window)
		gadgettop += ginfo->gi_Window->TopEdge;
	WORD top = gadgettop - (data->active - data->toplabel) * lineHeight;
	if (top < 0)
		top = 0
	else if (top+height > ginfo->gi_Screen->Height)
		top = ginfo->gi_Screen->Height - height;
	data->left = left;
	data->top = top;
	data->width = width;
	data->height = height;

	// Menü darstellen
	if (!(data->save = AllocBitMap20(width,height,
		ginfo->gi_Screen->BitMap.Depth,0,&ginfo->gi_Screen->BitMap)))
	{
		return FALSE;
	};
	data->rastport = CopyRastPort(&ginfo->gi_Screen->RastPort);
	BltBitMap(&ginfo->gi_Screen->BitMap,left,top,data->save,0,0,
		width,height,0xc0,0xff,NULL);
	data->highlighted = FALSE;
	data->arrowup = FALSE;
	data->arrowdown = FALSE;
	renderPopupMenuFrame(data->rastport,left,top,width,height,
		ginfo->gi_DrInfo->dri_Pens);
	renderLabels(data,ginfo);
	invertActive(data,ginfo);
	renderArrowUp(data,ginfo);
	renderArrowDown(data,ginfo);
	return TRUE;
}

VOID PopupMenuClass::FinishPopupMenu(struct InstanceData *data,
	struct GadgetInfo *ginfo)
{
	if (data->save)
	{
		BltBitMap(data->save,0,0,&ginfo->gi_Screen->BitMap,data->left,data->top,
			data->width,data->height,0xc0,0xff,NULL);
		WaitBlit();
		FreeBitMap20(data->save);
	};
	FreeRastPort(data->rastport);
}

VOID PopupMenuClass::renderLabels(struct InstanceData *data,
	struct GadgetInfo *ginfo)
{
	if (data->labels)
	{
		UWORD textwidth = data->width - 16;
		UWORD top = data->top + 2;
		UWORD left = data->left + 2;
		int i;
		for (i = data->toplabel; i < data->toplabel + data->labelanz; i++) {
			STRPTR label = data->labels[i];
			if (label)
			{
				TextExtent extent;
				SetFont(data->rastport,data->font);
				ULONG c = TextFit(data->rastport,label,strlen(label),
					&extent,NULL,1,textwidth,data->lineHeight);
				if (c > 0)
				{
					SetAPen(data->rastport,ginfo->gi_DrInfo->dri_Pens[TEXTPEN]);
					SetBPen(data->rastport,ginfo->gi_DrInfo->dri_Pens[BACKGROUNDPEN]);
					SetDrMd(data->rastport,JAM2);
					Move(data->rastport,left+extent.te_Extent.MinX +
						((textwidth - extent.te_Extent.MinX - extent.te_Width) >> 1),
						top - extent.te_Extent.MinY);
					Text(data->rastport,label,c);
				};
			}
			else {
				return;
			};
			top += data->lineHeight;
		};
	};
}

VOID PopupMenuClass::invertActive(struct InstanceData *data,
	struct GadgetInfo *ginfo)
{
	if (data->active >= data->toplabel &&
		data->active < data->toplabel+data->labelanz)
	{
		if (data->labels)
		{
			UWORD textwidth = data->width - 16;
			UWORD top = data->top + 2 +
				(data->active - data->toplabel)*data->lineHeight;
			UWORD left = data->left + 2;
			STRPTR label = data->labels[data->active];
			if (label)
			{
				TextExtent extent;
				SetFont(data->rastport,data->font);
				ULONG c = TextFit(data->rastport,label,strlen(label),
					&extent,NULL,1,textwidth,data->lineHeight);
				if (c > 0)
				{
					UWORD textleft = left + extent.te_Extent.MinX +
						((textwidth - extent.te_Extent.MinX - extent.te_Width) >> 1);
#if 0
					SetAPen(data->rastport,ginfo->gi_DrInfo->dri_Pens[TEXTPEN]);
					SetBPen(data->rastport,ginfo->gi_DrInfo->dri_Pens[BACKGROUNDPEN]);
					if (!data->highlighted)
					{
						SetDrMd(data->rastport,JAM2);
						RectFill(data->rastport,left,top,
							textleft - 1,top + data->lineHeight - 1);
						RectFill(data->rastport,textleft + extent.te_Width,top,
							left + textwidth - 1,top + data->lineHeight - 1);
						SetDrMd(data->rastport,JAM2|INVERSVID);
						Move(data->rastport,textleft,top - extent.te_Extent.MinY);
						Text(data->rastport,label,c);
					}
					else {
						SetDrMd(data->rastport,JAM2|INVERSVID);
						RectFill(data->rastport,left,top,
							textleft - 1,top + data->lineHeight - 1);
						RectFill(data->rastport,textleft + extent.te_Width,top,
							left + textwidth - 1,top + data->lineHeight - 1);
						SetDrMd(data->rastport,JAM2);
						Move(data->rastport,textleft,top - extent.te_Extent.MinY);
						Text(data->rastport,label,c);
					};
#else
					if (!data->highlighted)
					{
						SetAPen(data->rastport,ginfo->gi_DrInfo->dri_Pens[TEXTPEN]);
						SetDrMd(data->rastport,JAM1);
						RectFill(data->rastport,left,top,
							left + textwidth - 1,top + data->lineHeight - 1);
						SetAPen(data->rastport,ginfo->gi_DrInfo->dri_Pens[BACKGROUNDPEN]);
						Move(data->rastport,textleft,top - extent.te_Extent.MinY);
						Text(data->rastport,label,c);
					}
					else {
						SetAPen(data->rastport,ginfo->gi_DrInfo->dri_Pens[BACKGROUNDPEN]);
						SetDrMd(data->rastport,JAM1);
						RectFill(data->rastport,left,top,
							left + textwidth - 1,top + data->lineHeight - 1);
						SetAPen(data->rastport,ginfo->gi_DrInfo->dri_Pens[TEXTPEN]);
						Move(data->rastport,textleft,top - extent.te_Extent.MinY);
						Text(data->rastport,label,c);
					};
#endif
				}
				else {
					SetAPen(data->rastport,ginfo->gi_DrInfo->dri_Pens[TEXTPEN]);
					SetBPen(data->rastport,ginfo->gi_DrInfo->dri_Pens[BACKGROUNDPEN]);
					if (!data->highlighted)
						SetDrMd(data->rastport,JAM2)
					else
						SetDrMd(data->rastport,JAM2|INVERSVID);
					RectFill(data->rastport,left,top,
						left + textwidth - 1,top + data->lineHeight - 1);
				};
			};
		};
		data->highlighted = !data->highlighted;
	};
}

VOID PopupMenuClass::renderArrowUp(struct InstanceData *data,
	struct GadgetInfo *ginfo)
{
	if (data->toplabel > 0)
	{
		if (!data->arrowup)
		{
			UWORD left = data->left + data->width - 12;
			UWORD top = data->top + 2;
			SetAPen(data->rastport,ginfo->gi_DrInfo->dri_Pens[SHADOWPEN]);
			SetDrMd(data->rastport,JAM1);
			WritePixel(data->rastport,left+4,top);
			Move(data->rastport,left+3,top+1);
			Draw(data->rastport,left+5,top+1);
			Move(data->rastport,left+2,top+2);
			Draw(data->rastport,left+6,top+2);
			Move(data->rastport,left+1,top+3);
			Draw(data->rastport,left+7,top+3);
			Move(data->rastport,left+0,top+4);
			Draw(data->rastport,left+8,top+4);
			data->arrowup = TRUE;
		};
	}
	else {
		if (data->arrowup)
		{
			UWORD left = data->left + data->width - 12;
			UWORD top = data->top + 2;
			SetAPen(data->rastport,ginfo->gi_DrInfo->dri_Pens[BACKGROUNDPEN]);
			SetDrMd(data->rastport,JAM1);
			RectFill(data->rastport,left,top,left+9,top+5);
			data->arrowup = FALSE;
		};
	};
}

VOID PopupMenuClass::renderArrowDown(struct InstanceData *data,
	struct GadgetInfo *ginfo)
{
	if (data->toplabel + data->labelanz < data->labelCount)
	{
		if (!data->arrowdown)
		{
			UWORD left = data->left + data->width - 12;
			UWORD top = data->top + data->height - 7;
			SetAPen(data->rastport,ginfo->gi_DrInfo->dri_Pens[SHADOWPEN]);
			SetDrMd(data->rastport,JAM1);
			WritePixel(data->rastport,left+4,top+4);
			Move(data->rastport,left+3,top+3);
			Draw(data->rastport,left+5,top+3);
			Move(data->rastport,left+2,top+2);
			Draw(data->rastport,left+6,top+2);
			Move(data->rastport,left+1,top+1);
			Draw(data->rastport,left+7,top+1);
			Move(data->rastport,left+0,top);
			Draw(data->rastport,left+8,top);
			data->arrowdown = TRUE;
		};
	}
	else {
		if (data->arrowdown)
		{
			UWORD left = data->left + data->width - 12;
			UWORD top = data->top + data->height - 7;
			SetAPen(data->rastport,ginfo->gi_DrInfo->dri_Pens[BACKGROUNDPEN]);
			SetDrMd(data->rastport,JAM1);
			RectFill(data->rastport,left,top,left+9,top+5);
			data->arrowdown = FALSE;
		};
	};
}

VOID PopupMenuClass::scrollUp(struct InstanceData *data,
		struct GadgetInfo *ginfo)
{
	SetAPen(data->rastport,ginfo->gi_DrInfo->dri_Pens[BACKGROUNDPEN]);
	SetDrMd(data->rastport,JAM1);
	ScrollRaster(data->rastport,0,data->lineHeight,data->left + 2,data->top + 2,
		data->left + data->width - 13,
		data->top + 1 + data->labelanz*data->lineHeight);
	if (data->labels)
	{
		UWORD textwidth = data->width - 16;
		UWORD top = data->top + 2 + (data->labelanz-1)*data->lineHeight;
		UWORD left = data->left + 2;
		STRPTR label = data->labels[data->toplabel+data->labelanz-1];
		if (label)
		{
			TextExtent extent;
			SetFont(data->rastport,data->font);
			ULONG c = TextFit(data->rastport,label,strlen(label),
				&extent,NULL,1,textwidth,data->lineHeight);
			if (c > 0)
			{
				SetAPen(data->rastport,ginfo->gi_DrInfo->dri_Pens[TEXTPEN]);
				SetBPen(data->rastport,ginfo->gi_DrInfo->dri_Pens[BACKGROUNDPEN]);
				SetDrMd(data->rastport,JAM2);
				Move(data->rastport,left+extent.te_Extent.MinX +
					((textwidth - extent.te_Extent.MinX - extent.te_Width) >> 1),
					top - extent.te_Extent.MinY);
				Text(data->rastport,label,c);
			};
		};
	};
}

VOID PopupMenuClass::scrollDown(struct InstanceData *data,
		struct GadgetInfo *ginfo)
{
	SetAPen(data->rastport,ginfo->gi_DrInfo->dri_Pens[BACKGROUNDPEN]);
	SetDrMd(data->rastport,JAM1);
	ScrollRaster(data->rastport,0,-data->lineHeight,data->left + 2,data->top + 2,
		data->left + data->width - 13,
		data->top + 1 + data->labelanz*data->lineHeight);
	if (data->labels)
	{
		UWORD textwidth = data->width - 16;
		UWORD top = data->top + 2;
		UWORD left = data->left + 2;
		STRPTR label = data->labels[data->toplabel];
		if (label)
		{
			TextExtent extent;
			SetFont(data->rastport,data->font);
			ULONG c = TextFit(data->rastport,label,strlen(label),
				&extent,NULL,1,textwidth,data->lineHeight);
			if (c > 0)
			{
				SetAPen(data->rastport,ginfo->gi_DrInfo->dri_Pens[TEXTPEN]);
				SetBPen(data->rastport,ginfo->gi_DrInfo->dri_Pens[BACKGROUNDPEN]);
				SetDrMd(data->rastport,JAM2);
				Move(data->rastport,left+extent.te_Extent.MinX +
					((textwidth - extent.te_Extent.MinX - extent.te_Width) >> 1),
					top - extent.te_Extent.MinY);
				Text(data->rastport,label,c);
			};
		};
	};
}

VOID PopupMenuClass::renderPopupMenuFrame(struct RastPort *rp, UWORD left, UWORD top,
			UWORD width, UWORD height, UWORD *pens)
{
	if (rp)
	{
		SetAPen(rp,pens[BACKGROUNDPEN]);
		SetDrMd(rp,JAM1);
		RectFill(rp,left,top,left+width-1,top+height-1);
		SetAPen(rp,pens[SHINEPEN]);
		Move(rp,left,top+height-1);
		Draw(rp,left,top);
		Draw(rp,left+width-1,top);
		SetAPen(rp,pens[SHADOWPEN]);
		Draw(rp,left+width-1,top+height-1);
		Draw(rp,left,top+height-1);
	};
}

// *************************************************************

PopupMenuClass BPopupMenuC::pmc;

// *************************************************************

struct BitMap *AllocBitMap20(ULONG width, ULONG height, ULONG depth,
						ULONG flags, struct BitMap *like)
{
	struct BitMap *bm = (struct BitMap *) AllocMem(sizeof(struct BitMap),MEMF_CLEAR);
	if (bm)
	{
		InitBitMap(bm,depth,width,height);
		UWORD i;
		BOOL failed = FALSE;
		for (i = 0; i < depth; i++) {
			if (!(bm->Planes[i] = AllocRaster(width,height)))
				failed = TRUE;
		};
		if (failed)
		{
			FreeBitMap20(bm);
			bm = NULL;
		};
	};
	return bm;
}

VOID FreeBitMap20(struct BitMap *bm)
{
	if (bm)
	{
		UWORD depth = bm->Depth;
		UWORD height = bm->Rows;
		UWORD width = bm->BytesPerRow << 3;
		int i;
		for (i = 0; i < depth; i++) {
			if (bm->Planes[i])
				FreeRaster(bm->Planes[i],width,height);
		};
		FreeMem(bm,sizeof(struct BitMap));
	};
}

struct RastPort *CopyRastPort(struct RastPort *rastport)
{
	struct RastPort *rp;
	if (rp = (struct RastPort *) AllocMem(sizeof(struct RastPort),0))
	{
		memcpy(rp,rastport,sizeof(struct RastPort));
	};
	return rp;
}

VOID FreeRastPort(struct RastPort *rp)
{
	if (rp)
	{
		FreeMem(rp,sizeof(struct RastPort));
	};
}

