/*
 *	ParM.c - Copyright © 1990 by S.R. & P.C.
 *
 *	Created:	01 Jul 1990
 *	Modified:	09 Feb 1992  11:45:05
 *
 *	Make>> make
 */

#include "ParMBase.h"

#define COPYRIGHT			"ParM V3.6 Copyright © 1990-92 by S.R. & P.C."
#define DEFAULT_CONFIG_FILE "S:ParM.cfg"

#define BASE_TIME	100000	/* the base time for all time operations: 10 time per seconds */

#define THE_END			0
#define NO_WIN			10

#define OPEN_ITEM		0
#define UPDATE_ITEM		1
#define STD_CFG_ITEM	2
#define CMDMODE_ITEM	3
#define COMMAND_ITEM	4
#define CHDIR_ITEM		5
#define QUIT_ITEM		6

/* MakeDateTimeString() arg */
#define DATE_STRING		1
#define TIME_STRING		2


extern void setmem(void *mem, size_t size, long value);
extern void CreateParMenu(short Color);
extern void exit(int);


/*****				global variables					*****/

extern struct ExecBase *SysBase;
extern struct ParMBase *ParMBase;
extern struct LayersBase *LayersBase;
extern struct IntuitionBase *IntuitionBase;
extern struct Menu Menu1;
extern struct MenuItem SubItem1;
extern struct WBStartup *WBenchMsg;
extern char HelpUsage[];
extern char Template[];

struct TextAttr MenuFont;

/*****				 local variables					*****/

struct ParMOpt {
	short ParMMode;		/* Window, MyMenu, CLI */
	short ParMenuColor;	/* Detail pen for first menu items */
	short MTDetailPen;	/* detailpen of the info string */
	short MTBlockPen;	/* blockpen of the info string */
	UWORD RefreshTime;	/* number of BASE_TIME to wait before refreshing InfoTop */
	UWORD Flags;		/* See below */
};

/* ParM modes */
#define MODE_CLI		0	/* default mode */
#define MODE_WINDOW		1
#define MODE_MYMENU		2

/* ParM Flags */
#define PF_SHOWTIME		0x0001
#define PF_SHOWMEM		0x0002
#define PF_AUTOFRONT	0x0004
#define PF_USENULL		0x0008		/* Redirect all commands to NULL: (Assure full detach) */


static struct Process *ParMProcess;
static struct Window *Win;
static struct Screen *WBS;
static struct Menu *LastWBMenu, *TheMenu;
static struct MsgPort SwapPort;
static short FirstParMMenuNum;	/* = 0  if not attached to WorkBench */
static ULONG OldIDCMP, WinSignal;
static BOOL PortTaken;
static BOOL quit;
static BOOL DoSetRast = TRUE;	/* Clear window on Open */
static struct FileReq FileReq;
static char Dir[REQ_DSIZE + 1];
static char File[REQ_FCHARS + 1];
static char Path[REQ_DSIZE + REQ_FCHARS + 2];
static struct ParMConfig ParMConfig;
static struct ParMOpt ParMOpt = {MODE_WINDOW, -1, 1, 2, 10, PF_AUTOFRONT};
static BOOL DoNextSelect;
static struct DateStamp Time;	/* the System Time */
static struct TextFont *Font;
static char InfoString[80];
static short OSVersion;


/*
 *	ParseArg presume that all TTN_something between FIRST_WIN_ARG and
 *	LAST_WIN_ARG are relative to ParM own window.
 *	ParseArg also presume that all args between TTN_DRAGBAR and TTN_SHOWTIME,
 *	and TTN_USENULL must have ON or OFF as argument.
 */

#define FIRST_WIN_ARG	2
#define LAST_WIN_ARG	14

