/*

 CloseWB.c - Simple hot key commodity compiled with SASC 6.55

 This piece of code is Copyright © 1995 by Stefano Reksten of 3AM
 - The Three Amigos!!!

*/

#include <dos/dos.h>
#include <exec/memory.h>
#include <exec/libraries.h>
#include <exec/execbase.h>
#include <devices/input.h>
#include <devices/inputevent.h>
#include <graphics/gfxbase.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <intuition/screens.h>
#include <libraries/commodities.h>
#include <libraries/gadtools.h>
#include <libraries/locale.h>
#include <workbench/startup.h>
#include <string.h>

#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/commodities.h>
#include <proto/dos.h>
#include <proto/icon.h>
#include <proto/gadtools.h>
#include <proto/graphics.h>
#include <proto/locale.h>
#include <clib/alib_protos.h>
#include <clib/alib_stdio_protos.h>

#define EVT_HOTKEY 1L
#define EVT_POPKEY 2L

#define CATCOMP_NUMBERS
#define CATCOMP_BLOCK
#define CATCOMP_CODE
#include "CloseWB_strings.h"

#define VERSION "1.9"

char *ver = "$VER: CloseWB "VERSION" "__AMIGADATE__;

extern STRPTR __asm GetString(register __a0 struct LocaleInfo *li,
					register __d0 LONG stringNum);
struct LocaleInfo li;
char *str_windows, *str_shells, *str_flush, *str_hide, *str_quit, *str_lock, *str_wbench,
	 *key_windows, *key_shells, *key_flush, *key_hide, *key_quit, *key_lock;

void ProcessMsg(void);
struct Screen *FindWB(void);
void CloseWindowsOrShells(void);
void SendWindowcloseTo(struct Window *);
void SendCtrlBackslashTo(struct Window *);
void LockWorkbench( void );
void UnlockWorkbench( void );
BOOL ProcessCommodityMsg(void);
BOOL ProcessWindowMsg(void);
BOOL OpenWBWindow(void);
void CloseWBWindow(void);

extern void FlushAll(void);
extern void __asm EmptyMsgPort( register __a0 struct MsgPort * );

struct EasyStruct warn_rq = { sizeof(struct EasyStruct), NULL, NULL, NULL, NULL };

extern struct ExecBase *SysBase;
struct IntuitionBase *IntuitionBase;
struct Library *CxBase, *GadToolsBase, *IconBase, *LocaleBase;
struct Window *wbwindow;
UWORD wleft = (UWORD)~0, wtop = (UWORD)~0;
struct MsgPort *broker_mp;
CxObj *broker;

struct NewBroker newbroker = {
	NB_VERSION,
	"CloseWB",
	"CloseWB "VERSION" © 1995 by Stefano Reksten",
	NULL,
	NBU_UNIQUE | NBU_NOTIFY,
	COF_SHOW_HIDE, 0, 0, 0 };

ULONG cxsigflag;
ULONG close_windows = TRUE, close_shells = TRUE, close_wb = TRUE, flush_all = TRUE, lock_wb = FALSE;

STRPTR underscore_wbenchstr = "_Workbench", wbenchstr;

#define CWND_ID 1
#define CSHL_ID 2
#define CWBS_ID 3
#define LOCK_ID 4
#define FLSH_ID 5
#define HIDE_ID 6
#define QUIT_ID 7


struct Gadget *glist = NULL;

