/*
**	CustomRequest.c
**
**	EasyRequest like custom requester
**
**	Copyright © 1990-1996 by Olaf `Olsen' Barthel
**		All Rights Reserved
**
**	:ts=4
*/

#ifndef _GLOBAL_H
#include "Global.h"
#endif

	/* GetScreenInfo():
	 *
	 *	Obtain the currently visible screen area.
	 */

STATIC VOID
GetScreenInfo(struct Screen *Screen,WORD *Left,WORD *Top,WORD *Width,WORD *Height)
{
	struct ViewPortExtra	*Extra;
	struct TagItem			 Tags[2] = { VTAG_VIEWPORTEXTRA_GET, NULL, TAG_DONE };

	if(!VideoControl(Screen->ViewPort.ColorMap,Tags))
		Extra = (struct ViewPortExtra *)Tags[0].ti_Data;
	else
		Extra = NULL;

	if(Extra)
	{
		struct Rectangle Clip;

		QueryOverscan(GetVPModeID(&Screen->ViewPort),&Clip,OSCAN_TEXT);

		*Width	= Extra->DisplayClip.MaxX - Extra->DisplayClip.MinX + 1;
		*Height	= Extra->DisplayClip.MaxY - Extra->DisplayClip.MinY + 1;

		if(*Width < Clip.MaxX - Clip.MinX + 1)
			*Width = Clip.MaxX - Clip.MinX + 1;

		if(*Height < Clip.MaxY - Clip.MinY + 1)
			*Height = Clip.MaxY - Clip.MinY + 1;
	}
	else
	{
		*Width	= Screen->Width;
		*Height	= Screen->Height;
	}

	*Left	= ABS(Screen->LeftEdge);
	*Top	= ABS(Screen->TopEdge);

	if(*Left > Screen->Width || *Left < 0)
		*Left = 0;

	if(*Top > Screen->Height || *Top < 0)
		*Top = 0;
}

	/* CentreWindow():
	 *
	 *	Centre a window within the visible bounds of
	 *	a screen.
	 */

STATIC VOID
CentreWindow(struct Screen *Screen,WORD Width,WORD Height,WORD *Left,WORD *Top)
{
	WORD ScreenWidth,ScreenHeight,ScreenLeft,ScreenTop;

	GetScreenInfo(Screen,&ScreenLeft,&ScreenTop,&ScreenWidth,&ScreenHeight);

	*Left	= ScreenLeft + (ScreenWidth - Width) / 2;
	*Top	= ScreenTop + (ScreenHeight - Height) / 2;
}

VOID SAVE_DS ASM
CustomStuffText(REG(a3) LONG *Data,REG(d0) UBYTE Char)
{
	struct IntuiText *IText;
	STRPTR Buffer;

	Buffer	= (STRPTR)Data[0];	/* Space allocated for the char buffer */
	IText	= (APTR)Data[1];	/* Space allocated for the IntuiTexts */

	if(Char == Data[2] || Char == '\0')
	{
		LONG Width;

		IText->LeftEdge	= 0;
		IText->TopEdge	= Data[3];	/* Text top edge */
		IText->ITextFont	= NULL;

			/* Line height, derived from font size */

		Data[3] += Data[4];

			/* Chop off the rest */

		*Buffer++ = '\0';

			/* Measure the length in pixels */

		if(Data[6])
		{
			Width = TextLength((APTR)Data[6],IText->IText,(LONG)Buffer - (LONG)IText->IText - 1);

				/* More than we got before? */

			if(Width > Data[5])
				Data[5] = Width;
		}

		if(Char != '\0')
		{
				/* Link to following text */

			IText->NextText = IText + 1;

				/* Get ready for the next line */

			Data[1] = (LONG)(++IText);

				/* Start of next line */

			IText->IText = Buffer;
		}
		else
			IText->NextText = NULL;			/* Terminate the chain */
	}
	else
		*Buffer++ = Char;						/* Just store the character */

	Data[0] = (LONG)Buffer;						/* Remember for next char */
}

