/*
**	termFastMacroWindow.c
**
**	Fast! macro window support routines
**
**	Copyright © 1990-1993 by Olaf `Olsen' Barthel & MXM
**		All Rights Reserved
*/

#include "termGlobal.h"

	/* Window border scroller. */

enum	{	GAD_SCROLLER = 10000,GAD_UP,GAD_DOWN };

	/* The arrow image height. */

#define ARROW_HEIGHT	11

	/* Pack two words into a long. */

#define PACK_ULONG(a,b)	(((ULONG)(a)) << 16 | b)

	/* Gadget extension. */

struct ButtonInfo
{
	struct Image	*ButtonImage;
	STRPTR		 ButtonCommand;
	LONG		 NumChars;
};

	/* A private button gadget attribute. */

#define BGA_Command	(TAG_USER + 1)

	/* Private button gadget class. */

STATIC struct IClass		*ButtonClass;

	/* Window border scroller. */

STATIC struct Gadget		*Scroller,
				*UpArrow,
				*DownArrow;

	/* Width of the window border scroller. */

STATIC UWORD			 RightBorderWidth;

	/* Scroller arrow imagery. */

STATIC struct Image		*UpImage,
				*DownImage;

	/* The window zoom box. */

STATIC struct IBox		 WindowZoomBox = { -1,-1,-1,-1 };

	/* The fast macro buttons. */

STATIC struct Gadget		*Buttons;

	/* The first visible button and the number buttons
	 * visible in total.
	 */

STATIC LONG			 ButtonTop,
				 ButtonCount;

	/* RenderText():
	 *
	 *	Render the button text.
	 */

STATIC VOID __regargs
RenderText(struct RastPort *RPort,struct GadgetInfo *GadgetInfo,struct Gadget *Gadget,LONG Len)
{
	UWORD	 Width	= (GadgetInfo -> gi_Window -> Width - (GadgetInfo -> gi_Window -> BorderLeft + GadgetInfo -> gi_Window -> BorderRight + 4));
	STRPTR	 Label	= (STRPTR)Gadget -> GadgetText;
	ULONG	 State;

		/* Which state is the button in? */

	if(Gadget -> Flags & GFLG_SELECTED)
		State = IDS_SELECTED;
	else
		State = IDS_NORMAL;

		/* Render the frame. */

	DoMethod(Gadget -> GadgetRender,IM_DRAWFRAME,RPort,PACK_ULONG(Gadget -> LeftEdge + 2,Gadget -> TopEdge),State,GadgetInfo -> gi_DrInfo,PACK_ULONG(Width,Gadget -> Height));

		/* Set the rendering pens. */

	SetDrMd(RPort,JAM1);

	if(State == IDS_SELECTED)
		SetAPen(RPort,GadgetInfo -> gi_DrInfo -> dri_Pens[FILLTEXTPEN]);
	else
		SetAPen(RPort,GadgetInfo -> gi_DrInfo -> dri_Pens[TEXTPEN]);

		/* Render the button label. */

	Move(RPort,Gadget -> LeftEdge + 4,Gadget -> TopEdge + (Gadget -> Height - GadgetInfo -> gi_DrInfo -> dri_Font -> tf_YSize) / 2 + GadgetInfo -> gi_DrInfo -> dri_Font -> tf_Baseline);
	Text(RPort,Label,Len);
}

	/* SetButton():
	 *
	 *	Set an aspect of the button.
	 */

