/*	SCCS Id: @(#)winmenu.c	3.2	96/02/17	*/
/* Copyright (c) Gregg Wonderly, Naperville, Illinois,  1991,1992,1993,1996. */
/* NetHack may be freely redistributed.  See license for details. */

#include "NH:sys/amiga/windefs.h"
#include "NH:sys/amiga/winext.h"
#include "NH:sys/amiga/winproto.h"

/* Start building the text for a menu */
void
amii_start_menu(window)
    register winid window;
{
    register int i;
    register struct amii_WinDesc *cw;
    register amii_menu_item *mip;

    if(window == WIN_ERR || (cw = amii_wins[window]) == NULL || cw->type != NHW_MENU)
	panic(winpanicstr,window, "start_menu");

    amii_clear_nhwindow(window);

    if( cw->data && ( cw->type == NHW_MESSAGE ||
	    cw->type == NHW_MENU || cw->type == NHW_TEXT ) )
    {
	for( i = 0; i < cw->maxrow; ++i )
	{
	    if( cw->data[ i ] )
		free( cw->data[ i ] );
	}
	free( cw->data );
	cw->data = NULL;
    }

    for( mip = cw->menu.items, i = 0; (mip = cw->menu.items) && i < cw->menu.count; ++i )
    {
	cw->menu.items = mip->next;
    	free( mip );
    }

    cw->menu.items = 0;
    cw->menu.count = 0;
    cw->menu.chr = 'a';

    if( cw->morestr ) free( cw->morestr );
    cw->morestr = NULL;

    if( window == WIN_INVEN && cw->win != NULL )
    {
	if( alwaysinvent )
	    cw->wasup = 1;
    }
    cw->cury = cw->rows = cw->maxrow = cw->maxcol = 0;
    return;
}

/* Add a string to a menu */
void
amii_add_menu(window,glyph, id, ch, gch, attr, str, preselected)
    register winid window;
    register int glyph;
    register const anything *id;
    register char ch;
    register char gch;
    register int attr;
    register const char *str;
    register BOOLEAN_P preselected;
{
    register struct amii_WinDesc *cw;
    amii_menu_item *mip;
    char buf[ 4+BUFSZ ];

    if(str == NULL)return;

    if(window == WIN_ERR || (cw = amii_wins[window]) == NULL || cw->type != NHW_MENU)
	panic(winpanicstr,window, "add_menu");

    mip = (amii_menu_item *)alloc( sizeof( *mip ) );
    mip->identifier = *id;
    mip->selected = preselected;
    mip->attr = attr;
    mip->glyph = Is_rogue_level(&u.uz) ? NO_GLYPH : glyph;
    mip->selector = 0;
    mip->gselector = gch;
    mip->count = -1;

    if (id->a_void && !ch && cw->menu.chr != 0)
    {
	ch = cw->menu.chr++;
	if( ch == 'z' )
	    cw->menu.chr = 'A';
	if( ch == 'Z' )
	    cw->menu.chr = 0;
    }

    mip->canselect = ( id->a_void != 0 );

    if( id->a_void && ch != '\0')
    {
	Sprintf( buf, "%c - %s", ch, str );
	str = buf;
	mip->canselect = 1;
    }

    mip->selector = ch;

    amii_putstr( window, attr, str );

    mip->str = cw->data[ cw->cury - 1 ];
    cw->menu.count++;

    mip->next = NULL;

    if( cw->menu.items == 0 )
    	cw->menu.last = cw->menu.items = mip;
    else
    {
	cw->menu.last->next = mip;
	cw->menu.last = mip;
    }
}

/* Done building a menu. */

void
amii_end_menu(window,morestr)
    register winid window;
    register const char *morestr;
{
    register struct amii_WinDesc *cw;

    if(window == WIN_ERR || (cw=amii_wins[window]) == NULL
       || cw->type != NHW_MENU )
	panic(winpanicstr,window, "end_menu");

    if( morestr && *morestr )
    {
	anything any;
#define PROMPTFIRST /* Define this to have prompt first */
#ifdef PROMPTFIRST
	amii_menu_item *mip;
	int i;
	char *t;
	mip = cw->menu.last;
#endif
	any.a_void = 0;
	amii_add_menu( window, NO_GLYPH, &any, 0, 0, ATR_NONE, morestr,
	   MENU_UNSELECTED);
#ifdef PROMPTFIRST /* Do some shuffling. Last first, push others one forward */
	mip->next = NULL;
	cw->menu.last->next = cw->menu.items;
	cw->menu.items = cw->menu.last;
	cw->menu.last = mip;
	t = cw->data[cw->cury-1];
	for (i=cw->cury-1; i>0; i--) {
	    cw->data[i] = cw->data[i-1];
	}
	cw->data[0] = t;
#endif
    }

    /* If prompt first, don't put same string in title where in most cases
       it's not entirely visible anyway */
#ifndef PROMPTFIRST
    if( morestr )
	cw->morestr = strdup( morestr );
#endif
}

/* Select something from the menu. */

int
amii_select_menu(window, how, mip )
    register winid window;
    register int how;
    register menu_item **mip;
{
    int cnt;
    register struct amii_WinDesc *cw;

    if( window == WIN_ERR || ( cw=amii_wins[window] ) == NULL ||
	  cw->type != NHW_MENU )
	panic(winpanicstr,window, "select_menu");

    cnt = DoMenuScroll( window, 1, how, mip );

    /* This would allow the inventory window to stay open. */
    if( !alwaysinvent || window != WIN_INVEN )
	dismiss_nhwindow(window);       /* Now tear it down */
    return cnt;
}

amii_menu_item *
find_menu_item( register struct amii_WinDesc *cw, int idx )
{
    amii_menu_item *mip;
    for( mip = cw->menu.items; idx > 0 && mip; mip = mip->next )
	--idx;

    return( mip );
}