VOID ASM
CustomCountChar(REG(a3) LONG *Count,REG(d0) UBYTE Char)
{
	if(Char == Count[2] || Char == '\0')	/* Count the number of lines */
		Count[1]++;

	Count[0]++;								/* Count the number of characters in total */
}

STATIC struct IntuiText *
BuildITextTable(STRPTR FormatString,va_list VarArgs,UBYTE Terminator,LONG *Information,LONG *MoreData)
{
	struct IntuiText *Primitive;

	Information[0] = Information[1] = 0;
	Information[2] = Terminator;

	#ifdef USE_GLUE
		RawDoFmt(FormatString,(APTR)VarArgs,(PUTCHAR)CustomCountCharGlue,(APTR)Information);
	#else
		RawDoFmt(FormatString,(APTR)VarArgs,(PUTCHAR)CustomCountChar,(APTR)Information);
	#endif

	if(Primitive = (struct IntuiText *)AllocVec(Information[1] * sizeof(struct IntuiText) + Information[0],MEMF_ANY))
	{
			/* Room for the char buffer */

		MoreData[0]	= (LONG)(Primitive + Information[1]);

			/* Room for the IntuiTexts */

		MoreData[1] = (LONG)Primitive;

			/* Separator char to use */

		MoreData[2] = Terminator;

			/* Top edge of first text, the following lines get placed below */

		MoreData[3] = 0;

			/* The maximum width of all lines will end up here */

		MoreData[5] = 0;

			/* Now this is important; the first IText buffer must */
			/* be set up here, the formatting code will do the rest */
			/* for all the others */

		Primitive[0].IText = (STRPTR)MoreData[0];
	}

	return(Primitive);
}

	/* ShowInfo():
	 *
	 *	Display an EasyRequest like information requester.
	 */

