/*  Altabber, n. He who performs an `Alt-Tab' action.
**
**  Version 1.2 (10.9.98) İ Mfc.
**
**  Please edit the following lines to fit your preferences.
**  Then recompile with DICE:
**
**	dcc -mi -mRR -ms -pi Altabber.c
*/

#define CX_PRI	    -2

#define KEYTAB	    "LAmiga Tab"

//#define QUA	 0x64	 /* LAlt */
//#define QUA	 0x65	 /* RAlt */
#define QUA	0x66	/* LAmiga */
//#define QUA	 0x67	 /* RAmiga */


#include <stdlib.h>
#include <string.h>
#include <clib/dos_protos.h>
#include <clib/commodities_protos.h>
#include <clib/alib_protos.h>
#include <clib/exec_protos.h>
#include <clib/graphics_protos.h>
#include <clib/intuition_protos.h>
#include <intuition/intuitionbase.h>

#define TAB 1
#define SPE 6
#define WIN_PERCENT    10
#define INNER_PERCENT  10

#define JAM1 0
#define JAM2 1

#define KEYDUMMY    "Ctrl LAmiga RAmiga A"
#define BUFFERLEN	256

/*
**  Prototypes:
*/

CxObj *AttachFilter(STRPTR, ULONG);
void handle(void);
void openit(void);
void closeit(void);
void nextit(void);
int ShowError(STRPTR);

/*
**  Globals:
*/

static STRPTR version = "$VER: Altabber 1.2 (10.9.98)";

extern struct IntuitionBase *IntuitionBase;

CxObj		*broker, *special;
struct MsgPort	*mp;
ULONG		flag, attn = 0;
struct Window	*win = NULL;
struct TextFont *font = NULL;
int fonth, fontbl;
int innertop, innerleft, innerwide;


struct Window	*w, *aw;
struct Screen	*as;

const IX specialix =
{
    IX_VERSION,
    IECLASS_RAWKEY,		/* class */
    IECODE_UP_PREFIX | QUA,	/* code bits = upstroke + QUA */
    0xffff,			/* code must match exactly */
    0, 0,			/* we don't care qualifiers */
    0
};






#ifdef _DCC
__stkargs
#endif
void _main()
{
    int error = 0;

    if (mp=CreateMsgPort())
    {
	struct NewBroker nb = {
	    NB_VERSION,
	    "Altabber",
	    "Altabber 1.2  İ1996-98 Mfc.",
	    "Windoze-like Alt-Tab function",
	    NBU_UNIQUE | NBU_NOTIFY,
	    0, CX_PRI, 0, 0
	};

	nb.nb_Port = mp;
	flag = 1L << mp->mp_SigBit;

	if (broker=CxBroker(&nb, &error))
	{
	    if (AttachFilter(KEYTAB, TAB) &&
		(special=AttachFilter(KEYDUMMY, SPE)))
	    {
		SetFilterIX(special, &specialix);
		ActivateCxObj(broker, 1);
		handle();
	    }
	    else
		error = ShowError("couldn't create filters.");

	    DeleteCxObjAll(broker);

	    struct Message *msg;
	    while (msg=GetMsg(mp))
		ReplyMsg(msg);
	}
	else
	{
	    if (error == CBERR_DUP)
		error = 0;
	    else
		error = ShowError("couldn't create broker.");
	}

	DeleteMsgPort(mp);
    }
    else
	error = ShowError("couldn't create message port.");

    _exit(error);
}


/*
**  AttachFilter
**  ŻŻŻŻŻŻŻŻŻŻŻŻ
*/

CxObj *AttachFilter(STRPTR key, ULONG event)
{
    CxObj *filter, *sender, *translator;

    if (filter=CxFilter(key))
    {
	AttachCxObj(broker, filter);

	if (sender=CxSender(mp, event))
	{
	    AttachCxObj(filter, sender);

	    if (translator=CxTranslate(NULL))
	    {
		AttachCxObj(filter, translator);

		if (!CxObjError(filter))
		    return filter;
	    }
	}
    }

    return NULL;
}

/*
**  handle
**  ŻŻŻŻŻŻ
*/