#define TTN_MYMENU			0
#define TTN_CLIWINDOW		1
#define TTN_OWNWINDOW		2
#define TTN_LEFTEDGE		3
#define TTN_TOPEDGE			4
#define TTN_DETAILPEN		5
#define TTN_BLOCKPEN		6
#define TTN_DRAGBAR			7
#define TTN_DEPTH			8
#define TTN_AUTOFRONT		9
#define TTN_SHOWMEM			10
#define TTN_SHOWTIME		11
#define TTN_REFRESHTIME		12
#define TTN_MTDETAILPEN		13
#define TTN_MTBLOCKPEN		14
#define TTN_MENUCOLOR		15
#define TTN_STACKSIZE		16
#define TTN_CONFIGFILE		17
#define TTN_USENULL			18		/* Keep in last position to prevent WB access */

static char *ToolTypesArg[] = {
	"MYMENU",
	"CLIWINDOW",
	"OWNWINDOW",
	"LEFTEDGE",
	"TOPEDGE",
	"DETAILPEN",
	"BLOCKPEN",
	"NODRAGBAR",
	"DEPTHGADGETS",
	"NOAUTOFRONT",
	"SHOWMEM",
	"SHOWTIME",
	"REFRESHTIME",
	"MTDETAILPEN",
	"MTBLOCKPEN",
	"MENUCOLOR",
	"STACKSIZE",
	"CONFIGFILE",
	"USENULL"
};


/* Number of CLI Args in template && ToolTypesArg in List */

#define NUM_ARGS	19		/* WARNING: also defined in Startup.c */


static struct NewWindow NWS = {
	0, 0,			/* window XY origin relative to TopLeft of screen */
	0, 10,			/* window width and height */
	1, 2,			/* detail and block pens */
	MENUPICK|RAWKEY|REFRESHWINDOW|ACTIVEWINDOW|INACTIVEWINDOW,	/* IDCMP flags */
	WINDOWDRAG|SMART_REFRESH|NOCAREREFRESH,	/* other window flags */
	NULL,			/* first gadget in gadget list */
	NULL,			/* custom CHECKMARK imagery */
	(UBYTE *)"ParM",/* window title */
	NULL,			/* custom screen pointer */
	NULL,			/* custom bitmap */
	5, 5,			/* minimum width and height */
	-1, -1,			/* maximum width and height */
	WBENCHSCREEN	/* destination screen type */
};


void MakeDateTimeString(char *dest, short Type)
{
	struct DateTime dt;
	char Date[12], Time[12], Day[12];

	setmem(&dt, sizeof(struct DateTime), 0);
	DateStamp(&dt.dat_Stamp);
	if (Type == DATE_STRING) {
		dt.dat_Flags = DTF_FUTURE;
		dt.dat_StrDay = Day;
		dt.dat_StrDate = Date;
		dt.dat_StrTime = Time;
	}
	else
		dt.dat_StrTime = dest;
	StamptoStr(&dt);
	dt.dat_StrTime[8] = '\0';	/* StamptoStr() don't allways NULL terminate this string */
	if (Type == DATE_STRING)
		SPrintf(dest, "%-10s %s %s", Day, Date, Time);
}


static void MakeInfoString(void)
{
	short chip_free, fast_free;
	short i = 0;

	if (Win == IntuitionBase->ActiveWindow)
		MakeDateTimeString(InfoString, DATE_STRING);
	else {
		if (ParMOpt.Flags & PF_SHOWMEM) {
			chip_free = AvailMem(MEMF_CHIP) >> 10;
			fast_free = AvailMem(MEMF_FAST) >> 10;
			i = SPrintf(InfoString, "Chip:%4d Fast:%5d", chip_free, fast_free);
		}
		if (ParMOpt.Flags & PF_SHOWTIME) {
			if (ParMOpt.Flags & PF_SHOWMEM)
				InfoString[i++] = ' ';
			MakeDateTimeString(&InfoString[i], TIME_STRING);
		}
	}
}


static void RefreshInfoString(void)
{
	struct RastPort *rp;

	if (ParMOpt.Flags & (PF_SHOWMEM|PF_SHOWTIME)) {
		MakeInfoString();
		rp = Win->RPort;
		if (DoSetRast) {
			SetRast(rp, ParMOpt.MTBlockPen);
			DoSetRast = FALSE;
		}
		SetAPen(rp, ParMOpt.MTDetailPen);
		Move(rp, 4, Font->tf_Baseline + 1);
		Text(rp, InfoString, strlen(InfoString));
		if (rp->cp_x < Win->Width) {
			SetAPen(rp, ParMOpt.MTBlockPen);
			RectFill(rp, rp->cp_x, 1, Win->Width, Font->tf_YSize);
		}
	}
}


