/***************************************************************************

Source code for RExpand (which is Copyright ©1996 Peter Liljenberg)

Version 1.0
Author: Peter Liljenberg, 1996.

This code is fully Public Domain: you can use it in any way you want as
long as you don't violate the copyright of the _program_ RExpand. This
means that you can use the code for inspiration, for help or as an example;
and you can even cut code out and include it in your own programs, as long
as you don't make a program which, basicly, is RExpand and release it. You
can make modifications or add features to RExpand for your own use (and
maybe some friends' as well), but I'd be grateful if you would send me the
modified code so I can make a new version with this stuff added. You will
get due credit for the changes. In the same way, I expect to get a mention
in the docs if you make a program with any help of this code.

If this code in any way cause damage or loss of data, I'm not responsible;
you are.

***************************************************************************/

#include "defs.h"

struct Window *win;
struct VisualInfo *vi;
UWORD	left = 100,
		top = 50,
		width = 200,
		height = 100;
LONG	select,
		topitem,
		listitems,
		shownitems;
struct Gadget *listgad, *contextgad;
char titlebuf[128];

BOOL openwin(STRPTR word, STRPTR title, STRPTR sname);
BOOL drawwin(void);

struct Node *
handlereq(STRPTR word, STRPTR title, STRPTR screen)
{
	BOOL keepgoing = TRUE;
	ULONG lastsec = 0, lastmic = 0;
	int c;
	struct IntuiMessage *msg;
	struct Node *node = NULL;

	if (!openwin(word, title, screen)) {
		Rexxmsg->rm_Result1 = RETURN_ERROR;
		Rexxmsg->rm_Result2 = NULL;
		return NULL;
	}

	while (keepgoing) {
		WaitPort(win->UserPort);
		while (msg = GT_GetIMsg(win->UserPort)) {
			switch(msg->Class) {
			case IDCMP_GADGETUP:
				/* Return on double click on the same item */
				if (select == msg->Code && DoubleClick(lastsec, lastmic, msg->Seconds, msg->Micros)) {
					keepgoing = FALSE;
					/* Find node */
					for (c = 0, node = ReqList.lh_Head; c < msg->Code; c++, node = node->ln_Succ)
						;
				} else {
					lastsec = msg->Seconds;
					lastmic = msg->Micros;
					select = msg->Code;
					GT_GetGadgetAttrs(listgad, win, NULL, GTLV_Top, &topitem);
				}
				break;

			case IDCMP_RAWKEY:
				switch (msg->Code) {
				case 0x45:	/* Esc */
					keepgoing = FALSE;
					node = NULL;
					break;

				case 0x44:	/* Return */
					keepgoing = FALSE;
					/* Find node */
					for (c = 0, node = ReqList.lh_Head; c < select; c++, node = node->ln_Succ)
						;
					break;

				case 0x4c:	/* Up arrow */
					if (select > 0) {
						if (msg->Qualifier & IEQUALIFIER_LSHIFT || msg->Qualifier & IEQUALIFIER_RSHIFT)
							select -= shownitems - 1;
						else if (msg->Qualifier & IEQUALIFIER_LALT || msg->Qualifier & IEQUALIFIER_RALT)
							select = 0;
						else
							--select;

						/* Get sensible values */
						if (select < 0) select = 0;
						if (select < topitem) topitem = select;

						/* Set new values (GTLV_Top only requiered for v37/v38,
						** overridden by GTLV_MakeVisible in v39+ */
						GT_SetGadgetAttrs(listgad, win, NULL,
							GTLV_Top, topitem,
							GTLV_MakeVisible, select,
							GTLV_Selected, select,
							TAG_END);
					}
					break;

				case 0x4d:	/* Down arrow */
					if (select < listitems - 1) {
						if (msg->Qualifier & IEQUALIFIER_LSHIFT || msg->Qualifier & IEQUALIFIER_RSHIFT)
							select += shownitems - 1;
						else if (msg->Qualifier & IEQUALIFIER_LALT || msg->Qualifier & IEQUALIFIER_RALT)
							select = listitems - 1;
						else
							++select;

						/* Get sensible values */
						if (select >= listitems) select = listitems - 1;
						if (select > topitem + shownitems - 1) {
							topitem = select - shownitems + 1;
							if (topitem < 0) topitem = 0;
						}
						GT_SetGadgetAttrs(listgad, win, NULL,
							GTLV_Top, topitem,
							GTLV_MakeVisible, select,
							GTLV_Selected, select,
							TAG_END);
					}
					break;
				}
				break;

			case IDCMP_CHANGEWINDOW:
				left = win->LeftEdge;
				top = win->TopEdge;
				/* Recalculate gadget size only if window size has changed */
				if (width != win->Width || height != win->Height) {
					width = win->Width;
					height = win->Height;
					if (!drawwin()) {
						keepgoing = FALSE;
						node = NULL;
					}
				}
				break;

			case IDCMP_REFRESHWINDOW:	/* Required by GadTools */
				GT_BeginRefresh(win);
				GT_EndRefresh(win, TRUE);
				break;

			case IDCMP_CLOSEWINDOW:
				keepgoing = FALSE;
				node = NULL;
				break;
			}
			GT_ReplyIMsg(msg);
		}
	}

	CloseWindow(win);
	FreeGadgets(contextgad);
	FreeVisualInfo(vi);

	return node;
}