int
make_menu_items( register struct amii_WinDesc *cw, register menu_item **rmip )
{
    register int idx = 0;
    register amii_menu_item *mip;
    register menu_item *mmip;

    for( mip = cw->menu.items; mip; mip = mip->next )
    {
	if( mip->selected )
	    ++idx;
    }

    if( idx )
    {
	mmip = *rmip = (menu_item *)alloc( idx * sizeof( *mip ) );
	for( mip = cw->menu.items; mip; mip = mip->next )
	{
	    if( mip->selected )
	    {
		mmip->item = mip->identifier;
		mmip->count = mip->count;
		mmip++;
	    }
	}

	cw->mi = *rmip;
    }
    return( idx );
}

int
DoMenuScroll( win, blocking, how, retmip )
    int win, blocking, how;
    menu_item **retmip;
{
    amii_menu_item *amip;
    register struct Window *w;
    register struct NewWindow *nw;
    struct PropInfo *pip;
    register struct amii_WinDesc *cw;
    struct IntuiMessage *imsg;
    struct Gadget *gd;
    register int wheight, xsize, ysize, aredone = 0;
    register int txwd, txh;
    long mics, secs, class, code;
    long oldmics = 0, oldsecs = 0;
    int aidx, oidx, topidx, hidden;
    int totalvis;
    SHORT mx, my;
    static char title[ 100 ];
    int dosize = 1;
    struct Screen *scrn = HackScreen;
    int x1,x2,y1,y2;
    long counting = FALSE, count = 0, reset_counting = FALSE;
    char countString[32];

    if( win == WIN_ERR || ( cw = amii_wins[ win ] ) == NULL )
	panic(winpanicstr,win,"DoMenuScroll");

    /*  Initial guess at window sizing values */
    txwd = txwidth;
    if( WINVERS_AMIV )
    {
	if( win == WIN_INVEN )
	    txh = max( txheight, pictdata.ysize + 3 ); /* interline space */
	else
	    txh = txheight; /* interline space */
    }
    else
	txh = txheight; /* interline space */

    /* Check to see if we should open the window, should need to for
     * TEXT and MENU but not MESSAGE.
     */

    w = cw->win;
    topidx = 0;

    if( w == NULL )
    {

#ifdef  INTUI_NEW_LOOK
	if( IntuitionBase->LibNode.lib_Version >= 37 )
	{
	    PropScroll.Flags |= PROPNEWLOOK;
	}
#endif
	nw = (void *)DupNewWindow( (void *)(&new_wins[ cw->type ].newwin) );
	if( !alwaysinvent || win != WIN_INVEN )
	{
	    xsize = scrn->WBorLeft + scrn->WBorRight + MenuScroll.Width + 1 +
				(txwd * cw->maxcol);
	    if( WINVERS_AMIV )
	    {
		if( win == WIN_INVEN )
		    xsize += pictdata.xsize + 4;
	    }
	    if( xsize > amiIDisplay->xpix )
		xsize = amiIDisplay->xpix;

	    /* If next row not off window, use it, else use the bottom */

	    ysize = ( txh * cw->maxrow ) +          	  /* The text space */
		    HackScreen->WBorTop + txheight + 1 +  /* Top border */
		    HackScreen->WBorBottom + 3;     	  /* The bottom border */
	    if( ysize > amiIDisplay->ypix )
		ysize = amiIDisplay->ypix;

	    /* Adjust the size of the menu scroll gadget */

	    nw->TopEdge = 0;
	    if( cw->type == NHW_TEXT && ysize < amiIDisplay->ypix )
		nw->TopEdge += ( amiIDisplay->ypix - ysize ) / 2;
	    nw->LeftEdge = amiIDisplay->xpix - xsize;
	    if( cw->type == NHW_MENU )
	    {
		if( nw->LeftEdge > 10 )
		    nw->LeftEdge -= 10;
		else
		    nw->LeftEdge = 0;
		if( amiIDisplay->ypix - nw->Height > 10 )
		    nw->TopEdge += 10;
		else
		    nw->TopEdge = amiIDisplay->ypix - nw->Height - 1;
	    }
	    if( cw->type == NHW_TEXT && xsize < amiIDisplay->xpix )
		nw->LeftEdge -= ( amiIDisplay->xpix - xsize ) / 2;
	}
	else if( win == WIN_INVEN )
	{
	    struct Window *mw = amii_wins[ WIN_MAP ]->win;
	    struct Window *sw = amii_wins[ WIN_STATUS ]->win;

	    xsize = scrn->WBorLeft + scrn->WBorRight + MenuScroll.Width + 1 +
				(txwd * cw->maxcol);

	    /* Make space for the glyph to appear at the left of the description */
	    if( WINVERS_AMIV )
		xsize += pictdata.xsize + 4;

	    if( xsize > amiIDisplay->xpix )
		xsize = amiIDisplay->xpix;

	    /* If next row not off window, use it, else use the bottom */

	    ysize = sw->TopEdge - (mw->TopEdge + mw->Height) - 1;
	    if( ysize > amiIDisplay->ypix )
		ysize = amiIDisplay->ypix;

	    /* Adjust the size of the menu scroll gadget */

	    nw->TopEdge = mw->TopEdge + mw->Height;
	    nw->LeftEdge = 0;
	}
	cw->newwin = (void *)nw;
	if( nw == NULL )
	    panic("No NewWindow Allocated" );

	nw->Screen = HackScreen;

	if( win == WIN_INVEN )
	{
	    sprintf( title, "%s the %s's Inventory", plname, pl_character );
	    nw->Title = title;
	    if( lastinvent.MaxX != 0 )
	    {
		nw->LeftEdge = lastinvent.MinX;
		nw->TopEdge = lastinvent.MinY;
		nw->Width = lastinvent.MaxX;
		nw->Height = lastinvent.MaxY;
	    }
	}
	else if( cw->morestr )
	    nw->Title = cw->morestr;

	/* Adjust the window coordinates and size now that we know
	 * how many items are to be displayed.
	 */

	if( ( xsize > amiIDisplay->xpix - nw->LeftEdge ) &&
	    ( xsize < amiIDisplay->xpix ) )
	{
	    nw->LeftEdge = amiIDisplay->xpix - xsize;
	    nw->Width = xsize;
	}
	else
	{
	    nw->Width = min( xsize, amiIDisplay->xpix - nw->LeftEdge );
	}
	nw->Height = min( ysize, amiIDisplay->ypix - nw->TopEdge );

	if( WINVERS_AMIV || WINVERS_AMII )
	{
	    /* Make sure we are using the correct hook structure */
	    nw->Extension = cw->wintags;
	}

	/* Now, open the window */
	w = cw->win = OpenShWindow( (void *)nw );

	if( w == NULL )
	{
	    char buf[ 130 ];

	    sprintf( buf, "No Window Opened For Menu (%d,%d,%d-%d,%d-%d)",
		nw->LeftEdge, nw->TopEdge, nw->Width, amiIDisplay->xpix,
		nw->Height, amiIDisplay->ypix );
	    panic( buf );
	}

#ifdef HACKFONT
	if( TextsFont )
	    SetFont(w->RPort, TextsFont );
	else if( HackFont )
	    SetFont(w->RPort, HackFont );
#endif
	txwd = w->RPort->TxWidth;
	if( WINVERS_AMIV )
	{
	    if( win == WIN_INVEN )
		txh = max( w->RPort->TxHeight, pictdata.ysize + 3 ); /* interline space */
	    else
		txh = w->RPort->TxHeight; /* interline space */
	}
	else
	    txh = w->RPort->TxHeight; /* interline space */

	/* subtract 2 to account for spacing away from border (1 on each side) */
	wheight = ( w->Height - w->BorderTop - w->BorderBottom - 2 ) / txh;
	if( WINVERS_AMIV )
	{
	    if( win == WIN_INVEN )
	    {
		cw->cols = ( w->Width - w->BorderLeft -
			    w->BorderRight - 4 - pictdata.xsize - 3 ) / txwd;
	    }
	    else
	    {
		cw->cols = ( w->Width - w->BorderLeft - w->BorderRight - 4 ) / txwd;
	    }
   	}
	else
	{
	    cw->cols = ( w->Width - w->BorderLeft - w->BorderRight - 4 ) / txwd;
	}
	totalvis = CountLines( win );
    }
    else
    {
	txwd = w->RPort->TxWidth;
	if( WINVERS_AMIV )
	{
	    if( win == WIN_INVEN )
		txh = max( w->RPort->TxHeight, pictdata.ysize + 3 ); /* interline space */
	    else
		txh = w->RPort->TxHeight; /* interline space */
	}
	else
	{
	    txh = w->RPort->TxHeight; /* interline space */
	}

	/* subtract 2 to account for spacing away from border (1 on each side) */
	wheight = ( w->Height - w->BorderTop - w->BorderBottom - 2 ) / txh;
	if( WINVERS_AMIV )
	{
	    if( win == WIN_INVEN )
	    {
		cw->cols = ( w->Width - w->BorderLeft -
			    w->BorderRight - 4 - pictdata.xsize - 3) / txwd;
	    }
	    else
		cw->cols = ( w->Width - w->BorderLeft - w->BorderRight - 4 ) / txwd;
	}
	else
	{
	    cw->cols = ( w->Width - w->BorderLeft - w->BorderRight - 4 ) / txwd;
	}

	totalvis = CountLines( win );

	for( gd = w->FirstGadget; gd && gd->GadgetID != 1; )
	    gd = gd->NextGadget;

	if( gd )
	{
	    pip = (struct PropInfo *)gd->SpecialInfo;
	    hidden = max( totalvis - wheight, 0 );
	    topidx = (((ULONG)hidden * pip->VertPot) + (MAXPOT/2)) >> 16;
	}
    }

    for( gd = w->FirstGadget; gd && gd->GadgetID != 1; )
	gd = gd->NextGadget;

    if( !gd ) panic("Can't find scroll gadget" );

    morc = 0;
    oidx = -1;

#if 0
    /* Make sure there are no selections left over from last time. */
/* XXX potential problem for preselection if this is really needed */
  for( amip = cw->menu.items; amip; amip = amip->next )
	amip->selected = 0;
#endif

    DisplayData( win, topidx );

    /* Make the prop gadget the right size and place */

    SetPropInfo( w, gd, wheight, totalvis, topidx );
    oldsecs = oldmics = 0;

    /* If window already up, don't stop to process events */
    if( cw->wasup )
    {
    	aredone = 1;
    	cw->wasup = 0;
    }

    while( !aredone )
    {
	/* Process window messages */

	WaitPort( w->UserPort );
	while( imsg = (struct IntuiMessage * ) GetMsg( w->UserPort ) )
	{
	    class = imsg->Class;
	    code = imsg->Code;
	    mics = imsg->Micros;
	    secs = imsg->Seconds;
	    gd = (struct Gadget *) imsg->IAddress;
	    mx = imsg->MouseX;
	    my = imsg->MouseY;

	    /* Only do our window or VANILLAKEY from other windows */

	    if( imsg->IDCMPWindow != w && class != VANILLAKEY &&
							class != RAWKEY )
	    {
		ReplyMsg( (struct Message *) imsg );
		continue;
	    }

	    /* Do DeadKeyConvert() stuff if RAWKEY... */
	    if( class == RAWKEY )
	    {
		class = VANILLAKEY;
		code = ConvertKey( imsg );
	    }
	    ReplyMsg( (struct Message *) imsg );

	    switch( class )
	    {
		case NEWSIZE:

		    /*
		     * Ignore every other newsize, no action needed,
		     * except RefreshWindowFrame() in case borders got overwritten
		     * for some reason. It should not happen, but ...
		     */

		    if( !dosize )
		    {
			RefreshWindowFrame(w);
			dosize = 1;
			break;
		    }

		    if( win == WIN_INVEN )
		    {
			lastinvent.MinX = w->LeftEdge;
			lastinvent.MinY = w->TopEdge;
			lastinvent.MaxX = w->Width;
			lastinvent.MaxY = w->Height;
		    }
		    else if( win == WIN_MESSAGE )
		    {
			lastmsg.MinX = w->LeftEdge;
			lastmsg.MinY = w->TopEdge;
			lastmsg.MaxX = w->Width;
			lastmsg.MaxY = w->Height;
		    }

		    /* Find the gadget */

		    for( gd = w->FirstGadget; gd && gd->GadgetID != 1; )
			gd = gd->NextGadget;

		    if( !gd )
			panic("Can't find scroll gadget" );

		    totalvis = CountLines( win );
		    wheight = ( w->Height - w->BorderTop -
						w->BorderBottom - 2) / txh;
		    if( WINVERS_AMIV )
		    {
			if( win == WIN_INVEN )
			{
			    cw->cols = ( w->Width - w->BorderLeft -
					w->BorderRight - 4 - pictdata.xsize - 3) / txwd;
			}
			else
			    cw->cols = ( w->Width - w->BorderLeft - w->BorderRight - 4 ) / txwd;
		    }
		    else
		    {
			cw->cols = ( w->Width - w->BorderLeft - w->BorderRight - 4 ) / txwd;
		    }

		    if( wheight < 2 )
			wheight = 2;

		    /*
		     * Clear the right side & bottom. Parts of letters are not erased by
		     * amii_cl_end if window shrinks and columns decrease.
		     */

		    if ( WINVERS_AMII || WINVERS_AMIV ) {
			amii_setfillpens(w, cw->type);
			SetDrMd(w->RPort, JAM2);
			x2 = w->Width - w->BorderRight;
			y2 = w->Height - w->BorderBottom;
			x1 = x2 - w->IFont->tf_XSize - w->IFont->tf_XSize;
			y1 = w->BorderTop;
			if (x1 < w->BorderLeft)
			    x1 = w->BorderLeft;
			RectFill(w->RPort, x1, y1, x2, y2);
			x1 = w->BorderLeft;
			y1 = y1 - w->IFont->tf_YSize;
			RectFill(w->RPort, x1, y1, x2, y2);
			RefreshWindowFrame(w);
		    }

		    /* Make the prop gadget the right size and place */

		    DisplayData( win, topidx );
		    SetPropInfo( w, gd, wheight, totalvis, topidx );

		    /* Force the window to a text line boundary <= to
		     * what the user dragged it to.  This eliminates
		     * having to clean things up on the bottom edge.
		     */

		    SizeWindow( w, 0, ( wheight * txh) +
			    w->BorderTop + w->BorderBottom + 2 - w->Height );

		    /* Don't do next NEWSIZE, we caused it */
		    dosize = 0;
		    oldsecs = oldmics = 0;
		    break;

		case VANILLAKEY:
#define CTRL(x)     ((x)-'@')
		    morc = code = map_menu_cmd(code);
		    if (code == MENU_SELECT_ALL) {
			if (how == PICK_ANY) {
			    amip = cw->menu.items;
			    while (amip) {
				if (amip->canselect && amip->selector) {
				/*
				 * Select those yet unselected
				 * and apply count if necessary
				 */
				    if (!amip->selected) {
					amip->selected = TRUE;
					if (counting) {
					    amip->count = count;
					    reset_counting = TRUE;
					/*
					 * This makes the assumption that 
					 * the string is in format "X - foo"
					 * with additional selecting and formatting
					 * data in front (size SOFF)
					 */
					    amip->str[SOFF+2] = '#';
					} else {
					    amip->count = -1;
					    amip->str[SOFF+2] = '-';
					}
				    }
				}
				amip=amip->next;
			    }
			    DisplayData(win, topidx);
			}
		    } else if (code == MENU_UNSELECT_ALL) {
			if (how == PICK_ANY) {
			    amip = cw->menu.items;
			    while (amip) {
				if (amip->selected) {
				    amip->selected = FALSE;
				    amip->count = -1;
				    amip->str[SOFF+2] = '-';
				}
				amip=amip->next;
			    }
			    DisplayData(win, topidx);
			}
		    } else if (code == MENU_INVERT_ALL) {
			if (how == PICK_ANY) {
			    amip = cw->menu.items;
			    while (amip) {
				if (amip->canselect && amip->selector) {
				    amip->selected = !amip->selected;
				    if (counting && amip->selected) {
					amip->count = count;
					amip->str[SOFF+2] = '#';
					reset_counting = TRUE;
				    } else {
					amip->count = -1;
					amip->str[SOFF+2] = '-';
				    }
				}
				amip=amip->next;
			    }
			    DisplayData(win, topidx);
			}
		    } else if (code == MENU_SELECT_PAGE) {
			if (how == PICK_ANY) {
			    int i = 0;
			    amip = cw->menu.items;
			    while (amip && i++ < topidx)
				amip = amip->next;
			    for (i=0;i < wheight && amip; i++, amip=amip->next) {
				if (amip->canselect && amip->selector) {
				    if (!amip->selected) {
					if (counting) {
					    amip->count = count;
					    reset_counting = TRUE;
					    amip->str[SOFF+2] = '#';
					} else {
					    amip->count = -1;
					    amip->str[SOFF+2] = '-';
					}
				    }
	 			    amip->selected = TRUE;
				}
			    }
			    DisplayData(win, topidx);
			}
		    } else if (code == MENU_UNSELECT_PAGE) {
			if (how == PICK_ANY) {
			    int i = 0;
			    amip = cw->menu.items;
			    while (amip && i++ < topidx)
				amip = amip->next;
			    for (i=0;i < wheight && amip; i++, amip=amip->next) {
				if (amip->selected) {
				    amip->selected = FALSE;
				    amip->count = -1;
				    amip->str[SOFF+2] = '-';
				}
			    }
			    DisplayData(win, topidx);
			}
		    } else if (code == MENU_INVERT_PAGE) {
			if (how == PICK_ANY) {
			    int i = 0;
			    amip = cw->menu.items;
			    while (amip && i++ < topidx)
				amip = amip->next;
			    for (i=0;i < wheight && amip; i++, amip=amip->next) {
				if (amip->canselect && amip->selector) {
				    amip->selected = !amip->selected;
				    if (counting && amip->selected) {
					amip->count = count;
					amip->str[SOFF+2] = '#';
					reset_counting = TRUE;
				    } else {
					amip->count = -1;
					amip->str[SOFF+2] = '-';
				    }
				}
			    }
			    DisplayData(win, topidx);
			}
		    } else if (code == MENU_SEARCH && cw->type == NHW_MENU) {
			if (how == PICK_ONE || how == PICK_ANY) {
			    char buf[BUFSZ];
			    amip = cw->menu.items;
			    amii_getlin("Search for:", buf);
			    if (!*buf || *buf == '\033')
				break;
			    while (amip) {
				if (amip->canselect && amip->selector && amip->str &&
				    strstri(&amip->str[SOFF], buf)) {
				    if (how == PICK_ONE) {
					amip->selected = TRUE;
					aredone = 1;
					break;
				    }
				    amip->selected = !amip->selected;
				    if (counting && amip->selected) {
					amip->count = count;
					reset_counting = TRUE;
					amip->str[SOFF+2] = '#';
				    } else {
					amip->count = -1;
					reset_counting = TRUE;
					amip->str[SOFF+2] = '-';
				    }
				}
				amip = amip->next;
			    }
			}
			DisplayData(win, topidx);
		    } else if (how == PICK_ANY && isdigit(code) &&
			       (counting || (!counting && code !='0'))) {
			if (count < LARGEST_INT) {
			    count = count*10 + (long)(code-'0');
			    if (count > LARGEST_INT)
				count = LARGEST_INT;
			    if (count > 0) {
				counting = TRUE;
				reset_counting = FALSE;
			    } else {
				reset_counting = TRUE;
			    }
			    sprintf(countString, "Count: %d", count);
			    pline(countString);
			}
		    } else if( code == CTRL('D') || code == CTRL('U') ||
			       code == MENU_NEXT_PAGE || code == MENU_PREVIOUS_PAGE ||
			       code == MENU_FIRST_PAGE || code == MENU_LAST_PAGE )
		    {
			int endcnt, i;

			for( gd = w->FirstGadget; gd && gd->GadgetID != 1; )
			    gd = gd->NextGadget;

			if( !gd )
			    panic("Can't find scroll gadget" );

			endcnt = wheight; /* /2; */
			if( endcnt == 0 )
			    endcnt = 1;

			if (code == MENU_FIRST_PAGE) {
			    topidx = 0;
			} else if (code == MENU_LAST_PAGE) {
			    topidx = cw->maxrow - wheight;
			} else for( i = 0; i < endcnt; ++i )
			{
			    if (code == CTRL('D') || code == MENU_NEXT_PAGE)
			    {
				if( topidx + wheight < cw->maxrow )
				    ++topidx;
				else
				    break;
			    }
			    else if (code = CTRL('U') || code == MENU_PREVIOUS_PAGE)
			    {
				if( topidx > 0 )
				    --topidx;
				else
				    break;
			    }
			}
			/* Make prop gadget the right size and place */

			DisplayData( win, topidx );
			SetPropInfo( w,gd, wheight, totalvis, topidx );
			oldsecs = oldmics = 0;
		    }
		    else if( code == '\b' )
		    {
			for( gd = w->FirstGadget; gd && gd->GadgetID != 1; )
			    gd = gd->NextGadget;

			if( !gd )
			    panic("Can't find scroll gadget" );

			if( topidx - wheight - 2 < 0 )
			{
			    topidx = 0;
			}
			else
			{
			    topidx -= wheight - 2;
			}
			DisplayData( win, topidx );
			SetPropInfo( w, gd, wheight, totalvis, topidx );
			oldsecs = oldmics = 0;
		    }
		    else if( code == ' ' )
		    {
			for( gd = w->FirstGadget; gd && gd->GadgetID != 1; )
			    gd = gd->NextGadget;

			if( !gd )
			    panic("Can't find scroll gadget" );

			if( topidx + wheight >= cw->maxrow )
			{
			    morc = 0;
			    aredone = 1;
			}
			else
			{
			    /*  If there are still lines to be seen */

			    if( cw->maxrow > topidx + wheight )
			    {
				if( wheight > 2 )
				    topidx += wheight - 2;
				else
				    ++topidx;
				DisplayData( win, topidx );
				SetPropInfo( w, gd, wheight,
						    totalvis, topidx );
			    }
			    oldsecs = oldmics = 0;
			}
		    }
		    else if( code == '\n' || code == '\r' )
		    {
			for( gd = w->FirstGadget; gd && gd->GadgetID != 1; )
			    gd = gd->NextGadget;

			if( !gd )
			    panic("Can't find scroll gadget" );

			/* If all line displayed, we are done */

			if( topidx + wheight >= cw->maxrow )
			{
			    morc = 0;
			    aredone = 1;
			}
			else
			{
			    /*  If there are still lines to be seen */

			    if( cw->maxrow > topidx + 1 )
			    {
				++topidx;
				DisplayData( win, topidx );
				SetPropInfo( w, gd, wheight,
						    totalvis, topidx );
			    }
			    oldsecs = oldmics = 0;
			}
		    }
		    else if( code == '\33' )
		    {
			if (counting) {
			    reset_counting = TRUE;
			} else {
			    aredone = 1;
			}
		    }
		    else
		    {
			int selected = FALSE;
			for( amip = cw->menu.items; amip; amip = amip->next )
			{
			    if( amip->selector == code )
			    {
				if( how == PICK_ONE )
				    aredone = 1;
				amip->selected = !amip->selected;
				if (counting && amip->selected) {
				    amip->count = count;
				    reset_counting = TRUE;
				    amip->str[SOFF+2] = '#';
				} else {
				    amip->count = -1;
				    reset_counting = TRUE;
				    amip->str[SOFF+2] = '-';
				}
				selected = TRUE;
			    } else if (amip->gselector == code )
			    {
				amip->selected = !amip->selected;
				if (counting) {
				    amip->count = count;
				    reset_counting = TRUE;
				    amip->str[SOFF+2] = '#';
				} else {
				    amip->count = -1;
				    reset_counting = TRUE;
				    amip->str[SOFF+2] = '-';
				}
				selected = TRUE;
			    }
			}
			if (selected)
			    DisplayData( win, topidx );
		    }
		    break;

		case CLOSEWINDOW:
		    if( win == WIN_INVEN )
		    {
			lastinvent.MinX = w->LeftEdge;
			lastinvent.MinY = w->TopEdge;
			lastinvent.MaxX = w->Width;
			lastinvent.MaxY = w->Height;
		    }
		    else if( win == WIN_MESSAGE )
		    {
			lastmsg.MinX = w->LeftEdge;
			lastmsg.MinY = w->TopEdge;
			lastmsg.MaxX = w->Width;
			lastmsg.MaxY = w->Height;
		    }
		    aredone = 1;
		    morc = '\33';
		    break;

		case GADGETUP:
		    if( win == WIN_MESSAGE )
			aredone = 1;
		    for( gd = w->FirstGadget; gd && gd->GadgetID != 1; )
			gd = gd->NextGadget;

		    pip = (struct PropInfo *)gd->SpecialInfo;
		    totalvis = CountLines( win );
		    hidden = max( totalvis - wheight, 0 );
		    aidx = (((ULONG)hidden * pip->VertPot) + (MAXPOT/2)) >> 16;
		    if( aidx != topidx )
			DisplayData( win, topidx = aidx );
		    break;

		case MOUSEMOVE:
		    for( gd = w->FirstGadget; gd && gd->GadgetID != 1; )
			gd = gd->NextGadget;

		    pip = (struct PropInfo *)gd->SpecialInfo;
		    totalvis = CountLines( win );
		    hidden = max( totalvis - wheight, 0 );
		    aidx = (((ULONG)hidden * pip->VertPot) + (MAXPOT/2)) >> 16;
		    if( aidx != topidx )
			DisplayData( win, topidx = aidx );
		    break;

		case INACTIVEWINDOW:
		    if( win == WIN_MESSAGE || ( win == WIN_INVEN && alwaysinvent ) )
			aredone = 1;
		    break;

		case MOUSEBUTTONS:
		    if( ( code == SELECTUP || code == SELECTDOWN ) &&
					cw->type == NHW_MENU && how != PICK_NONE )
		    {
			/* Which one is the mouse pointing at? */

			aidx = ( ( my - w->BorderTop - 1 ) / txh ) + topidx;

			/* If different lines, don't select double click */

			if( aidx != oidx )
			{
			    oldsecs = 0;
			    oldmics = 0;
			}

			/* If releasing, check for double click */

			if( code == SELECTUP )
			{
			    amip = find_menu_item( cw, aidx );
			    if( aidx == oidx )
			    {
				if( DoubleClick( oldsecs,
						    oldmics, secs, mics ) )
				{
				    aredone = 1;
				}
				oldsecs = secs;
				oldmics = mics;
			    }
			    else
			    {
				amip = find_menu_item( cw, oidx );
				amip->selected = 0;
				amip->count = -1;
				reset_counting = TRUE;
				if (amip->canselect && amip->selector)
				    amip->str[SOFF+2] = '-';
			    }
			    if (counting && amip->selected && amip->canselect && amip->selector) {
				amip->count = count;
				reset_counting = TRUE;
				amip->str[SOFF+2] = '#';
			    }
			    DisplayData( win, topidx );
			}
			else if( aidx - topidx < wheight &&
				aidx < cw->maxrow && code == SELECTDOWN )
			{
			    /* Remove old highlighting if visible */

			    amip = find_menu_item( cw, oidx );
			    if( amip && oidx != aidx &&
				    ( oidx > topidx && oidx - topidx < wheight ) )
			    {
				if( how != PICK_ANY ) {
				    amip->selected = 0;
				    amip->count = -1;
				    reset_counting = TRUE;
				    if (amip->canselect && amip->selector)
					amip->str[SOFF+2] = '-';
				}
				oidx = -1;
			    }
			    amip = find_menu_item( cw, aidx );

			    if( amip && amip->canselect && amip->selector && how != PICK_NONE )
			    {
				oidx = aidx;
				if( !DoubleClick( oldsecs,
						    oldmics, secs, mics ) )
				{
				    amip->selected = !amip->selected;
				    if (counting && amip->selected) {
					amip->count = count;
					reset_counting = TRUE;
					amip->str[SOFF+2] = '#';
				    } else {
					amip->count = -1;
					reset_counting = TRUE;
					if (amip->canselect && amip->selector)
					    amip->str[SOFF+2] = '-';
				    }
				}
			    }
			    else
			    {
				DisplayBeep( NULL );
				oldsecs = 0;
				oldmics = 0;
			    }
			    DisplayData( win, topidx );
			}
		    }
		    else
		    {
			DisplayBeep( NULL );
		    }
		    break;
	    }
	    if (!counting && morc == '\33') {
		amip = cw->menu.items;
		while (amip) {
		    if (amip->canselect && amip->selector) {
			amip->selected = FALSE;
			amip->count = -1;
			amip->str[SOFF+2] = '-';
		    }
		    amip=amip->next;
		}
	    }
	    if (reset_counting) {
		count = 0;
		if (counting)
		    pline("Count: 0");
		counting = FALSE;
	    }
	}
    }
    /* Force a cursor reposition before next message output */
    if( win == WIN_MESSAGE )
	cw->curx = -1;
    return( make_menu_items( cw, retmip ) );
}