void __main( void )
{
CxObj *hotkeyobj, *popkeyobj;
BOOL popup;

wbenchstr = underscore_wbenchstr + 1;

li.li_Catalog = NULL;
if ( LocaleBase = OpenLibrary( "locale.library", 38L ) )
	{
	li.li_LocaleBase = LocaleBase;
	li.li_Catalog = OpenCatalogA( NULL, "CloseWB.catalog", NULL );

	warn_rq.es_Title = GetString( &li, MSG_CLOSEWBREQUESTTITLE );
	warn_rq.es_TextFormat = GetString( &li, MSG_PLEASECLOSEALL );
	warn_rq.es_GadgetFormat = GetString( &li, MSG_REQUESTGADGETS );

	newbroker.nb_Descr = GetString( &li, MSG_NEWBROKER );

	str_windows = GetString( &li, MSG_WINDOWS );
	key_windows = GetString( &li, MSG_WINDOWS_KEY );
	str_shells = GetString( &li, MSG_SHELLS );
	key_shells = GetString( &li, MSG_SHELLS_KEY );
	str_flush = GetString( &li, MSG_FLUSH );
	key_flush = GetString( &li, MSG_FLUSH_KEY );
	str_lock = GetString( &li, MSG_LOCK );
	key_lock = GetString( &li, MSG_LOCK_KEY );
	str_hide = GetString( &li, MSG_HIDE );
	key_hide = GetString( &li, MSG_HIDE_KEY );
	str_quit = GetString( &li, MSG_QUIT );
	key_quit = GetString( &li, MSG_QUIT_KEY );
	str_wbench = underscore_wbenchstr;
	}
else
	{
	key_windows = str_windows = "Windows";
	key_shells = str_shells = "Shells";
	key_flush = str_flush = "Flush";
	key_lock = str_lock = "Lock";
	key_hide = str_hide = "Hide";
	key_quit = str_quit = "Quit";
	str_wbench = wbenchstr;
	}

if (IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 37L))
	{
	if (CxBase = OpenLibrary("commodities.library", 37L))
		{
		if (broker_mp = CreateMsgPort())
			{
			newbroker.nb_Port = broker_mp;
			cxsigflag = 1L << broker_mp->mp_SigBit;

			if (IconBase = OpenLibrary("icon.library",37))
				{
				char **ttypes;
				struct DiskObject *ob;

				if ( ob = GetDiskObject( "CloseWB" ) )
					{
					ttypes = ob->do_ToolTypes;

					newbroker.nb_Pri = (BYTE)ArgInt(ttypes, "CX_PRIORITY", 0);

					close_windows = FindToolType(ttypes, "CLOSEWINDOWS") ? TRUE : FALSE;
					close_shells = FindToolType(ttypes, "CLOSESHELLS") ? TRUE : FALSE;
					close_wb = FindToolType(ttypes, "CLOSEWORKBENCH") ? TRUE : FALSE;
					lock_wb = FindToolType(ttypes, "LOCKWORKBENCH") ? TRUE : FALSE;
					flush_all = FindToolType(ttypes, "FLUSH") ? TRUE : FALSE;

					popup = MatchToolValue(FindToolType(ttypes, "CX_POPUP"), "YES");

					hotkeyobj = HotKey( ArgString(ttypes, "CX_HOTKEY", "lalt w"), broker_mp, EVT_HOTKEY );
					popkeyobj = HotKey( ArgString(ttypes, "CX_POPKEY", "ctrl lalt w" ), broker_mp, EVT_POPKEY );

					wleft = (UWORD)ArgInt(ttypes,"WLEFT", ~0);
					wtop = (UWORD)ArgInt(ttypes,"WTOP", ~0);

					if ( lock_wb )
						LockWorkbench();

					FreeDiskObject( ob );
					}
				CloseLibrary(IconBase);
				}

			if ( broker = CxBroker(&newbroker, NULL) )
				{
				AttachCxObj( broker, hotkeyobj );
				AttachCxObj( broker, popkeyobj );
				if ( !CxObjError( broker ) )
					{
					ActivateCxObj(broker, 1L);
					if (popup)
						OpenWBWindow();
					ProcessMsg();
					}
				DeleteCxObjAll(broker);
				}
			else
				{
				DeleteCxObj( hotkeyobj );
				DeleteCxObj( popkeyobj );
				}
			EmptyMsgPort( broker_mp );
			DeletePort( broker_mp );
			}
		CloseLibrary( CxBase );
		}
	CloseWBWindow();
	CloseLibrary( (struct Library *)IntuitionBase );
	}

if ( LocaleBase )
	{
	if ( li.li_Catalog )
		 CloseCatalog( li.li_Catalog );
	CloseLibrary( LocaleBase );
	}
}


void ProcessMsg(void)
{
BOOL stay = TRUE;
ULONG sigrcvd, waitmask;

while(stay)
	{
	waitmask = cxsigflag | SIGBREAKF_CTRL_C;
	if (wbwindow) waitmask |= 1L<<wbwindow->UserPort->mp_SigBit;
	sigrcvd = Wait(waitmask);

	if (sigrcvd & SIGBREAKF_CTRL_C)
		stay = FALSE;
	else
	if (sigrcvd & cxsigflag)
		stay = ProcessCommodityMsg();
	else
		stay = ProcessWindowMsg();
	}
}


struct Screen *FindWB(void)
{
register struct Screen *scr = IntuitionBase->FirstScreen;

while(scr && (!(scr->DefaultTitle) || strcmp(scr->DefaultTitle, "Workbench Screen")))
	scr = scr->NextScreen;
return(scr);
}