STATIC ULONG __regargs
SetButton(struct IClass *class,Object *object,struct opSet *SetInfo)
{
	struct ButtonInfo	*ButtonInfo = INST_DATA(class,object);
	struct TagItem		*Tag;

		/* Pick up the button command. */

	if(Tag = FindTagItem(BGA_Command,SetInfo -> ops_AttrList))
		ButtonInfo -> ButtonCommand = (STRPTR)Tag -> ti_Data;

		/* Label text changed? */

	if(Tag = FindTagItem(GA_Text,SetInfo -> ops_AttrList))
	{
		struct RastPort	*RPort	= ObtainGIRPort(SetInfo -> ops_GInfo);
		STRPTR		 Label	= (STRPTR)Tag -> ti_Data;
		LONG		 Len	= strlen(Label),
				 Width	= ((struct Gadget *)object) -> Width - 8;

			/* Install the new label text. */

		((struct Gadget *)object) -> GadgetText = (struct IntuiText *)Label;

		if(RPort)
		{
				/* Determine label length. */

			while(Len > 0 && TextLength(RPort,Label,Len) > Width)
				Len--;

			ButtonInfo -> NumChars = Len;

			RenderText(RPort,SetInfo -> ops_GInfo,(struct Gadget *)object,Len);

			ReleaseGIRPort(RPort);
		}

		return(1);
	}
	else
		return(DoSuperMethodA(class,object,(Msg)SetInfo));
}

	/* RenderButton():
	 *
	 *	Render the button label.
	 */

STATIC ULONG __regargs
RenderButton(struct IClass *class,Object *object,struct gpRender *RenderInfo)
{
	struct ButtonInfo *ButtonInfo = INST_DATA(class,object);

		/* No label length calculated yet? */

	if(!ButtonInfo -> NumChars)
	{
		STRPTR	Label	= (STRPTR)((struct Gadget *)object) -> GadgetText;
		LONG	Len	= strlen(Label),
			Width	= ((struct Gadget *)object) -> Width - 8;

		while(Len > 0 && TextLength(RenderInfo -> gpr_RPort,Label,Len) > Width)
			Len--;

		ButtonInfo -> NumChars = Len;
	}

	RenderText(RenderInfo -> gpr_RPort,RenderInfo -> gpr_GInfo,(struct Gadget *)object,ButtonInfo -> NumChars);

	return(1);
}

	/* FreeButton():
	 *
	 *	Free a button gadget object.
	 */

STATIC ULONG __regargs
FreeButton(struct IClass *class,Object *object,Msg msg)
{
	struct ButtonInfo *ButtonInfo = INST_DATA(class,object);

	if(ButtonInfo -> ButtonImage)
		DisposeObject(ButtonInfo -> ButtonImage);

	return(DoSuperMethodA(class,object,msg));
}

	/* NewButton():
	 *
	 *	Create a new button object.
	 */

STATIC ULONG __regargs
NewButton(struct IClass *class,Object *object,struct opSet *SetMethod)
{
	LONG		 Width,
			 Height;
	struct TagItem	*Tag;
	struct Gadget	*NewGadget = NULL;

		/* Determine object width. */

	if(Tag = FindTagItem(GA_Width,SetMethod -> ops_AttrList))
		Width = Tag -> ti_Data;
	else
		Width = 0;

		/* Determine object height. */

	if(Tag = FindTagItem(GA_Height,SetMethod -> ops_AttrList))
		Height = Tag -> ti_Data;
	else
		Height = 0;

		/* Proper dimensions? */

	if(Width && Height)
	{
		struct Image *ButtonImage;

			/* Create a button frame. */

		if(ButtonImage = NewObject(NULL,"frameiclass",
			IA_Width,	Width,
			IA_Height,	Height,
		TAG_DONE))
		{
			struct TagItem	*OldTags = SetMethod -> ops_AttrList,
					 NewTags[2];

				/* Add new tag values. */

			NewTags[0] . ti_Tag	= GA_Image;
			NewTags[0] . ti_Data	= (ULONG)ButtonImage;
			NewTags[1] . ti_Tag	= TAG_MORE;
			NewTags[1] . ti_Data	= (ULONG)OldTags;

			SetMethod -> ops_AttrList = NewTags;

				/* Create new object. */

			if(NewGadget = (struct Gadget *)DoSuperMethodA(class,object,(Msg)SetMethod))
			{
				struct ButtonInfo *ButtonInfo = INST_DATA(class,NewGadget);

					/* Clear the number of characters displayed,
					 * the first rendering call will adjust it.
					 */

				ButtonInfo -> NumChars = 0;

					/* Install the corresponding command. */

				if(Tag = FindTagItem(BGA_Command,OldTags))
					ButtonInfo -> ButtonCommand = (STRPTR)Tag -> ti_Data;

					/* Remember the frame image. */

				ButtonInfo -> ButtonImage = ButtonImage;

					/* Restore the tags. */

				SetMethod -> ops_AttrList = OldTags;
			}
			else
				DisposeObject(ButtonImage);
		}
	}

	return((ULONG)NewGadget);
}

	/* HitButton():
	 *
	 *	Check to see whether a point is within the button frame.
	 */