void
ReDisplayData( win )
    winid win;
{
    int totalvis;
    register struct amii_WinDesc *cw;
    register struct Window *w;
    register struct Gadget *gd;
    unsigned long hidden, aidx, wheight;
    struct PropInfo *pip;
    
    if( win == WIN_ERR || !(cw = amii_wins[win]) || !( w = cw->win ) )
	return;

    for( gd = w->FirstGadget; gd && gd->GadgetID != 1; )
	gd = gd->NextGadget;

    if( !gd )
	return;

    wheight = (w->Height - w->BorderTop - w->BorderBottom-2)/w->RPort->TxHeight;

    pip = (struct PropInfo *)gd->SpecialInfo;
    totalvis = CountLines( win );
    hidden = max( totalvis - wheight, 0 );
    aidx = (((ULONG)hidden * pip->VertPot) + (MAXPOT/2)) >> 16;
    clear_nhwindow( win );
    DisplayData( win, aidx );
}

long
FindLine( win, line )
    winid win;
    int line;
{
    int txwd;
    register char *t;
    register struct amii_WinDesc *cw;
    register struct Window *w;
    register int i, disprow, len;
    int col = -1;

    if( win == WIN_ERR || !(cw = amii_wins[win]) || !( w = cw->win ) )
    {
	panic( winpanicstr, win, "No Window in FindLine" );
    }
    txwd = w->RPort->TxWidth;
    if( WINVERS_AMIV )
    {
	if( win == WIN_INVEN )
	{
	    cw->cols = ( w->Width - w->BorderLeft -
			w->BorderRight - 4 - pictdata.xsize - 3) / txwd;
	}
	else
	    cw->cols = ( w->Width - w->BorderLeft - w->BorderRight - 4 ) / txwd;
    }
    else
    {
	cw->cols = ( w->Width - w->BorderLeft - w->BorderRight - 4 ) / txwd;
    }

    disprow = 0;
    for( col = i = 0; line > disprow && i < cw->maxrow; ++i )
    {
	t = cw->data[ i ] + SOFF;
	if( cw->data[i][1] >= 0 )
	{
	    ++disprow;
	    col = 0;
	}

	while( *t )
	{
	    len = strlen( t );
	    if( col + len > cw->cols )
		len = cw->cols - col;
	    while( len > 0 )
	    {
	    	if( !t[len] || t[len] == ' ' )
		    break;
	    	--len;
	    }
	    if( len == 0 ) {
		while ( *t && *t != ' ') {
		    t++; col++;
		}
	    } else {
		t += len;
		col += len;
	    }
	    if( *t )
	    {
		while( *t == ' ' )
		    ++t;
		col = 0;
		++disprow;
	    }
	}
    }
    return( i );
}