void CloseWindowsOrShells()
{
register struct Window *window, *next;
register struct Screen *wbs = FindWB();

if (close_windows && wbwindow && wbwindow->WScreen == wbs)
	CloseWBWindow();

if (close_windows)
	{
	window = wbs->FirstWindow;
	while(window)
		{
		next = window->NextWindow;
		if (window->UserPort)
			{
			if (window->Title && strcmp(window->Title, wbenchstr))
				SendWindowcloseTo(window);
			}
		window = next;
		}
	}

if (close_shells)
	{
	window = wbs->FirstWindow;
	while(window)
		{
		next = window->NextWindow;

		ActivateWindow(window);
		SendCtrlBackslashTo(window);
		Delay( 5 );

		window = next;
		}
	}
}


void SendWindowcloseTo(struct Window *window)
{
register struct IntuiMessage *imsg;

if (imsg = (struct IntuiMessage *)AllocVec(sizeof(struct IntuiMessage), MEMF_ANY|MEMF_CLEAR))
	{
	imsg->ExecMessage.mn_ReplyPort = broker_mp;
	imsg->ExecMessage.mn_Length = sizeof(struct IntuiMessage);
	imsg->Class = IDCMP_CLOSEWINDOW;
	imsg->ExecMessage.mn_Node.ln_Type = NT_MESSAGE;
	imsg->IDCMPWindow = window;
	PutMsg(window->UserPort, (struct Message *)imsg);
	WaitPort(broker_mp);
	GetMsg(broker_mp);
	FreeVec(imsg);
	}
}


void SendCtrlBackslashTo(struct Window *window)
{
register struct InputEvent *fakeev;
register struct IOStdReq *ioreq;

if (fakeev = AllocVec(sizeof(struct InputEvent),MEMF_PUBLIC|MEMF_CLEAR))
	{
	if (ioreq = CreateIORequest(broker_mp,sizeof(struct IOStdReq)))
		{
		if ( !OpenDevice("input.device",NULL,(struct IORequest *)ioreq,NULL))
			{
			fakeev->ie_Class = IECLASS_RAWKEY;
			fakeev->ie_Code = 0x0D;
			fakeev->ie_Qualifier = IEQUALIFIER_CONTROL;

			ioreq->io_Data = (APTR)fakeev;
			ioreq->io_Length = sizeof(struct InputEvent);
			ioreq->io_Command = IND_WRITEEVENT;

			DoIO((struct IORequest *)ioreq);
			CloseDevice((struct IORequest *)ioreq);
			}
		DeleteIORequest(ioreq);
		}
	FreeVec(fakeev);
	}
}


struct Screen *WBS = NULL;

void LockWorkbench( void )
{
if ( lock_wb && !WBS )
	WBS = LockPubScreen( wbenchstr );
}


void UnlockWorkbench( void )
{
if ( WBS )
	{
	UnlockPubScreen( wbenchstr, WBS );
	WBS = NULL;
	}
}


struct IntuiText it = { 0, 0, 0, 0, 0, NULL, NULL, NULL };
struct NewGadget ng = { 0, 0, 50, 0, NULL, NULL, 0, 0, NULL, NULL };
struct VisualInfo *vi;
struct Gadget *gad;
UWORD textw;
struct Gadget *windows_gad, *shells_gad, *workbench_gad, *flush_gad, *lock_gad;

/* Some other routines to make it shorter */

UWORD MaxBetween( STRPTR text, UWORD prev )
{
register UWORD ilength, max;

it.IText = text;
ilength = IntuiTextLength( &it );
max = ( ilength > prev ? ilength : prev );
return max;
}

struct Gadget *AddCheckboxGadget( STRPTR name, ULONG ID, BOOL selected )
{
ng.ng_TopEdge += ng.ng_Height + 1;
ng.ng_GadgetText = name;
ng.ng_GadgetID = ID;
gad = CreateGadget(CHECKBOX_KIND,gad,&ng,GTCB_Checked,selected,GTCB_Scaled,TRUE,GT_Underscore,'_',TAG_END);
textw = MaxBetween( name, textw );
return gad;
}