STATIC ULONG __regargs
HitButton(struct IClass *class,Object *object,struct gpHitTest *HitInfo)
{
	struct ButtonInfo	*ButtonInfo	= INST_DATA(class,object);
	ULONG			*Spot		= (ULONG *)&HitInfo -> gpht_Mouse,
				*Size		= (ULONG *)&((struct Gadget *)object) -> Width;

	return(DoMethod((Object *)ButtonInfo -> ButtonImage,IM_HITFRAME,*Spot,*Size));
}

	/* HandleButton():
	 *
	 *	Handle button input.
	 */

STATIC ULONG __regargs
HandleButton(struct IClass *class,Object *object,struct gpInput *InputInfo)
{
	ULONG Result = GMR_MEACTIVE;

		/* Only mouse events are supported. */

	if(InputInfo -> gpi_IEvent -> ie_Class == IECLASS_RAWMOUSE)
	{
		struct ButtonInfo	*ButtonInfo	= INST_DATA(class,object);
		struct Gadget		*Button		= (struct Gadget *)object;
		BYTE			 Redraw;

			/* Selection cancelled or no command available? */

		if(InputInfo -> gpi_IEvent -> ie_Code == IECODE_RBUTTON || !ButtonInfo -> ButtonCommand[0])
		{
			*InputInfo -> gpi_Termination = 1;

			Result = GMR_REUSE | GMR_VERIFY;

			if(Button -> Flags & GFLG_SELECTED)
			{
				Button -> Flags &= ~GFLG_SELECTED;

				Redraw = TRUE;
			}
			else
				Redraw = FALSE;
		}
		else
		{
				/* Select button released? */

			if(InputInfo -> gpi_IEvent -> ie_Code == (IECODE_LBUTTON | IECODE_UP_PREFIX))
			{
				if(Button -> Flags & GFLG_SELECTED)
				{
					*InputInfo -> gpi_Termination = 0;

					Result = GMR_NOREUSE | GMR_VERIFY;

					Button -> Flags &= ~GFLG_SELECTED;

					Redraw = TRUE;
				}
				else
				{
					*InputInfo -> gpi_Termination = 1;

					Result = GMR_REUSE | GMR_VERIFY;

					Redraw = FALSE;
				}
			}
			else
			{
				ULONG *Size = (ULONG *)&Button -> Width;

					/* Hit inside the boundaries? */

				if(DoMethod((Object *)ButtonInfo -> ButtonImage,IM_HITFRAME,PACK_ULONG(InputInfo -> gpi_Mouse . X,InputInfo -> gpi_Mouse . Y),*Size))
				{
					if(Button -> Flags & GFLG_SELECTED)
						Redraw = FALSE;
					else
					{
						Button -> Flags |= GFLG_SELECTED;

						Redraw = TRUE;
					}
				}
				else
				{
					if(Button -> Flags & GFLG_SELECTED)
					{
						Button -> Flags &= ~GFLG_SELECTED;

						Redraw = TRUE;
					}
					else
						Redraw = FALSE;
				}
			}
		}

			/* Redraw the object? */

		if(Redraw)
		{
			struct RastPort *RPort;

			if(RPort = ObtainGIRPort(InputInfo -> gpi_GInfo))
			{
				RenderText(RPort,InputInfo -> gpi_GInfo,Button,ButtonInfo -> NumChars);

				ReleaseGIRPort(RPort);
			}
		}
	}

	return(Result);
}

	/* ButtonDispatch():
	 *
	 *	The button gadget class dispatcher.
	 */