LONG
ShowInfo(struct Window *Parent,STRPTR Title,STRPTR Continue,STRPTR FormatString,...)
{
	struct Screen *Screen;
	ULONG IntuiLock;
	LONG Result;
	BOOL PopBack;

	Result = 0;

	if(Parent)
		Screen = Parent->WScreen;
	else
		Screen = LockPubScreen(NULL);

	IntuiLock = LockIBase(NULL);

	PopBack = (BOOL)(IntuitionBase->FirstScreen != Screen);

	UnlockIBase(IntuiLock);

	if(Screen)
	{
		struct DrawInfo *DrawInfo;

		if(PopBack)
			ScreenToFront(Screen);

		if(DrawInfo = GetScreenDrawInfo(Screen))
		{
			LONG Information[3],GadgetInformation[3];
			LONG MoreData[7],GadgetData[7];
			struct Image *GadgetFrameImage;
			struct IntuiText *GadgetText;
			struct TextAttr FontTemplate;
			struct Gadget **GadgetTable;
			struct IntuiText *BodyText;
			struct TextFont *Font;
			LONG FullButtonWidth;
			LONG DistX,DistY;
			LONG RoomX,RoomY;
			va_list VarArgs;

				/* We'll need this one later */

			Font = DrawInfo->dri_Font;

				/* Set up the spacing information */

			RoomY = (Font->tf_YSize + 1) / 2;
			RoomX = (RoomY * DrawInfo->dri_Resolution.Y) / DrawInfo->dri_Resolution.X;

				/* Make a copy of the screen font */

			AskFont(&Screen->RastPort,&FontTemplate);

				/* Request that it be boldfaced, the leftmost */
				/* button will receive this type of label */

			FontTemplate.ta_Style |= FSF_BOLD;

			va_start(VarArgs,FormatString);
			BodyText = BuildITextTable(FormatString,VarArgs,'\n',Information,MoreData);
			va_end(VarArgs);

			GadgetText = BuildITextTable(Continue,NULL,'|',GadgetInformation,GadgetData);

			GadgetTable = (struct Gadget **)AllocVec(sizeof(struct Gadget *) * GadgetInformation[1],MEMF_ANY|MEMF_CLEAR);

			GadgetFrameImage = (struct Image *)NewObjectA(NULL,FRAMEICLASS,NULL);

			if(GadgetFrameImage && GadgetTable && GadgetText)
			{
				struct Gadget *Previous;
				LONG ID,Last,i;
				ULONG PrevTag;

				ID			= 0;
				Last		= GadgetInformation[1] - 1;

				Previous	= NULL;
				PrevTag		= TAG_IGNORE;

					/* We'll work with the button sizes in a minute... */

				FullButtonWidth = 0;

					/* We don't want fonts to be measured */

				GadgetData[6] = 0;

				#ifdef USE_GLUE
					RawDoFmt(Continue,NULL,(PUTCHAR)CustomStuffTextGlue,GadgetData);
				#else
					RawDoFmt(Continue,NULL,(PUTCHAR)CustomStuffText,GadgetData);
				#endif	/* USE_GLUE */

				DistY = 2;
				DistX = (DistY * DrawInfo->dri_Resolution.Y) / DrawInfo->dri_Resolution.X;

				GadgetText[0].ITextFont = &FontTemplate;

				for(i = 0 ; i <= Last ; i++)
				{
					if(i == Last)
						ID = 0;
					else
						ID++;

					GadgetText[i].NextText = NULL;

					if(GadgetTable[i] = NewObject(NULL,FRBUTTONCLASS,
						GA_IntuiText,	&GadgetText[i],
						GA_Image,		GadgetFrameImage,
						GA_RelVerify,	TRUE,
						GA_ID,			ID,
						GA_DrawInfo,	DrawInfo,
						PrevTag,		Previous,
					TAG_DONE))
					{
						Previous	= GadgetTable[i];
						PrevTag		= GA_Previous;

						SetAttrs(Previous,
							GA_Width,	2*DistX + Previous->Width,
							GA_Height,	2*DistY + Previous->Height,
							GA_DrawInfo,DrawInfo,
						TAG_DONE);

							/* Add the width of the button, plus */
							/* some room in between */

						FullButtonWidth += Previous->Width + RoomX;
					}
					else
					{
						LONG j;

						for(j = 0 ; j < i ; j++)
							DisposeObject(GadgetTable[i]);

						FreeVec(GadgetTable);
						GadgetTable = NULL;

						break;
					}
				}

					/* Subtract the trailing space, add the border space */

				FullButtonWidth += Screen->WBorRight + RoomX + Screen->WBorLeft;
			}

				/* Did we get what we wanted and will the buttons fit on */
				/* the screen? */

			if(BodyText && GadgetText && GadgetTable && FullButtonWidth <= Screen->Width)
			{
				WORD WindowWidth,WindowHeight,ScreenWidth,ScreenHeight,ScreenLeft,ScreenTop;
				struct Image *FillImage,*FrameImage,*TextImage;
				WORD Width,Height,Left,Top;
				struct Image *DepthImage;
				LONG FullTitleWidth;
				ULONG DepthWidth;

					/* Before we do anything else, set up the */
					/* full width of the window title; this includes */
					/* both the text and the depth arrangement gadget */

				if(Title)
					FullTitleWidth = TextLength(&Screen->RastPort,Title,strlen(Title));
				else
					FullTitleWidth = 0;

					/* Get the width of the window depth gadget for this screen */

				if(DepthImage = NewObject(NULL,SYSICLASS,
					SYSIA_Size,		(Screen->Flags & SCREENHIRES) ? SYSISIZE_MEDRES : SYSISIZE_LOWRES,
					SYSIA_Which,	DEPTHIMAGE,
					SYSIA_DrawInfo,	DrawInfo,
				TAG_DONE))
				{
					GetAttr(IA_Width,DepthImage,&DepthWidth);

					DisposeObject(DepthImage);
				}
				else
					DepthWidth = (Screen->Flags & SCREENHIRES) ? 23 : 17;

				FullTitleWidth += DepthWidth + 2;

				if(FullTitleWidth > Screen->Width)
					FullTitleWidth = Screen->Width;

					/* Increase the size to fit all the buttons into the window */

				if(FullButtonWidth > FullTitleWidth)
					FullTitleWidth = FullButtonWidth;

					/* Height of each line */

				MoreData[4] = Font->tf_YSize;

					/* Pointer to screen rastport used for measuring the text */

				MoreData[6] = (LONG)&Screen->RastPort;

					/* Now do the magic formatting... */

				va_start(VarArgs,FormatString);

				#ifdef USE_GLUE
					RawDoFmt(FormatString,(APTR)VarArgs,(PUTCHAR)CustomStuffTextGlue,(APTR)MoreData);
				#else
					RawDoFmt(FormatString,(APTR)VarArgs,(PUTCHAR)CustomStuffText,(APTR)MoreData);
				#endif	/* USE_GLUE */

				va_end(VarArgs);

					/* The maximum width of all lines is now in MoreData[5], */
					/* the effective height of the block in MoreData[3]; */

				TextImage = NewObject(NULL,ITEXTICLASS,
					IA_FGPen,		DrawInfo->dri_Pens[TEXTPEN],
					IA_Data,		BodyText,
					IA_Width,		MoreData[5] + 2*DistX,	/* Add a little room around the text */
					IA_Height,		MoreData[3] + 2*DistY,
				TAG_DONE);

					/* Build the checkered background fill pattern */

				FillImage = NewObject(NULL,FILLRECTCLASS,
					IA_APattern,	&Crosshatch,
					IA_APatSize,	1,
					IA_Mode,		JAM2,
					IA_FGPen,		DrawInfo->dri_Pens[SHINEPEN],
					IA_BGPen,		DrawInfo->dri_Pens[BACKGROUNDPEN],
				TAG_DONE);

					/* Do the same for the frame around the text */

				FrameImage = NewObject(NULL,FRAMEICLASS,
					IA_Recessed,	TRUE,
				TAG_DONE);

					/* Did we get 'em all? */

				if(FillImage && FrameImage && TextImage)
				{
					LONG WindowLeft,WindowTop;
					struct IBox FramedSize;
					struct Window *Window;
					struct Gadget Dummy;

						/* We'll need these offsets pretty soon */

					WindowLeft	= Screen->WBorLeft;
					WindowTop	= Screen->WBorTop + Screen->Font->ta_YSize + 1;

						/* Now check how large the surrounding frame would be */

					DoMethod((Object *)FrameImage,IM_FRAMEBOX,&TextImage->LeftEdge,&FramedSize,DrawInfo,NULL);

						/* Pad the space around the frame a bit */

					FramedSize.Width	+= 2 * RoomX;
					FramedSize.Height	+= 2 * RoomY;

						/* Save these for the window open stuff */

					Width	= WindowLeft +	FramedSize.Width +	Screen->WBorRight;
					Height	= WindowTop +	FramedSize.Height +	Screen->WBorBottom;

						/* If the window title won't fit on the screen, */
						/* expand the fillimage a bit; do the same to the */
						/* textimage. */

					if(Width < FullTitleWidth)
					{
						LONG Adapt = (FullTitleWidth - Width + 1) & ~1;

						FramedSize.Width	+= Adapt;
						Width				+= Adapt;

						DistX				+= Adapt / 2;

						SetAttrs((Object *)TextImage,
							IA_Width,MoreData[5] + 2*DistX,
						TAG_DONE);
					}

						/* Adjust size and position of the background pattern */

					SetAttrs((Object *)FillImage,
						IA_Left,	WindowLeft,
						IA_Top,		WindowTop,
						IA_Width,	FramedSize.Width,
						IA_Height,	FramedSize.Height,
					TAG_DONE);

						/* Do the whole thing all over again for the framed text; */
						/* this will put the right coords to centre the text within */
						/* the frame into FramedSize */

					DoMethod((Object *)FrameImage,IM_FRAMEBOX,&TextImage->LeftEdge,&FramedSize,DrawInfo,FRAMEF_SPECIFY);

						/* Move the text around */

					SetAttrs((Object *)TextImage,
						IA_Left,	WindowLeft	- FramedSize.Left	+ DistX,
						IA_Top,		WindowTop	- FramedSize.Top	+ DistY,
					TAG_DONE);

						/* Almost finished, now centre the frame around the text */

					DoMethod((Object *)FrameImage,IM_FRAMEBOX,&TextImage->LeftEdge,&FramedSize,DrawInfo,NULL);

					SetAttrs((Object *)FrameImage,
						IA_Left,	FramedSize.Left		- DistX,
						IA_Top,		FramedSize.Top		- DistY,
						IA_Width,	FramedSize.Width,
						IA_Height,	FramedSize.Height,
					TAG_DONE);

						/* Now link the objects together */

					FillImage->NextImage	= FrameImage;
					FrameImage->NextImage	= TextImage;

						/* Zap the dummy gadget and put the image inside */

					memset(&Dummy,0,sizeof(Dummy));

					Dummy.Flags			= GFLG_GADGIMAGE|GFLG_GADGHNONE;
					Dummy.GadgetRender	= FillImage;

						/* Put the gadget in */

					/* ALWAYS */
					{
						LONG LocalLeft	= FrameImage->LeftEdge;
						LONG LocalTop	= FillImage->TopEdge + FillImage->Height;

						GadgetTable[GadgetInformation[1] - 1]->NextGadget = &Dummy;

						SetAttrs((Object *)FillImage,
							IA_Height,	FillImage->Height + GadgetTable[0]->Height + RoomY,
						TAG_DONE);

						Height += GadgetTable[0]->Height + RoomY;

						if(GadgetInformation[1] > 1)
						{
							LONG Room,Count,i;

							Room		 = Width - FullButtonWidth;
							Count		 = GadgetInformation[1] - 1;
							LocalLeft	 = FrameImage->LeftEdge;
							Room		+= Count * RoomX;

							for(i = 0 ; i <= Count ; i++)
							{
								SetAttrs((Object *)GadgetTable[i],
									GA_Top,		LocalTop,
									GA_Left,	LocalLeft,
								TAG_DONE);

								LocalLeft += GadgetTable[i]->Width + ((Room * (i + 1)) / Count - (Room * i) / Count);
							}
						}
						else
						{
							SetAttrs((Object *)GadgetTable[0],
								GA_Top,		LocalTop,
								GA_Left,	LocalLeft + (FillImage->Width - GadgetTable[0]->Width) / 2,
							TAG_DONE);
						}
					}

						/* Check if the window will fit on the screen */

					if(Width <= Screen->Width && Height <= Screen->Height)
					{
							/* Centre the body text strings */

						/* ALWAYS */
						{
							struct IntuiText *This;

							for(This = BodyText ; This ; This = This->NextText)
							{
								This->ITextFont = Screen->Font;
								This->LeftEdge = (MoreData[5] - IntuiTextLength(This)) / 2;
								This->ITextFont = NULL;
							}
						}

							/* Centre the window to open */

						if(Parent)
						{
							WindowLeft		= Parent->LeftEdge + Parent->BorderLeft;
							WindowTop		= Parent->TopEdge + Parent->BorderTop;
							WindowWidth		= Parent->Width - (Parent->BorderLeft + Parent->BorderRight);
							WindowHeight	= Parent->Height - (Parent ->BorderTop + Parent->BorderBottom);

							if((Left = WindowLeft + (WindowWidth - Width) / 2) < 0)
								Left = 0;

							if((Top = WindowTop + (WindowHeight - Height) / 2) < 0)
								Top = 0;

							GetScreenInfo(Screen,&ScreenLeft,&ScreenTop,&ScreenWidth,&ScreenHeight);

							if(Left < ScreenLeft || Left + Width > ScreenLeft + ScreenWidth)
								Left = -1;

							if(Top < ScreenTop || Top + Height > ScreenTop + ScreenHeight)
								Top = -1;

							if(Top == -1 || Left == -1)
								CentreWindow(Screen,Width,Height,&Left,&Top);
						}
						else
							CentreWindow(Screen,Width,Height,&Left,&Top);

							/* Finally, open the window */

						if(Window = OpenWindowTags(NULL,
							WA_Left,			Left,
							WA_Top,				Top,
							WA_Width,			Width,
							WA_Height,			Height,
							WA_Title,			Title,
							WA_Flags,			WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_RMBTRAP | WFLG_NOCAREREFRESH | WFLG_SIMPLE_REFRESH | WFLG_ACTIVATE,
							WA_IDCMP,			IDCMP_GADGETUP | IDCMP_VANILLAKEY | IDCMP_RAWKEY,
							WA_CustomScreen,	Screen,
							WA_Gadgets,			GadgetTable[0],
						TAG_DONE))
						{
							struct IntuiMessage	*Message;
							struct Gadget *MsgGadget;
							struct Gadget *Gadget;
							BOOL Terminated;
							ULONG MsgClass;
							LONG MsgCode;

							Gadget		= NULL;
							Terminated	= FALSE;

							do
							{
								WaitPort(Window->UserPort);

								while(Message = (struct IntuiMessage *)GetMsg(Window->UserPort))
								{
									MsgClass	= Message->Class;
									MsgCode		= Message->Code;
									MsgGadget	= Message->IAddress;

									ReplyMsg((struct Message *)Message);

									switch(MsgClass)
									{
										case IDCMP_GADGETUP:

											Terminated	= TRUE;
											Result		= MsgGadget->GadgetID;

											break;

										case IDCMP_VANILLAKEY:

											MsgCode = ToUpper(MsgCode);

											switch(MsgCode)
											{
												case '\033':
												case 'B':
												case CONTROL_('C'):

													Terminated	= TRUE;
													Gadget		= GadgetTable[GadgetInformation[1] - 1];

													break;

												case '\r':
												case 'V':

													Terminated	= TRUE;
													Gadget		= GadgetTable[0];

													break;
											}

											break;

										case IDCMP_RAWKEY:

											switch(MsgCode)
											{
												case HELP_CODE:

													GuideDisplay(CONTEXT_MAIN);
													break;

												default:

													if(MsgCode >= F01_CODE && MsgCode <= F10_CODE)
													{
														MsgCode -= F01_CODE;

														if(MsgCode < GadgetInformation[1])
														{
															Terminated = TRUE;

															Gadget = GadgetTable[MsgCode];
														}
													}

													break;
											}

											break;
									}
								}
							}
							while(!Terminated);

							if(Gadget)
							{
								STATIC ULONG SelectedTags[]		= { GA_Selected,TRUE,	TAG_DONE };
								STATIC ULONG DeselectedTags[]	= { GA_Selected,FALSE,	TAG_DONE };

								SetGadgetAttrsA(Gadget,Window,NULL,(struct TagItem *)SelectedTags);
								RefreshGList(Gadget,Window,NULL,1);

								DelayTime(0,80000);

								SetGadgetAttrsA(Gadget,Window,NULL,(struct TagItem *)DeselectedTags);
								RefreshGList(Gadget,Window,NULL,1);
							}

							CloseWindow(Window);
						}
					}
				}

					/* Get rid of the images */

				DisposeObject(FillImage);
				DisposeObject(FrameImage);
				DisposeObject(TextImage);
			}

				/* And the buffers so much work went into... */

			FreeVec(BodyText);
			FreeVec(GadgetText);

			if(GadgetTable)
			{
				LONG i;

				for(i = 0 ; i < GadgetInformation[1] ; i++)
					DisposeObject(GadgetTable[i]);

				FreeVec(GadgetTable);
			}

			DisposeObject(GadgetFrameImage);

			FreeScreenDrawInfo(Screen,DrawInfo);
		}

		if(PopBack)
			ScreenToBack(Screen);

		if(!Parent)
			UnlockPubScreen(NULL,Screen);
	}

	return(Result);
}