static struct Window *FindWorkBench(void)
{
	struct Window *WBW = NULL;
	ULONG ilock;

	ilock = LockIBase(0L);	/* Just so's things don't change on us */
	WBW = WBS->FirstWindow;
	while (WBW) {
		/* Only main WB Window has DISKINSERTED IDCMPFlag set, so find it */
		if ((WBW->Flags & WBENCHWINDOW) && (WBW->IDCMPFlags & DISKINSERTED)) {
			TheMenu = WBW->MenuStrip;
			LastWBMenu = TheMenu->NextMenu->NextMenu;
			FirstParMMenuNum = 3;
			if (OSVersion >= 36) {
				LastWBMenu = LastWBMenu->NextMenu;
				FirstParMMenuNum = 4;
			}
			break;
		}
		WBW = WBW->NextWindow;
	}
	UnlockIBase(ilock);
	return WBW;
}


void TakeMsgPort(void)
{
	struct MsgPort *WinPort;
	struct List *myMsgList = &(SwapPort.mp_MsgList);
	struct List *winMsgList;

	SwapPort.mp_Node.ln_Type = NT_MSGPORT;
	SwapPort.mp_Flags = PA_SIGNAL;
	Forbid();

	/* get the info we need */
	WinPort = Win->UserPort;
	winMsgList = &(WinPort->mp_MsgList);

	/* setup our port */
	SwapPort.mp_SigBit = WinPort->mp_SigBit;
	SwapPort.mp_SigTask = WinPort->mp_SigTask;

	/* flip things around */
	WinPort->mp_SigTask = (struct Task *) ParMProcess;
	/* Prevent signal for being allocated later */
	WinSignal = AllocSignal(WinPort->mp_SigBit);
	myMsgList->lh_Head = (struct Node *) & winMsgList->lh_Tail;
	myMsgList->lh_TailPred = winMsgList->lh_TailPred;
	winMsgList->lh_TailPred = (struct Node *) & myMsgList->lh_Head;
	myMsgList->lh_TailPred->ln_Succ = (struct Node *) & myMsgList->lh_Tail;

	Permit();

	/* prevent deadlocks */
	SetTaskPri((struct Task *) ParMProcess, SwapPort.mp_SigTask->tc_Node.ln_Pri + 1);
}


/* Restore things the way they were before TakeMsgPort() */

void DelMsgPort(void)
{
	struct Message *Msg;
	struct List *myMsgList = &(SwapPort.mp_MsgList);

	Forbid();
	/* clean out our list */
	while (Msg = GetMsg(&SwapPort))
		PutMsg(&SwapPort, Msg);
	/* restore things */
	myMsgList->lh_Head->ln_Pred = myMsgList->lh_TailPred;
	myMsgList->lh_TailPred->ln_Succ = myMsgList->lh_Head;
	Win->UserPort->mp_SigTask = SwapPort.mp_SigTask;
	if (WinSignal)
		FreeSignal(WinSignal);
	Permit();
}


/* Configuration managing functions  */

static void InstallWBMenus(struct Menu * Menus)
{
	long ilock;

	ilock = LockIBase(0);
	ClearMenuStrip(Win);
	NewCleanUp(TheMenu, &MenuFont, 0);
	LastWBMenu->NextMenu = Menus;
	SetMenuStrip(Win, TheMenu);
	UnlockIBase(ilock);
}


static void ClearMenus(void)
{
	if (ParMOpt.ParMMode == MODE_MYMENU) {
		InstallWBMenus(NULL);
		Menu1.LeftEdge = LastWBMenu->LeftEdge + LastWBMenu->Width;
	}
	else
		ClearMenuStrip(Win);
}