STATIC ULONG __saveds __asm
ButtonDispatch(register __a0 struct IClass *class,register __a2 Object *object,register __a1 Msg msg)
{
		/* Select the message method. */

	switch(msg -> MethodID)
	{
		case OM_NEW:

			return(NewButton(class,object,(struct opSet *)msg));

		case OM_UPDATE:
		case OM_SET:

			return(SetButton(class,object,(struct opSet *)msg));

		case OM_DISPOSE:

			return(FreeButton(class,object,msg));

		case GM_RENDER:

			return(RenderButton(class,object,(struct gpRender *)msg));

		case GM_HITTEST:

			return(HitButton(class,object,(struct gpHitTest *)msg));

		case GM_GOINACTIVE:

			return(0);

		case GM_GOACTIVE:

			return(GMR_MEACTIVE);

		case GM_HANDLEINPUT:

			return(HandleButton(class,object,(struct gpInput *)msg));

		default:

			return(DoSuperMethodA(class,object,msg));
	}
}

	/* FreeButtonClass(VOID):
	 *
	 *	Free private button gadget class.
	 */

STATIC VOID
FreeButtonClass(VOID)
{
	if(ButtonClass)
	{
		FreeClass(ButtonClass);

		ButtonClass = NULL;
	}
}

	/* NewButtonClass(VOID):
	 *
	 *	Create private button gadget class.
	 */

STATIC BYTE
NewButtonClass(VOID)
{
	if(ButtonClass = MakeClass(NULL,GADGETCLASS,NULL,sizeof(struct ButtonInfo),0))
	{
		ButtonClass -> cl_Dispatcher . h_Entry = (LONG (*)())ButtonDispatch;

		return(TRUE);
	}
	else
		return(FALSE);
}

	/* FastDeleteScroller(VOID):
	 *
	 *	Delete fast! macro window scroller.
	 */

STATIC VOID
FastDeleteScroller(VOID)
{
	if(Scroller)
	{
		DisposeObject(Scroller);

		Scroller = NULL;
	}

	if(UpArrow)
	{
		DisposeObject(UpArrow);

		UpArrow = NULL;
	}

	if(DownArrow)
	{
		DisposeObject(DownArrow);

		DownArrow = NULL;
	}

	if(UpImage)
	{
		DisposeObject(UpImage);

		UpImage = NULL;
	}

	if(DownImage)
	{
		DisposeObject(DownImage);

		DownImage = NULL;
	}
}

	/* FastCreateScroller(struct Screen *Screen):
	 *
	 *	Create fast! macro window scroller.
	 */