long
CountLines( win )
    winid win;
{
    int txwd;
    amii_menu_item *mip;
    register char *t;
    register struct amii_WinDesc *cw;
    register struct Window *w;
    register int i, disprow, len;
    int col = -1;

    if( win == WIN_ERR || !(cw = amii_wins[win]) || !( w = cw->win ) )
    {
	panic( winpanicstr, win, "No Window in CountLines" );
    }

    txwd = w->RPort->TxWidth;
    if( WINVERS_AMIV )
    {
	if( win == WIN_INVEN )
	{
	    cw->cols = ( w->Width - w->BorderLeft -
			w->BorderRight - 4 - pictdata.xsize - 3) / txwd;
	}
	else
	    cw->cols = ( w->Width - w->BorderLeft - w->BorderRight - 4 ) / txwd;
    }
    else
    {
	cw->cols = ( w->Width - w->BorderLeft - w->BorderRight - 4 ) / txwd;
    }

    disprow = cw->maxrow;
    mip = cw->menu.items;
    for( col = i = 0; i < cw->maxrow; ++i )
    {
	t = cw->data[ i ] + SOFF;
	if( cw->type == NHW_MESSAGE && cw->data[ i ][ SEL_ITEM ] < 0 )
	    --disprow;
	else
	    col = 0;
	while( *t )
	{
	    len = strlen( t );
	    if( col + len > cw->cols )
		len = cw->cols - col;
	    while( len > 0 )
	    {
	    	if( !t[len] || t[len] == ' ' )
		    break;
	    	--len;
	    }
	    if( len == 0 ) {
		while ( *t && *t != ' ') {
		    t++; col++;
		}
	    } else {
		t += len;
		col += len;
	    }
	    if( *t )
	    {
		while( *t == ' ' )
		    ++t;
		col = 0;
		++disprow;
	    }
	}
    }
    return( disprow );
}