BOOL
openwin(STRPTR word, STRPTR title, STRPTR sname)
{
	select = 0;
	topitem = 0;
	contextgad = listgad = NULL;
	struct Screen *screen = NULL;
	BOOL retval = FALSE;

	if (title)
		sprintf(titlebuf, title, listitems);
	else
		sprintf(titlebuf, "RExpand [%s] (%d matches) ", word, listitems);

	if (sname)
		screen = LockPubScreen(sname);

	if (win = OpenWindowTags(NULL,
			WA_Title, titlebuf,
			WA_Left, left,
			WA_Top, top,
			WA_Width, width,
			WA_Height, height,
			WA_DragBar, TRUE,
			WA_DepthGadget, TRUE,
			WA_CloseGadget, TRUE,
			WA_SizeGadget, TRUE,
			WA_SizeBBottom, TRUE,
			WA_Activate, TRUE,
			WA_IDCMP,	LISTVIEWIDCMP |
						IDCMP_CLOSEWINDOW |
						IDCMP_REFRESHWINDOW |
						IDCMP_RAWKEY |
						IDCMP_CHANGEWINDOW,
			WA_MinWidth, 100,
			WA_MinHeight, 50,
			WA_MaxWidth, -1,
			WA_MaxHeight, -1,
			WA_RMBTrap, TRUE,
			WA_PubScreen, screen,
			TAG_END)) {
		if (vi = GetVisualInfo(win->WScreen, TAG_END)) {
			if (drawwin())
				retval = TRUE;
			else {
				FreeVisualInfo(vi);
				CloseWindow(win);
			}
		} else
			CloseWindow(win);
	}

	if (screen)
		UnlockPubScreen(NULL, screen);

	return retval;
}


BOOL
drawwin(void)
{
	struct NewGadget ng;
	int space;

	/* Remove all gadgets and clear up window */
	if (contextgad) {
		RemoveGList(win, contextgad, -1);
		FreeGadgets(contextgad);
		EraseRect(win->RPort, 0, 0, win->Width - 1, win->Height - 1);
		RefreshWindowFrame(win);
		contextgad = NULL;
	}
	listgad = NULL;

	/* Align window height so there's no extra space beneath the listview */
	space = (height - win->BorderTop - win->BorderBottom - 8) % (win->WScreen->Font->ta_YSize + 1);
	if (space) {
		height -= space;
		/* If smaller than MinHeight (50), add a line */
		if (height < 50)
			height += win->WScreen->Font->ta_YSize + 1;
		ChangeWindowBox(win, left, top, width, height);
	}

	/* Set up listview gadget data */
	ng.ng_VisualInfo = vi;
	ng.ng_TextAttr = win->WScreen->Font;
	ng.ng_LeftEdge = win->BorderLeft + 2;
	ng.ng_TopEdge = win->BorderTop + 2;
	ng.ng_Width = width - win->BorderLeft - win->BorderRight - 4;
	ng.ng_Height = height - win->BorderTop - win->BorderBottom - 4;
	ng.ng_GadgetText = NULL;
	ng.ng_GadgetID = NULL;
	ng.ng_Flags = NULL;

	/* Find out haw many items that is displayed in the listview */
	shownitems = (ng.ng_Height - 4) / (ng.ng_TextAttr->ta_YSize + 1);
	if (select > topitem + shownitems - 1)
		topitem = select;

	/* Create the gadget */
	listgad = CreateContext(&contextgad);
	listgad = CreateGadget(LISTVIEW_KIND, listgad, &ng,
		GTLV_Labels, &ReqList,
		GTLV_ShowSelected, NULL,
		GTLV_Selected, select,
		GTLV_MakeVisible, select,	/* v39+ only, has to be done manually with v37/v38 */
		GTLV_Top, topitem,			/* to make sure the selected item is visible */
		LAYOUTA_Spacing, 1,
		TAG_END);

	/* If all went well, show gadget */
	if (listgad) {
		AddGList(win, contextgad, -1, -1, NULL);
		RefreshGList(contextgad, win, NULL, -1);
		GT_RefreshWindow(win, NULL);
		return TRUE;
	} else {
		FreeGadgets(contextgad);
		return FALSE;
	}
}