STATIC BYTE __regargs
FastCreateScroller(struct Screen *Screen)
{
	struct DrawInfo	*DrawInfo;
	BYTE		 Result = FALSE;

	if(DrawInfo = GetScreenDrawInfo(Screen))
	{
		LONG	SizeWidth,
			SizeHeight;
		UWORD	SizeType;

		if(Screen -> Flags & SCREENHIRES)
		{
			SizeWidth	= 18;
			SizeHeight	= 10;

			SizeType	= SYSISIZE_MEDRES;
		}
		else
		{
			SizeWidth	= 13;
			SizeHeight	= 11;

			SizeType	= SYSISIZE_LOWRES;
		}

		RightBorderWidth = SizeWidth;

		if(UpImage = (struct Image *)NewObject(NULL,"sysiclass",
			SYSIA_Size,	SizeType,
			SYSIA_Which,	UPIMAGE,
			SYSIA_DrawInfo,	DrawInfo,
		TAG_DONE))
		{
			if(DownImage = (struct Image *)NewObject(NULL,"sysiclass",
				SYSIA_Size,	SizeType,
				SYSIA_Which,	DOWNIMAGE,
				SYSIA_DrawInfo,	DrawInfo,
			TAG_DONE))
			{
				if(Scroller = NewObject(NULL,"propgclass",
					GA_ID,		GAD_SCROLLER,

					GA_Top,		Screen -> WBorTop + Screen -> Font -> ta_YSize + 2,
					GA_RelHeight,	-(Screen -> WBorTop + Screen -> Font -> ta_YSize + 2 + SizeHeight + 1 + 2 * ARROW_HEIGHT),
					GA_Width,	SizeWidth - 8,
					GA_RelRight,	-(SizeWidth - 5),

					GA_Immediate,	TRUE,
					GA_FollowMouse,	TRUE,
					GA_RelVerify,	TRUE,
					GA_RightBorder,	TRUE,

					PGA_Freedom,	FREEVERT,
					PGA_NewLook,	TRUE,
					PGA_Borderless,	TRUE,

					PGA_Visible,	1,
					PGA_Total,	1,
				TAG_DONE))
				{
					STATIC struct TagItem ArrowMappings[] =
					{
						GA_ID,	GA_ID,
						TAG_END
					};

					if(UpArrow = NewObject(NULL,"buttongclass",
						GA_ID,		GAD_UP,

						GA_Image,	UpImage,
						GA_RelRight,	-(SizeWidth - 1),
						GA_RelBottom,	-(SizeHeight - 1 + 2 * ARROW_HEIGHT),
						GA_Height,	ARROW_HEIGHT,
						GA_Width,	SizeWidth,
						GA_Immediate,	TRUE,
						GA_RelVerify,	TRUE,
						GA_Previous,	Scroller,
						GA_RightBorder,	TRUE,

						ICA_TARGET,	ICTARGET_IDCMP,
						ICA_MAP,	ArrowMappings,
					TAG_DONE))
					{
						if(DownArrow = NewObject(NULL,"buttongclass",
							GA_ID,		GAD_DOWN,

							GA_Image,	DownImage,
							GA_RelRight,	-(SizeWidth - 1),
							GA_RelBottom,	-(SizeHeight - 1 + ARROW_HEIGHT),
							GA_Height,	ARROW_HEIGHT,
							GA_Width,	SizeWidth,
							GA_Immediate,	TRUE,
							GA_RelVerify,	TRUE,
							GA_Previous,	UpArrow,
							GA_RightBorder,	TRUE,

							ICA_TARGET,	ICTARGET_IDCMP,
							ICA_MAP,	ArrowMappings,
						TAG_DONE))
							Result = TRUE;
					}
				}
			}
		}

		FreeScreenDrawInfo(Screen,DrawInfo);
	}

	return(Result);
}

	/* RefreshFastWindow(WORD FastWindowHeight):
	 *
	 *	Refresh the contents of the fast! macro window.
	 */