void UpDateMenus(void)
{
	ClearMenus();
	ParMConfig.MenuPen = ParMOpt.ParMenuColor;
	if (!ParseMenus(&ParMConfig))
		FreeMenus(&ParMConfig);
	if (ParMOpt.ParMMode == MODE_MYMENU)
		InstallWBMenus(&Menu1);
	else
		SetMenuStrip(Win, &Menu1);
}


static void OpenMenus(void)
{
	char *yuck;

	FileReq.Title = "Open";
	strcpy(Dir, ParMConfig.CurCfg);
	strcpy(File, yuck = BaseName(Dir));
	*yuck = '\0';		/* cut off filename from fr_Dir */
	if (!FileRequester(&FileReq))
		return;		/* quit if Open canceled */
	strcpy(ParMConfig.CurCfg, Path);
	UpDateMenus();
}


/* Print busy message in place of window title */

static void Busy(BOOL status)
{
	if (ParMOpt.ParMMode == MODE_WINDOW) {
		SetWindowTitles(Win, (status) ? "Busy" : "ParM", COPYRIGHT);
		if (ParMOpt.Flags & (PF_SHOWMEM|PF_SHOWTIME)) {
			if (status)		/* Busy */
				RefreshWindowFrame(Win);
			else
				SetRast(Win->RPort, ParMOpt.MTBlockPen);
		}
	}
}


/* Atchao, bonsoir...  */

void Bye(short err)
{
	if (err == THE_END) {
		ClearMenus();
		FreeMenus(&ParMConfig);
		if (PortTaken)
			DelMsgPort();
		if (ParMOpt.ParMMode == MODE_CLI)
			ModifyIDCMP(Win, OldIDCMP);
		else if (ParMOpt.ParMMode == MODE_WINDOW)
			CloseWindowSafely(Win, NULL);
	}
	CloseFont(Font);
	exit(err);
}


/* change parm current directory */

static void ChangeDir(char *dir)
{
	BPTR NewLock, OldLock;

	NewLock = Lock(dir, ACCESS_READ);
	if (NewLock) {
		OldLock = CurrentDir(NewLock);
		if (OldLock)
			UnLock(OldLock);
	}
}


static void DoExtMenu(USHORT MenuNum)
{
	struct Extended_MenuItem *Item;
	struct Extended_WBStartup *EWBS;

	Item = (struct Extended_MenuItem *) ItemAddress(TheMenu, MenuNum);
	switch (Item->emi_Mode) {
	case TOK_ARUN:
		ARun(&ParMConfig, &Item->emi_RunInfo);
		break;
	case TOK_RUN:
	case TOK_SHELL:
		Run(&ParMConfig, &Item->emi_RunInfo, Item->emi_Mode);
		break;
	case TOK_WB:
		if (EWBS = MakeWBStartup(&ParMConfig, &Item->emi_RunInfo))
			WBRun(&ParMConfig, EWBS, Item->emi_RunInfo.ri_Pri);
		break;
	case TOK_CFG:		/* new cfg */
		strcpy(ParMConfig.CurCfg, Item->emi_RunInfo.ri_Cmd);
		UpDateMenus();
		DoNextSelect = FALSE;	/* Tell DoIntuiMsg not to execute next menu selection */
	}
}