BOOL OpenWBWindow(void)
{
struct Screen *wbs = LockPubScreen(NULL);
register UWORD fonth, wheight, wwidth;

if (!wbwindow)
	{
	fonth = wbs->Font->ta_YSize;
	if ( fonth < 12 )
		fonth = 12;
	it.ITextFont = wbs->Font;
	textw = 0;
	wheight = wbs->BarHeight + fonth * 6 + 5 + wbs->WBorBottom;
	if (GadToolsBase = OpenLibrary("gadtools.library",37L))
		{
		if (vi = GetVisualInfo(wbs,NULL))
			{
			ng.ng_VisualInfo = vi;
			ng.ng_LeftEdge = wbs->WBorLeft + 4;
			ng.ng_TopEdge = wbs->BarHeight;
			ng.ng_TextAttr = wbs->Font;

			gad = CreateContext(&glist);
			if (gad)
				{
				ng.ng_TopEdge -= fonth - 2;
				ng.ng_Flags = PLACETEXT_RIGHT;
				ng.ng_Height = fonth - 1;
				ng.ng_Width = 26;

				windows_gad = AddCheckboxGadget( str_windows, CWND_ID, close_windows );
				shells_gad = AddCheckboxGadget( str_shells, CSHL_ID, close_shells );
				workbench_gad = AddCheckboxGadget( str_wbench, CWBS_ID, close_wb );
				lock_gad = AddCheckboxGadget( str_lock, LOCK_ID, lock_wb );
				flush_gad = AddCheckboxGadget( str_flush, FLSH_ID, flush_all );

				wwidth = textw + wbs->WBorLeft + wbs->WBorRight + 48;

				textw = MaxBetween( str_hide, MaxBetween( str_quit, 0 ) );
				if ( ( textw * 2 + wbs->WBorLeft + wbs->WBorRight + 48 ) > wwidth )
				wwidth = ( textw * 2 + wbs->WBorLeft + wbs->WBorRight + 48 );
				textw = ( wwidth - wbs->WBorLeft - wbs->WBorRight - 10) >> 1;

				ng.ng_Height = fonth + 2;
				ng.ng_TopEdge += fonth;
				ng.ng_Width = textw;
				ng.ng_Flags = PLACETEXT_IN;
				ng.ng_GadgetText = str_hide;
				ng.ng_GadgetID = HIDE_ID;
				gad = CreateGadget(BUTTON_KIND,gad,&ng,GT_Underscore,'_',TAG_END);

				ng.ng_LeftEdge += ng.ng_Width + 2;
				ng.ng_GadgetText = str_quit;
				ng.ng_GadgetID = QUIT_ID;
				gad = CreateGadget(BUTTON_KIND,gad,&ng,GT_Underscore,'_',TAG_END);

				if ( wtop == ~0 )
					wtop = (wbs->Height - wheight)>>1;
				if ( wleft == ~0 )
					wleft = (wbs->Width - wwidth)>>1;

				if (gad && (wbwindow = OpenWindowTags( NULL,
					WA_Left, wleft,
					WA_Top, wtop,
					WA_Width, wwidth,
					WA_Height, wheight,
					WA_Title, "CloseWB",
					WA_ScreenTitle, "CloseWB "VERSION" © 1995 by Stefano Reksten of 3AM",
					WA_Flags, WFLG_CLOSEGADGET|WFLG_DEPTHGADGET|WFLG_DRAGBAR|WFLG_ACTIVATE|WFLG_RMBTRAP,
					WA_Gadgets, glist,
					WA_IDCMP, IDCMP_CLOSEWINDOW|IDCMP_GADGETDOWN|IDCMP_VANILLAKEY|IDCMP_REFRESHWINDOW|BUTTONIDCMP,
					TAG_END )))
					{
					ScreenToFront(wbs);
					UnlockPubScreen(NULL,wbs);
					return TRUE;
					}
				}
			FreeVisualInfo(vi);
			}
		CloseLibrary(GadToolsBase);
		}
	}

UnlockPubScreen(NULL,wbs);
return FALSE;
}