VOID
RefreshFastWindow(BYTE FullRefresh)
{
	if(FastWindow)
	{
		LONG Count = (FastWindow -> Height - (FastWindow -> BorderTop + FastWindow -> BorderBottom + 1)) / (2 + UserFontHeight + 2 + 1);

		if(FullRefresh)
			ButtonCount = 0;

		if(Count == ButtonCount && Buttons)
		{
			LONG NewTop;

			if(!GetAttr(PGA_Top,Scroller,&NewTop))
				NewTop = 0;

			if(NewTop != ButtonTop)
			{
				ButtonTop = NewTop;

				Count = 0;

				if(FastMacroList . lh_Head -> ln_Succ)
				{
					struct DrawInfo *DrawInfo;

					if(DrawInfo = GetScreenDrawInfo(FastWindow -> WScreen))
					{
						struct MacroNode	*Node	= (struct MacroNode *)GetListNode(ButtonTop,&FastMacroList);
						struct Gadget		*Button	= Buttons;

						while(Count < ButtonCount && Node -> mn_Succ)
						{
							SetGadgetAttrs(Button,FastWindow,NULL,
								GA_Text,	Node -> mn_Macro,
								BGA_Command,	Node -> mn_Code,
							TAG_DONE);

							Count++;

							Button = Button -> NextGadget;

							Node = Node -> mn_Succ;
						}

						FreeScreenDrawInfo(FastWindow -> WScreen,DrawInfo);
					}
				}
			}
		}
		else
		{
			LONG Top = FastWindow -> BorderTop + 1;

			if(Buttons)
			{
				struct Gadget	*Next = Buttons,
						*Button;

				RemoveGList(FastWindow,Buttons,-1);

				while(Button = Next)
				{
					Next = Button -> NextGadget;

					DisposeObject(Button);
				}

				Buttons = NULL;
			}

			ButtonCount = Count;

			if(ButtonTop >= FastMacroCount)
				ButtonTop = FastMacroCount - ButtonCount;
			else
			{
				if(ButtonTop + ButtonCount > FastMacroCount)
					ButtonTop = FastMacroCount - ButtonCount;
			}

			if(ButtonTop < 0)
				ButtonTop = 0;

			Count = 0;

			if(FastMacroList . lh_Head -> ln_Succ)
			{
				struct DrawInfo *DrawInfo;

				if(DrawInfo = GetScreenDrawInfo(FastWindow -> WScreen))
				{
					struct MacroNode	*Node		= (struct MacroNode *)GetListNode(ButtonTop,&FastMacroList);
					LONG			 Width		= FastWindow -> Width - (FastWindow -> BorderLeft + FastWindow -> BorderRight);
					struct Gadget		*Previous	= NULL,
								*Button;

					while(Count < ButtonCount && Node -> mn_Succ)
					{
						if(Button = NewObject(ButtonClass,NULL,
							GA_ID,		Count,

							GA_Top,		Top,
							GA_Left,	FastWindow -> BorderLeft,

							GA_Width,	Width,
							GA_Height,	(2 + UserFontHeight + 2),
							GA_RelVerify,	TRUE,

							GA_DrawInfo,	DrawInfo,
							GA_Text,	Node -> mn_Macro,
							BGA_Command,	Node -> mn_Code,

							Previous ? GA_Previous : TAG_IGNORE,Previous,
						TAG_DONE))
						{
							Top += 2 + UserFontHeight + 2 + 1;

							Count++;

							Previous = Button;

							if(!Buttons)
								Buttons = Button;

							Node = Node -> mn_Succ;
						}
					}

					FreeScreenDrawInfo(FastWindow -> WScreen,DrawInfo);
				}
			}

			if(Count)
			{
				AddGList(FastWindow,Buttons,-1,-1,NULL);

				RefreshGList(Buttons,FastWindow,NULL,-1);
			}

			if(Top < FastWindow -> Height - (FastWindow -> BorderBottom + 1))
			{
				SetAPen(FastWindow -> RPort,0);

				RectFill(FastWindow -> RPort,FastWindow -> BorderLeft,Top,FastWindow -> Width - (FastWindow -> BorderRight + 1),FastWindow -> Height - (FastWindow -> BorderBottom + 1));
			}

			SetGadgetAttrs(Scroller,FastWindow,NULL,
				PGA_Top,	ButtonTop,
				PGA_Visible,	ButtonCount,
				PGA_Total,	FastMacroCount,
			TAG_DONE);
		}
	}
}

	/* CloseFastWindow():
	 *
	 *	Close the fast! macro window and free the associated resources.
	 */

VOID
CloseFastWindow()
{
	CheckItem(MEN_FAST_MACROS_WINDOW,FALSE);

	if(Buttons)
	{
		struct Gadget	*Next = Buttons,
				*Button;

		RemoveGList(FastWindow,Buttons,-1);

		while(Button = Next)
		{
			Next = Button -> NextGadget;

			DisposeObject(Button);
		}

		Buttons = NULL;
	}

	if(FastWindow)
	{
		PutWindowInfo(WINDOW_FAST,FastWindow -> LeftEdge,FastWindow -> TopEdge,FastWindow -> Width,FastWindow -> Height);

		ClearMenuStrip(FastWindow);
		CloseWindowSafely(FastWindow);

		FastWindow = NULL;
	}

	FreeButtonClass();
}

	/* OpenFastWindow():
	 *
	 *	Open the fast! macro window.
	 */

