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

/* Change this if you experience conflicts with other */
/* commodities (or MCP hotkeys): */
#define CX_PRI	    -2

#define COLOR2	    2	    /* Current win/scr */
#define COLOR1	    1	    /* Other win/scr */

/* If you change one of these, you should also change the */
/* QUA definition below. Please stick to a single qualifier. */
#define KEYWINTAB   "LAmiga Tab"
#define KEYSCRTAB   "LAmiga Shift Tab"

/* Uncomment this if you want complex handling, ie you can choose a screen */
/* with Amiga+Shift+Tab, then you can cycle through the windows */
/* of *THAT* screen with Amiga+Tab. In the normal (non-complex) mode */
/* you can only cycle through the windows of the active screen, no matter */
/* if you have chosen a different screen. WARNING: STILL IN BETA STATE !! */
//#define COMPLEX

/* This must match the definition of KEY(WIN|SCR)TAB above. Please note that */
/* that Shift+Tab and Alt+Tab have special meaning inside KingCON windows. */
//#define QUA	 0x63	 /* Ctrl */
//#define QUA	 0x64	 /* LAlt */
//#define QUA	 0x65	 /* RAlt */
#define QUA	0x66	/* LAmiga */
//#define QUA	 0x67	 /* RAmiga */



/*********** DO NOT EDIT BELOW THIS LINE ***********/

#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 WINTAB 1	/* Qualifier + Tab */
#define SCRTAB 2	/* Qualifier + Shift + Tab */
#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

#ifdef COMPLEX
#define FIRSTWIN    s->FirstWindow
#else
#define FIRSTWIN    as->FirstWindow
#endif

/*
**  Prototypes:
*/

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

/*
**  Globals:
*/

#define BROKERVERSION "Altabber 1.4  İ1998 Mfc."
static const STRPTR version = "$VER: Altabber 1.4 (11.10.98)";

extern struct IntuitionBase *IntuitionBase;

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

ULONG	flag;	    /* signals we wait for */
ULONG	attn = 0;   /* openit() requested */
int	mode;	    /* can be WINTAB or SCRTAB */

struct Window	*w;	/* window ptr */
struct Window	*aw;	/* active window */
struct Screen	*s;	/* screen ptr */
struct Screen	*as;	/* active screen */


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",
	    BROKERVERSION,
	    "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(KEYWINTAB, WINTAB) &&
		AttachFilter(KEYSCRTAB, SCRTAB) &&
		(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);

#ifdef _DCC
    /* Dummy reference to make Dice process the WBMsg */
    void _waitwbmsg(void);
    _waitwbmsg();
#endif
}




/*
**  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 == WINTAB || id == SCRTAB)
		    {
			if (!win)
			{
			    attn = 1;
			    as = s = IntuitionBase->ActiveScreen;
			    aw = w = IntuitionBase->ActiveWindow;
			}

#ifdef COMPLEX
			if ( mode==SCRTAB && id==WINTAB )
			    w = FIRSTWIN;
#endif

			mode = id;
			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
**  ŻŻŻŻŻŻ
**
**  mode must be WINTAB (cycle trough open windows) or SCRTAB (cycle through screens)
*/

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

    if (mode == WINTAB)
    {
	/* Cycle trough windows! */

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

	while (len == 0)
	{
	    /* Get the next window: */
	    w = w->NextWindow;
	    if (w == NULL)
		w = FIRSTWIN;

	    /* We don't want to loop forever: */
	    if (w == aw)
		++avoidlock;

	    /* Remember to skip windows without a title */
	    title = w->Title;
	    if (title)
		len = strlen(title);

	    if (avoidlock == 2)
	    {
		/* There are no windows with a non-void title */
		/* (except perhaps the active one) */
		/* so, do nothing at all! */

		attn = 0;
		return;
	    }
	}
    }
    else
    {
	/* Cycle through screens! */

	if (s == as)
	    avoidlock = 1;
	else
	    avoidlock = 0;

	/* No loop, because we do not skip any screen, even though */
	/* it has no name. */

	{
	    /* Get the next screen: */
	    s = s->NextScreen;
	    if (s == NULL)
		s = IntuitionBase->FirstScreen;

	    /* We don't want to loop forever: */
	    if (s == as)
		++avoidlock;

	    title = s->Title;
	    if (title == NULL)
		/* Many screens do not have a name, but they */
		/* should not be skipped */
		title = "<no title>";
	    len = strlen(title);

	    if (avoidlock == 2)
	    {
		/* There are no screens other than the active one */
		/* so, do nothing at all! */

		attn = 0;
		return;
	    }

	}
    }


    /* 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;
    }

    /* Open the window: */
    if (attn)
    {
	openit();
	attn = 0;
    }

    /* Print buffer into the window: */
    if (win)
    {
	static struct TextExtent te;
	int len1;

	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);

	/* Use COLOR2 if we are showing the active win/scr */
	if ( (mode==WINTAB && w==aw) ||
	     (mode==SCRTAB && s==as)
	   )
	    SetAPen(win->RPort, COLOR2);
	else
	    SetAPen(win->RPort, COLOR1);

	Text(win->RPort, title, len1);
    }

}





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

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

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

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

    if ( mode == SCRTAB  &&  s != as )
    {
	ScreenToFront(s);
    }

}





/*
**  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;
}