void
DisplayData( win, start )
    winid win;
    int start;
{
    int txwd;
    amii_menu_item *mip;
    register char *t;
    register struct amii_WinDesc *cw;
    register struct Window *w;
    register struct RastPort *rp;
    register int i, disprow, len, wheight;
    int whichcolor = -1;
    int col;

    if( win == WIN_ERR || !(cw = amii_wins[win]) || !( w = cw->win ) )
    {
	panic( winpanicstr, win, "No Window in DisplayData" );
    }

    rp = w->RPort;
    SetDrMd( rp, JAM2 );
    if( WINVERS_AMIV && win == WIN_INVEN )
    {
	wheight = ( w->Height - w->BorderTop - w->BorderBottom - 2 ) /
		    max( rp->TxHeight, pictdata.ysize + 3 );
    }
    else
    {
	wheight = ( w->Height - w->BorderTop - w->BorderBottom - 2 ) /
		    rp->TxHeight;
    }

    cw->rows = wheight;
    txwd = rp->TxWidth;
    if( WINVERS_AMIV )
    {
	if( win == WIN_INVEN )
	{
	    cw->cols = ( w->Width - w->BorderLeft -
			w->BorderRight - 4 - pictdata.xsize - 3) / txwd;
	}
	else
	    cw->cols = ( w->Width - w->BorderLeft - w->BorderRight - 4 ) / txwd;
    }
    else
    {
	cw->cols = ( w->Width - w->BorderLeft - w->BorderRight - 4 ) / txwd;
    }

    /* Get the real line to display at */
    start = FindLine( win, start );

    mip = cw->menu.items;
    for( i = 0; mip && i < start; ++i )
    {
	mip = mip->next;
    }

    /* Skip any initial response to a previous line */
    if( cw->type == NHW_MESSAGE && mip && mip->selected < 0 )
	++start;
    if( WINVERS_AMIV && cw->type == NHW_MESSAGE )
	SetAPen( rp, amii_msgAPen );
    
    for( disprow = i = start; disprow < wheight + start; i++ )
    {
	/* Just erase unused lines in the window */
    	if( i >= cw->maxrow )
    	{
	    if (WINVERS_AMIV && win == WIN_INVEN) {
		amii_curs( win, 0, disprow - start );
		amiga_print_glyph( win, 0, NO_GLYPH);
	    }
	    amii_curs( win, 1, disprow - start );
	    amii_cl_end( cw, 0 );
	    ++disprow;
	    continue;
    	}

	/* Any string with a highlighted attribute goes
	 * onto the end of the current line in the message window.
	 */
	if( cw->type == NHW_MESSAGE )
	    SetAPen( rp, cw->data[ i ][ SEL_ITEM ] < 0 ? C_RED : amii_msgAPen );

	/* Selected text in the message window goes onto the end of the current line */
	if( cw->type != NHW_MESSAGE || cw->data[ i ][ SEL_ITEM ] >= 0 )
	{
	    amii_curs( win, 1, disprow - start );
	    if( WINVERS_AMIV && win == WIN_INVEN )
	    {
		if( mip )
		    amiga_print_glyph( win, 0, mip->glyph );
		amii_curs( win, 1, disprow - start );
	    }
	    col = 0;
	}

	/* If this entry is to be highlighted, do so */
	if( mip && mip->selected != 0 )
	{
	    if( whichcolor != 1 )
	    {
		SetDrMd( rp, JAM2 );
		if( WINVERS_AMIV )
		{
		    SetAPen( rp, amii_menuBPen );
		    SetBPen( rp, C_BLUE );
		}
		else
		{
		    SetAPen( rp, C_BLUE );
		    SetBPen( rp, amii_menuAPen );
		}
		whichcolor = 1;
	    }
	}
	else if( whichcolor != 2 )
	{
	    SetDrMd( rp, JAM2 );
	    if( cw->type == NHW_MESSAGE )
	    {
		SetAPen( rp, amii_msgAPen );
		SetBPen( rp, amii_msgBPen );
	    }
	    else if( cw->type == NHW_MENU )
	    {
		SetAPen( rp, amii_menuAPen );
		SetBPen( rp, amii_menuBPen );
	    }
	    else if( cw->type == NHW_TEXT )
	    {
		SetAPen( rp, amii_textAPen );
		SetBPen( rp, amii_textBPen );
	    }
	    whichcolor = 2;
	}

	/* Next line out, wrap if too long */

	t = cw->data[ i ] + SOFF;
	++disprow;
	col = 0;
	while( *t )
	{
	    len = strlen( t );
	    if( len > (cw->cols - col) )
		len = cw->cols - col;
	    while( len > 0 )
	    {
	    	if( !t[len] || t[len] == ' ' )
		    break;
	    	--len;
	    }
	    if( len == 0 ) {
		Text( rp, t, cw->cols - col );
		while ( *t && *t != ' ') {
		    t++; col++;
		}
	    } else {
		Text( rp, t, len );
		t += len;
		col += len;
	    }
	    amii_cl_end( cw, col );
	    if( *t )
	    {
		++disprow;
		/* Stop at the bottom of the window */
		if( disprow > wheight + start )
		    break;
		while( *t == ' ' )
		    ++t;
		amii_curs( win, 1, disprow - start - 1 );
		if( mip && win == WIN_INVEN && WINVERS_AMIV )
		{
		    /* Erase any previous glyph drawn here. */
		    amiga_print_glyph( win, 0, NO_GLYPH );
		    amii_curs( win, 1, disprow - start - 1 );
		}
		Text( rp, "+", 1 );
		col = 1;
	    }
	}

	if( cw->type == NHW_MESSAGE )
	{
	    SetAPen( rp, amii_msgBPen );
	    SetBPen( rp, amii_msgBPen );
	}
	else if( cw->type == NHW_MENU )
	{
	    SetAPen( rp, amii_menuBPen );
	    SetBPen( rp, amii_menuBPen );
	}
	else if( cw->type == NHW_TEXT )
	{
	    SetAPen( rp, amii_textBPen );
	    SetBPen( rp, amii_textBPen );
	}
 	amii_cl_end( cw, col );
	whichcolor = -1;
	if( mip ) mip = mip->next;
    }
    RefreshWindowFrame(w);
    return;
}

void SetPropInfo( win, gad, vis, total, top )
    register struct Window *win;
    register struct Gadget *gad;
    register long vis, total, top;
{
    long mflags;
    register long hidden;
    register int body, pot;

    hidden = max( total-vis, 0 );

    /* Force the last section to be just to the bottom */

    if( top > hidden )
	top = hidden;

    /* Scale the body position. */
    /* 2 lines overlap */

    if( hidden > 0 && total > 2)
	body = (ULONG) ((vis - 2) * MAXBODY) / (total - 2);
    else
	body = MAXBODY;

    if( hidden > 0 )
	pot = (ULONG) (top * MAXPOT) / hidden;
    else
	pot = 0;

    mflags = AUTOKNOB|FREEVERT;
#ifdef  INTUI_NEW_LOOK
    if( IntuitionBase->LibNode.lib_Version >= 37 )
    {
	mflags |= PROPNEWLOOK;
    }
#endif

    NewModifyProp( gad, win, NULL,
			    mflags, 0, pot, MAXBODY, body, 1 );
}