BYTE
OpenFastWindow()
{
	if(NewButtonClass())
	{
		if(FastCreateScroller(Window -> WScreen))
		{
			LONG	Left	= 0,
				Top	= 0,
				Width	= Window -> WScreen -> WBorLeft + 2 + SZ_GetLen("####################") + 2 + RightBorderWidth,
				Height	= 0;

			GetWindowInfo(WINDOW_FAST,&Left,&Top,&Width,&Height,NULL,Window -> WScreen -> WBorTop + Window -> WScreen -> Font -> ta_YSize + 1 + 1 + 10 * (2 + UserFontHeight + 2 + 1) + Window -> WScreen -> WBorBottom);

			if(WindowZoomBox . Left == -1)
			{
				WindowZoomBox . Left	= 0;
				WindowZoomBox . Top	= 0;
				WindowZoomBox . Width	= Width;
				WindowZoomBox . Height	= Window -> WScreen -> WBorTop + Window -> WScreen -> Font -> ta_YSize + 1;
			}

			if(FastWindow = OpenWindowTags(NULL,
				WA_Width,		Width,
				WA_Height,		Height,

				WA_Left,		Left,
				WA_Top,			Top,

				WA_DragBar,		TRUE,
				WA_DepthGadget,		TRUE,
				WA_CloseGadget,		TRUE,
				WA_RMBTrap,		TRUE,
				WA_Zoom,		&WindowZoomBox,
				WA_NoCareRefresh,	TRUE,

				WA_SizeGadget,		TRUE,

				WA_MinWidth,		Width,
				WA_MaxWidth,		Width,

				WA_MinHeight,		Window -> WScreen -> WBorTop + Window -> WScreen -> Font -> ta_YSize + 1 + 2 * (2 + UserFontHeight + 2 + 1) + 1 + Window -> WScreen -> WBorBottom,
				WA_MaxHeight,		Window -> WScreen -> Height,

				WA_Title,		LocaleString(MSG_FASTMACROS_FAST_MACROS_TXT),

				WA_NewLookMenus,	TRUE,
				WA_Gadgets,		Scroller,

				WA_CustomScreen,	Window -> WScreen,
			TAG_DONE))
			{
				ButtonTop	= 0;
				ButtonCount	= -1;

				FastWindow -> UserPort = Window -> UserPort;

				ModifyIDCMP(FastWindow,Window -> IDCMPFlags | IDCMP_IDCMPUPDATE | IDCMP_SIZEVERIFY);

				SetMenuStrip(FastWindow,Menu);

				FastWindow -> Flags &= ~WFLG_RMBTRAP;

				RefreshFastWindow(TRUE);

				CheckItem(MEN_FAST_MACROS_WINDOW,TRUE);

				return(TRUE);
			}

			FastDeleteScroller();
		}

		FreeButtonClass();
	}

	return(FALSE);
}

	/* HandleFastWindowGadget(ULONG IClass,LONG ICode,LONG GadgetID):
	 *
	 *	Process fast! macro window input.
	 */

VOID
HandleFastWindowGadget(ULONG IClass,LONG ICode,LONG GadgetID)
{
	STATIC BYTE Scrolling = FALSE;

	switch(IClass)
	{
		case IDCMP_GADGETUP:

			if(GadgetID == GAD_SCROLLER)
				Scrolling = FALSE;
			else
			{
				if(GadgetID < GAD_SCROLLER && !ICode)
				{
					struct MacroNode *Node;

					if(Node = (struct MacroNode *)GetListNode(GadgetID + ButtonTop,&FastMacroList))
					{
						if(Node -> mn_Code[0])
							SerialCommand(Node -> mn_Code);
					}
				}
			}

			break;

		case IDCMP_GADGETDOWN:

			if(GadgetID == GAD_SCROLLER)
			{
				Scrolling = TRUE;

				RefreshFastWindow(FALSE);
			}

			break;

		case IDCMP_MOUSEMOVE:

			if(Scrolling)
				RefreshFastWindow(FALSE);

			break;

		case IDCMP_IDCMPUPDATE:

			switch(GadgetID)
			{
				case GAD_UP:	if(ButtonTop)
						{
							SetGadgetAttrs(Scroller,FastWindow,NULL,
								PGA_Top,ButtonTop - 1,
							TAG_DONE);

							RefreshFastWindow(FALSE);
						}

						break;

				case GAD_DOWN:	if(ButtonTop + ButtonCount < FastMacroCount)
						{
							SetGadgetAttrs(Scroller,FastWindow,NULL,
								PGA_Top,ButtonTop + 1,
							TAG_DONE);

							RefreshFastWindow(FALSE);
						}

						break;
			}

			break;
	}
}