static void parse_arg(short opt, char *args)
{
	char Buf[80];
	long argl;

	if ((opt >= TTN_DRAGBAR && opt <= TTN_SHOWTIME) || opt == TTN_USENULL) {
		if (!Strcmp(args, "ON"))
			argl = 1;
		else if (!Strcmp(args, "OFF"))
			argl = 0;
		else {
			SPrintf(Buf, "ON|OFF argument required for %s", ToolTypesArg[opt]);
			if (WBenchMsg)
				SimpleRequest(ParMConfig.ReqTitle, Buf);
			else
				Printf("%s\n", Buf);
			Bye(NO_WIN);
		}
	}
	else
		argl = Atol(args);
	if (opt >= FIRST_WIN_ARG && opt <= LAST_WIN_ARG)
		ParMOpt.ParMMode = MODE_WINDOW;
	switch (opt) {
	case TTN_MYMENU:
		ParMOpt.ParMMode = MODE_MYMENU;
		break;
	case TTN_CLIWINDOW:
		if (!WBenchMsg)
			ParMOpt.ParMMode = MODE_CLI;
		break;
	case TTN_STACKSIZE:
		if (argl >= 4000)
			ParMConfig.DefaultStack = argl;
		break;
	case TTN_LEFTEDGE:
		NWS.LeftEdge = argl;
		break;
	case TTN_TOPEDGE:
		NWS.TopEdge = argl;
		break;
	case TTN_DETAILPEN:
		NWS.DetailPen = argl;
		break;
	case TTN_BLOCKPEN:
		NWS.BlockPen = argl;
		ParMOpt.MTBlockPen = argl;	/* Default MTBLOCKPEN is BLOCKPEN */
		break;
	case TTN_MENUCOLOR:
		ParMOpt.ParMenuColor = argl;
		break;
	case TTN_CONFIGFILE:
		strcpy(ParMConfig.CurCfg, args);
		break;
	case TTN_DRAGBAR:	/* no drag bar */
		if (argl)
			NWS.Flags |= WINDOWDRAG;
		else
			NWS.Flags &= ~WINDOWDRAG;
		break;
	case TTN_DEPTH:		/* depth gadgets */
		if (argl)
			NWS.Flags |= WINDOWDEPTH;
		else
			NWS.Flags &= ~WINDOWDEPTH;
		/* don't break, depth gadgets disable autofront */
	case TTN_AUTOFRONT:
		if (argl)
			ParMOpt.Flags |= PF_AUTOFRONT;
		else
			ParMOpt.Flags &= ~PF_AUTOFRONT;
		break;
	case TTN_SHOWMEM:
		if (argl)
			ParMOpt.Flags |= PF_SHOWMEM;
		else
			ParMOpt.Flags &= ~PF_SHOWMEM;
		break;
	case TTN_SHOWTIME:
		if (argl)
			ParMOpt.Flags |= PF_SHOWTIME;
		else
			ParMOpt.Flags &= ~PF_SHOWTIME;
		break;
	case TTN_REFRESHTIME:
		ParMOpt.RefreshTime = argl;
		break;
	case TTN_MTDETAILPEN:
		ParMOpt.MTDetailPen = argl;
		break;
	case TTN_MTBLOCKPEN:
		ParMOpt.MTBlockPen = argl;
		break;
	case TTN_USENULL:
		if (argl)
			ParMOpt.Flags |= PF_USENULL;
		else
			ParMOpt.Flags &= ~PF_USENULL;
		break;
	}
}