void handle()
{
    int error = 0;
    int count = 0;

    while (!error)
    {
	CxMsg *msg;
	ULONG signal = Wait(SIGBREAKF_CTRL_C | flag);

	while (msg = (CxMsg*) GetMsg(mp))
	{
	    ULONG id = CxMsgID(msg);
	    ULONG type = CxMsgType(msg);
	    ReplyMsg((struct Message *) msg);

	    switch (type)
	    {
		case CXM_IEVENT:

		    if (id == TAB)
		    {
			if (!win)
			{
			    attn = 1;
			    as = IntuitionBase->ActiveScreen;
			    aw = w = IntuitionBase->ActiveWindow;
			}

			nextit();
		    }

		    if (id == SPE)
		    {
			if (win)    closeit();
		    }

		    break;

		case CXM_COMMAND:

		    switch (id)
		    {
			case CXCMD_DISABLE:
			    ActivateCxObj(broker, 0);
			    break;

			case CXCMD_ENABLE:
			    ActivateCxObj(broker, 1);
			    break;

			case CXCMD_KILL:
			case CXCMD_UNIQUE:
			    error = 1;
			    break;
		    }
		    break;
	    }
	}

	if (signal & SIGBREAKF_CTRL_C)
	    error = 1;
    }

    if (win) closeit();
}


/*
**  openit
**  ŻŻŻŻŻŻ
*/

void openit()
{
    int winw  = as->Width * (50-WIN_PERCENT)/50;
    fonth = as->Font->ta_YSize;
    innerleft = winw * INNER_PERCENT/100;
    innertop = 2 * fonth;
    innerwide = winw * (50-INNER_PERCENT)/50;

    if (win = OpenWindowTags(NULL,
	    WA_Left,	(ULONG) as->Width * WIN_PERCENT/100,
	    WA_Top,	(ULONG) (as->Height - 5*fonth)/2,
	    WA_Width,	(ULONG) winw,
	    WA_Height,	(ULONG) 5*fonth,
	    WA_CustomScreen,(ULONG) as,
	    TAG_END,	0))
    {
	if (font=OpenFont(as->Font))
	{
	    SetFont(win->RPort, font);
	    fontbl = font->tf_Baseline;
	}
	SetABPenDrMd(win->RPort, 1, 0, JAM1);
    }
}


/*
**  nextit
**  ŻŻŻŻŻŻ
*/

void nextit()
{
    static unsigned char buffer[BUFFERLEN];
    struct TextExtent te;
    int len = 0;
    int len1;
    STRPTR title;
    int avoidlock;

    if (w == aw)
	avoidlock = 1;
    else
	avoidlock = 0;

    while (len == 0)
    {
	w = w->NextWindow;
	if (w == NULL)
	    w = as->FirstWindow;
	if (w == aw)
	    ++avoidlock;

	if (title = w->Title)
	{
	    /* Strip off the trailing spaces */

	    unsigned char *p = title;
	    unsigned char *q = buffer;

	    while (*p)
		*(q++) = *(p++);

	    while (*(--p) == ' ')
		--q;

	    *q = '\0';
	    len = q - buffer;
	}

	if (avoidlock == 2)
	{
	    if (attn)
	    {
		/* There isn't any win with a title: don't open the box! */
		attn = 0;
		return;
	    }
	    else
	    {
		/* Something stange has happened: close all!  */
		closeit();
		return;
	    }
	}

    }

    if (attn)
    {
	openit();
	attn = 0;
    }

    if (win)
    {
	len1 = TextFit(win->RPort, buffer, len, &te, NULL, 1, innerwide, fonth);

	EraseRect(win->RPort, innerleft, innertop,
	    innerleft + innerwide, innertop + fonth);
	Move(win->RPort, innerleft + (innerwide - te.te_Width)/2, innertop + fontbl);
	Text(win->RPort, title, len1);
    }

}


/*
**  closeit
**  ŻŻŻŻŻŻŻ
*/

void closeit()
{
    if (font)
    {
	CloseFont(font);
	font = NULL;
    }

    if (win)
    {
	CloseWindow(win);
	win = NULL;
    }

    if (w != aw)
    {
	WindowToFront(w);
	ActivateWindow(w);
    }

}


/*
**  ShowError
**  ŻŻŻŻŻŻŻŻŻ
*/

int ShowError(STRPTR err)
{
    struct EasyStruct errmsg =
    {
	sizeof(struct EasyStruct),
	0,
	"Altabber",
	"An error has occurred:\n%s\nQuitting.",
	"Ok"
    };

    EasyRequest(NULL, &errmsg, NULL, err);

    return 10;
}