BOOL ProcessCommodityMsg(void)
{
CxMsg *msg;
ULONG msgid, msgtype;
BOOL cont, returnvalue = TRUE;

while(msg = (CxMsg *)GetMsg(broker_mp))
	{
	msgid = CxMsgID(msg);
	msgtype = CxMsgType(msg);
	ReplyMsg((struct Message *)msg);

	switch(msgtype)
		{
		case CXM_IEVENT:
			switch(msgid)
				{
				case EVT_HOTKEY:
					if (FindWB())
						{
						if (close_windows || close_shells)
							CloseWindowsOrShells();
						cont = TRUE;
						if ( lock_wb )
							UnlockWorkbench();
						while (cont && close_wb)
								{
								cont = !CloseWorkBench();
								if (cont)
									{
									DisplayBeep(NULL);
									cont = EasyRequestArgs(NULL, &warn_rq, NULL, NULL);
									if (!cont && lock_wb)
										LockWorkbench();
									}
								}
						if (flush_all)
							FlushAll();
						}
					else
						{
						OpenWorkBench();
						if ( lock_wb )
							LockWorkbench();
						}
					break;
				case EVT_POPKEY:
					OpenWBWindow();
				default:
					break;
				}
			break;
		case CXM_COMMAND:
			switch(msgid)
				{
				case CXCMD_DISABLE:
					ActivateCxObj(broker, 0L);
					break;
				case CXCMD_ENABLE:
					ActivateCxObj(broker, 1L);
					break;
				case CXCMD_KILL:
					returnvalue = FALSE;
					break;
				case CXCMD_UNIQUE:
					OpenWBWindow();
					break;
				case CXCMD_APPEAR:
					OpenWBWindow();
					break;
				case CXCMD_DISAPPEAR:
					CloseWBWindow();
				default:
					break;
				}
			break;
		default:
			break;
		}
	}
return returnvalue;
}


BOOL ProcessWindowMsg(void)
{
register struct IntuiMessage *msg;
register BOOL returnvalue = TRUE;
register ULONG class;
register UWORD msgID;

while(wbwindow && (msg = GT_GetIMsg(wbwindow->UserPort)))
	{
	class = msg->Class;
	if (class & (BUTTONIDCMP|IDCMP_GADGETDOWN))
		msgID = ((struct Gadget *)msg->IAddress)->GadgetID;
	if (class == IDCMP_VANILLAKEY)
		msgID = msg->Code;
	GT_ReplyIMsg(msg);

	switch(class)
		{
		case IDCMP_REFRESHWINDOW:
			GT_BeginRefresh(wbwindow);
			GT_EndRefresh(wbwindow,TRUE);
			break;
		case IDCMP_CLOSEWINDOW:
			CloseWBWindow();
			break;
		case BUTTONIDCMP:
		case IDCMP_GADGETDOWN:
			if ( msgID == CWND_ID ) close_windows ^= TRUE;
			if ( msgID == CSHL_ID ) close_shells ^= TRUE;
			if ( msgID == CWBS_ID ) close_wb ^= TRUE;
			if ( msgID == LOCK_ID ) { lock_wb ^= TRUE; if ( lock_wb ) LockWorkbench(); else UnlockWorkbench(); }
			if ( msgID == FLSH_ID ) flush_all ^= TRUE;
			if ( msgID == HIDE_ID ) CloseWBWindow();
			if ( msgID == QUIT_ID ) returnvalue = FALSE;
			break;
		case IDCMP_VANILLAKEY:
			msgID ^= 32; /* To upper */
			if ( msgID == *key_windows ) { close_windows ^= TRUE; GT_SetGadgetAttrs( windows_gad, wbwindow, NULL, GTCB_Checked, close_windows, TAG_END ); 	}
			if ( msgID == *key_shells )  { close_shells ^= TRUE; GT_SetGadgetAttrs( shells_gad, wbwindow, NULL, GTCB_Checked, close_shells, TAG_END ); 	}
			if ( msgID == 'W' )          { close_wb ^= TRUE; GT_SetGadgetAttrs( workbench_gad, wbwindow, NULL, GTCB_Checked, close_wb, TAG_END ); 	}
			if ( msgID == *key_lock )    { lock_wb ^= TRUE; if ( lock_wb ) LockWorkbench(); else UnlockWorkbench(); GT_SetGadgetAttrs( lock_gad, wbwindow, NULL, GTCB_Checked, lock_wb, TAG_END ); 	}
			if ( msgID == *key_flush )   { flush_all ^= TRUE; GT_SetGadgetAttrs( flush_gad, wbwindow, NULL, GTCB_Checked, flush_all, TAG_END ); 	}
			if ( msgID == *key_hide )    CloseWBWindow();
			if ( msgID == *key_quit )    returnvalue = FALSE;
		}
	}
return returnvalue;
}


void CloseWBWindow(void)
{
if(wbwindow)
	{
	wleft = wbwindow->LeftEdge;
	wtop = wbwindow->TopEdge;
	EmptyMsgPort( wbwindow->UserPort );
	CloseWindow(wbwindow);
	FreeGadgets(glist);
	FreeVisualInfo(vi);
	CloseLibrary(GadToolsBase);
	wbwindow = NULL;
	}
}