void main(int argc, char *argv[])
{
	struct CommandLineInterface *cli;
	struct DiskObject *dop;
	struct MsgPort *WorkPort;
	struct IntuiMessage *IMsg;
	ULONG Class;
	USHORT Code, Qual;
	APTR WindowPtr;
	BPTR fh;
	char *arg;
	short i;

	/* for TopInfo String */
	struct MsgPort *Timer_Port = NULL;
	struct timerequest Time_Req;
	long TimerOpen = 1;
	long IntuiSignal = 0, TimerSignal = 0, Signal = 0;

	/* for GetEnv() */
	int ParMOptArgC;
	char **ParMOptArgV;
	char ParMOptString[256];

	OSVersion = IntuitionBase->LibNode.lib_Version;
	TheMenu = &Menu1;
	InitDefaults(&ParMConfig);
	ParMConfig.ReqTitle = "ParM Request";
	ParMConfig.LinkMenu = &Menu1;
	ParMConfig.SimpleCmdMode = TRUE;
	FileReq.Dir = Dir;
	FileReq.File = File;
	FileReq.PathName = Path;
	strcpy(FileReq.Show, "*.cfg");
	ParMProcess = (struct Process *)SysBase->ThisTask;
	strcpy(ParMConfig.CurCfg, DEFAULT_CONFIG_FILE);

	WBS = OpenWorkBench();
	AskFont(&WBS->RastPort, &MenuFont);		/* Get workbench screen font */
	Font = OpenFont(&MenuFont);

	/* read the ParMOpt environment first, so it can be overriden by CLI/WB arguments */
	if (Getenv("ParMOpt", ParMOptString, 255)) {
		ParMOptArgV = ArpAlloc(sizeof(APTR) * (NUM_ARGS + 1));
		ParMOptArgC = (int) GADS(ParMOptString, strlen(ParMOptString), HelpUsage, ParMOptArgV, Template);
		if (ParMOptArgC < 0) {
			SimpleRequest(ParMConfig.ReqTitle, "Error in ParMOpt: %s", ParMOptArgV[0]);
			Bye(NO_WIN);
		}
		else {
			for (i = 0; i < NUM_ARGS; i++) {
				if (ParMOptArgV[i])
					parse_arg(i, ParMOptArgV[i]);
			}
		}
	}

	if (WBenchMsg) {	/* Tool Types parsing */
		if (dop = GetDiskObject(WBenchMsg->sm_ArgList->wa_Name)) {
			ParMConfig.DefaultStack = dop->do_StackSize;
			for (i = 0; i < NUM_ARGS-1; i++) {	/* NUM_ARGS-1 => don't parse USENULL */
				if (arg = FindToolType(dop->do_ToolTypes, ToolTypesArg[i]))
					parse_arg(i, arg);
			}
			FreeDiskObject(dop);
		}
		parse_arg(TTN_USENULL, "ON");	/* allways redirect output to NULL: if started from WorkBench */
		if (ParMOpt.ParMMode == MODE_CLI)
			ParMOpt.ParMMode = MODE_WINDOW;		/* Forbid CLI mode from WB */
	}
	else {			/* CLI parsing */
		cli = (struct CommandLineInterface *) ((long) ParMProcess->pr_CLI << 2);
		ParMConfig.DefaultStack = cli->cli_DefaultStack << 2;
		for (i = 0; i < NUM_ARGS; i++) {
			if (argv[i])
				parse_arg(i, argv[i]);
		}
	}
	/* SHOWMEM, SHOWTIME and AUTOFRONT allowed only with ParM's own window */
	if (ParMOpt.ParMMode != MODE_WINDOW)
		ParMOpt.Flags &= ~(PF_SHOWMEM|PF_SHOWTIME|PF_AUTOFRONT);
	switch (ParMOpt.ParMMode) {
	case MODE_WINDOW:
		/* Bug in 2.0. Screen->BarHeight is one pixel less than true height */
		NWS.Height = WBS->BarHeight + (OSVersion >= 36);

		/* calculate the real lenght of string ParM (font can be proportional in 2.0) */
		/* if the font is proportional tf_YSize chould contain max char size */
		if ((ParMOpt.Flags & PF_SHOWMEM) && (ParMOpt.Flags & PF_SHOWTIME))
			i = 29;		/* 29 = strlen("Chip:0000 Fast:00000 00:00:00") */
		else if (ParMOpt.Flags & PF_SHOWTIME)
			i = 8;		/* 8 = strlen("00:00:00") */
		else if (ParMOpt.Flags & PF_SHOWMEM)
			i = 20;		/* 20 = strlen("Chip:0000 Fast:00000") */
		else {
			i = 4;		/* strlen("ParM") */
			NWS.Width = (OSVersion >= 36) ? 10 : 2;
			if (NWS.Flags & WINDOWDEPTH)
				NWS.Width += 51;	/* Depth gadget stay hidden when show Mem/Time */
		}
		NWS.Width += Font->tf_XSize * i + 6;	

		if ((Timer_Port = CreatePort(0, 0L))) {
			if (!(TimerOpen = OpenDevice(TIMERNAME, UNIT_VBLANK, (struct IORequest *) & Time_Req, 0L))) {
				Time_Req.tr_node.io_Message.mn_ReplyPort = Timer_Port;
				Time_Req.tr_node.io_Command = TR_ADDREQUEST;
				Time_Req.tr_node.io_Flags = 0;
				Time_Req.tr_node.io_Error = 0;
				Time_Req.tr_time.tv_secs = 0;
				Time_Req.tr_time.tv_micro = BASE_TIME * ParMOpt.RefreshTime;
			}
		}
		if (!(Win = OpenWindow(&NWS)))
			Bye(NO_WIN);
		SetWindowTitles(Win, "ParM", COPYRIGHT);
		SetFont(Win->RPort, Font);
		break;
	case MODE_CLI:
		Win = GetWindow();
		if (Win->UserPort) {
			if (Win->IDCMPFlags & MENUPICK) {
				SimpleRequest(ParMConfig.ReqTitle, "This window already have menus.");
				Bye(NO_WIN);
			}
			else {
				PortTaken = TRUE;
				OldIDCMP = Win->IDCMPFlags;
			}
		}
		ModifyIDCMP(Win, Win->IDCMPFlags | MENUPICK | CLOSEWINDOW);
		break;
	case MODE_MYMENU:
		if (Win = FindWorkBench()) {
			if (LastWBMenu->NextMenu) {
				SimpleRequest(ParMConfig.ReqTitle, "Menus already installed in WorkBench.");
				Bye(NO_WIN);
			}
			else
				PortTaken = TRUE;
		}
		else {
			SimpleRequest(ParMConfig.ReqTitle, "Can't find WorkBench.");
			Bye(NO_WIN);
		}
	}
	ParMConfig.Win = Win;
	if (PortTaken)
		TakeMsgPort();
	if (ParMOpt.ParMenuColor < 0)
		ParMOpt.ParMenuColor = Win->DetailPen;
	CreateParMenu(ParMOpt.ParMenuColor);
	UpDateMenus();
	SetBPen(Win->RPort, ParMOpt.MTBlockPen);
	SetDrMd(Win->RPort, JAM2);
	RefreshInfoString();
	if (ParMOpt.Flags & PF_USENULL) {
		/* prevent request "Please insert volume NULL:" if not mounted */
		WindowPtr = ParMProcess->pr_WindowPtr;
		ParMProcess->pr_WindowPtr = (APTR)-1L;
		if ((fh = Open("NULL:", MODE_NEWFILE)) || (fh = Open("NIL:", MODE_NEWFILE))) {
			ParMProcess->pr_CIS = fh;
			ParMProcess->pr_COS = fh;
			ParMProcess->pr_ConsoleTask = (APTR)((struct FileHandle *)(fh << 2))->fh_Type;
		}
		ParMProcess->pr_WindowPtr = WindowPtr;
	}

	/* Monitor Menu Events */

	WorkPort = (PortTaken) ? &SwapPort : Win->UserPort;
	IntuiSignal = 1L << WorkPort->mp_SigBit;
	if (Timer_Port) {
		TimerSignal = 1L << Timer_Port->mp_SigBit;
		SendIO((struct IORequest *) & Time_Req.tr_node);
	}

	while (!quit) {
		Signal = Wait(IntuiSignal | TimerSignal);
		if (Signal & IntuiSignal) {
			while (IMsg = (struct IntuiMessage *) GetMsg(WorkPort)) {
				Class = IMsg->Class;
				Code = IMsg->Code;
				Qual = IMsg->Qualifier;
				/* Use raw keys to prevent ALT and CTRL convertions. Allow only Shift qualifiers */
				if (Class == RAWKEY && !(Code & IECODE_UP_PREFIX) && (Qual & ParMConfig.ShortCutQual) && !(Qual & IEQUALIFIER_REPEAT)) {
					Code = RawKeyToAscii(Code, Qual & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT|IEQUALIFIER_RELATIVEMOUSE), IMsg->IAddress);
					if (Code > 32 && Code < 127) {
						Code = MakeMenuShortCut(&Menu1, Code);
						Class = MENUPICK;
					}
				}
				/*
				 *	PutMsg() conditions (Messages given to port monitored):
				 *	MODE_WINDOW:
				 *		No such port, so all messages replied.
				 *	MODE_MYMENU:
				 *		give all messages but ParM's menus MENUPICK and RAWKEYs converted
				 *		to MENUPICK. handle NEWPREFS after PutMsg().
				 *		For OS2.0, disable ResetWB (1st item of Tools menu).
				 *	MODE_CLI:
				 *		if PortTaken, all but MENUPICK and CLOSEWINDOW.
				 */
				if (!PortTaken
				    || (Class == MENUPICK && (MENUNUM(Code) >= FirstParMMenuNum || OSVersion >= 36 && MENUNUM(Code) == 3 && ITEMNUM(Code) == 0))
				    || (ParMOpt.ParMMode == MODE_CLI && Class == CLOSEWINDOW))
				{
					if (Class == 0x0FL)	/* Msg sent bye 'ParMCD' command */
						ChangeDir((char *) IMsg->IAddress);
					ReplyMsg((struct Message *) IMsg);
				}
				else {
					PutMsg(&SwapPort, (struct Message *) IMsg);
					if (Class == NEWPREFS) {
						while (LastWBMenu->NextMenu)
							Delay(10);
						InstallWBMenus(&Menu1);
					}
					continue;	/* Don't handle these messages */
				}
				switch (Class) {
				case ACTIVEWINDOW:
				case INACTIVEWINDOW:
					if (ParMOpt.ParMMode == MODE_WINDOW && (ParMOpt.Flags & (PF_SHOWMEM|PF_SHOWTIME)))
						DoSetRast = TRUE;
					break;
				case CLOSEWINDOW:
					quit = TRUE;
					break;
				case MENUPICK:
					DoNextSelect = TRUE;
					if (Code != MENUNULL) {
						Busy(TRUE);
						do {
							/* Prevent extended selection of workbench menus */
							if (MENUNUM(Code) == FirstParMMenuNum) {
								switch (ITEMNUM(Code)) {
								case OPEN_ITEM:
									OpenMenus();
									DoNextSelect = FALSE;
									break;
								case UPDATE_ITEM:
									UpDateMenus();
									DoNextSelect = FALSE;
									break;
								case STD_CFG_ITEM:
									strcpy(ParMConfig.CurCfg, DEFAULT_CONFIG_FILE);
									UpDateMenus();
									DoNextSelect = FALSE;
									break;
								case CMDMODE_ITEM:
									ParMConfig.SimpleCmdMode = SubItem1.Flags & CHECKED;
									break;
								case COMMAND_ITEM:
									Command(&ParMConfig);
									break;
								case CHDIR_ITEM:
									PathName(ParMProcess->pr_CurrentDir, Dir, REQ_DSIZE);
									FileReq.Flags = FRQDIRONLYM;
									FileReq.Title = "Enter New Dir...";
									if (FileRequester(&FileReq))
										ChangeDir(Dir);
									FileReq.Flags = 0;
									break;
								case QUIT_ITEM:
									quit = TRUE;
									DoNextSelect = FALSE;
								}
							}
							else if (MENUNUM(Code) > FirstParMMenuNum)	/* custom menus */
								DoExtMenu(Code);
						} while (DoNextSelect && (Code = ItemAddress(TheMenu, Code)->NextSelect) != MENUNULL);
						Busy(FALSE);
					}
					break;
				}
			}
		}
		if (Signal & TimerSignal) {
			if (ParMOpt.ParMMode == MODE_WINDOW && (ParMOpt.Flags & PF_AUTOFRONT) && IsHidden(Win))
				WindowToFront(Win);
			Time_Req.tr_time.tv_secs = 0;
			Time_Req.tr_time.tv_micro = BASE_TIME * ParMOpt.RefreshTime;
			SendIO((struct IORequest *) & Time_Req.tr_node);
		}
		if (ParMOpt.ParMMode == MODE_WINDOW)
			RefreshInfoString();
		/* See if some wb messages have been replied, and free them */
		FreeRepliedWBMessages();
	}

	if (!TimerOpen) {
		WaitIO((struct IORequest *)&Time_Req.tr_node);	/* wait for the last timer msg before exit */
		CloseDevice((struct IORequest *)&Time_Req);
	}
	if (Timer_Port)
		DeletePort(Timer_Port);

	Bye(THE_END);
}


