/***************************************************************
* regGadget.c 
*
* Creates and maintains the custom register display gadget
*
***************************************************************/
 
#include <strings.h>
#include <stdio.h>
#include <stdlib.h>
#include <libraries/guifront.h>
#include <intuition/gadgetclass.h>
#include <exec/memory.h>
#include <clib/graphics_protos.h>
#include <clib/exec_protos.h>
#include <clib/diskfont_protos.h>
#include <intuition/screens.h>
#include <clib/intuition_protos.h>
#include <graphics/gfxmacros.h> 
#include <graphics/gfxbase.h>

#include "amiCheck.h"
#include "mainPanel.h"
#include "regGadget.h"
#include "dataBase.h"

extern struct GfxBase *GfxBase;

#define WaitBlit();

#define RTEST(x)        {if ((x)==FALSE) return FALSE;}

#define AREA_SIZE       200
#define NUMCOLS         7
#define COLBUFFER       4       /* pixels on either side */

/*
 * globals and typedefs
 */
BOOL editDisable = TRUE;
 
/* typedef for register row */
typedef struct {
        ULONG rpY;      /* start of row */
        ULONG height;   /* height of row */ 
        ULONG fontY;    /* distance from top for text */
} regRowType;

/* typedef for register column */
typedef struct {
        ULONG rpX;      /* start of column */
        ULONG width;    /* width of column */
        ULONG fontX;    /* distance from start for text */
} regColType;

/* typedef for register cell */
typedef struct {
        regRowType              *cellRow;
        regColType              *cellCol;
} regCellType;

/* tool to index regGrid */
#define REGGRID(r,c)   regGrid[(r)*numCols + (c)]


regColType  registerCols[NUMCOLS];
regRowType  *registerRows = NULL;
regCellType *regGrid = NULL;
filterNode  *regTopNode = NULL;

char *regTypeArray[3] = {
	"Check",
	"Withdrawal",
	"Deposit",
};
long regPrintChars[3];
long regPrintFlags[3];

/* vars for our geometry */
ULONG regWidth, regHeight;
ULONG regTop, regLeft;
ULONG numRows;               /* number of items that fit */
ULONG numCols = NUMCOLS;
ULONG entryHeight;           /* pixels of entry */
ULONG depth;

extern GadgetSpec gadgetspecs[]; 

/* vars for our working RP */
struct RastPort workRP, *activeRP = NULL;
chip struct BitMap   workbuffer;
chip struct AreaInfo workarea;
chip struct TmpRas   worktmp;
chip void *tmprp = NULL;
chip UWORD areaBuffer[AREA_SIZE];

/* vars for our header RP */
struct RastPort headerRP;
chip struct BitMap   headerbuffer;
chip struct AreaInfo headerarea;
chip struct TmpRas   headertmp;
chip void *headertmprp = NULL;
chip UWORD headerareaBuffer[AREA_SIZE];
ULONG hWidth,hHeight,hTop,hLeft;

/* vars for our FONT */
struct TextFont *regFont = NULL;

/* misc control vars */
ULONG selRow = -1;      /* selected row */
ULONG regValidRows;        /* number of valid rows from top */
ULONG regTopRow;

/* lets remember the register window! */
struct Window *guiWindow = NULL;

/* special register config var */
BOOL showMemo = FALSE;

/*
 * Configurations!
 */

/* headers for register */
char *headers[] = {
        "Type",
        "Date",
        "Description",
        "V",
        "C",
	"T",
        "$ Amount",
};


/* 
 * local prototypes 
 */
BOOL RegRPInit(void);
BOOL RegLayout(void);
BOOL RegAllocHeader(void);
void RegRenderGrid(void);
void RegRenderHeader(void);

void RegDeselect(BOOL);
void RegScrollSetup(ULONG,ULONG,ULONG);
void RegFillRow(filterNode *, WORD);
void RegScrollListUp(void);
void RegLastPage(void);

/*
 * routines
 */

/*************************************************************
* RegSwapMemo()
*
*	Swaps memo in register
**************************************************************/
void RegSwapMemo(void)
{
 long entries, lastselRow, lasttop;

 /* preserve some globals */
 lasttop = regTopRow;
 lastselRow = selRow;
 entries = entryCount;

 AmiLock();
  if (showMemo)
	showMemo = FALSE;
 else	showMemo = TRUE;

 /* free up arrays */
 if (registerRows != NULL) {
        FreeVec(registerRows);
 }

 if (regGrid != NULL) {
        FreeVec(regGrid);
 }
        
 RegLayout();

 entryCount = entries;
 if (entryCount > numRows)
	regValidRows = numRows;
 else	regValidRows = entryCount;

 RegClearGrid();

 selRow = lastselRow;
 if (entryCount > numRows && entryCount-lasttop < regValidRows)
	lasttop = entryCount - regValidRows;

 if (selRow != -1)
	RegMoveToSel(&filtered);
 else	RegMoveList(&filtered,lasttop);

 RegRefresh(FALSE);
 RegScrollSetup(regTopRow,entryCount,regValidRows);
 AmiUnlock();
}

/**************************************************************
* RegInit()
*
* Initialize the register gadget
*
**************************************************************/
BOOL RegInit(void)
{
int x;

 /* build structures */       
 RTEST(RegRPInit());
 RTEST(RegLayout());
 RTEST(RegAllocHeader());

 /* create default imagery */
 RegRenderHeader();
 RegClearGrid();

 /* set up scroller */
 RegScrollSetup(0,0,0);
 regTopRow = regValidRows = 0;
 
 RegRefresh(TRUE);

 for (x=0;x<3;x++) {
	regPrintFlags[x] = regPrintChars[x] = -1;
 }
	  
 return (TRUE);    
} 

/**************************************************************
* RegAllocHeader()
*
*       Allocates RP and BitMap for header.  Must be done
*       _after_ reglayout() has selected and opened the font.
*
***************************************************************/
BOOL RegAllocHeader(void)
{
 ULONG x;

 hTop = AC_MainSpecs[GID_REGHEADER]->gs_Gadget->TopEdge + 1;
 hLeft = AC_MainSpecs[GID_REGHEADER]->gs_Gadget->LeftEdge + 2;
 hWidth =  AC_MainSpecs[GID_REGHEADER]->gs_Gadget->Width - 4;
 hHeight=  AC_MainSpecs[GID_REGHEADER]->gs_Gadget->Height - 2;
 
 /* create bitmap */
 InitBitMap(&headerbuffer,depth, hWidth,hHeight);

 for (x=0;x<depth;x++) {
        headerbuffer.Planes[x] = (PLANEPTR)AllocRaster(hWidth+1,hHeight+1);
        if (headerbuffer.Planes[x] == NULL) return (FALSE);
 }

 /* create area buffer */
 for (x=0;x<AREA_SIZE;x++) headerareaBuffer[x] =0;
 InitArea(&headerarea, headerareaBuffer, (AREA_SIZE*2)/5);

 /* create TmpRas */
 headertmprp =  (void *)AllocVec((hWidth+1)*(hHeight+1), MEMF_CHIP);
 if (headertmprp == NULL) return (FALSE);
 
 InitTmpRas(&headertmp,headertmprp,(hWidth+1)*(hHeight+1));

 /* link in to our work rastport */
 InitRastPort(&headerRP);
 headerRP.BitMap = &headerbuffer;
 headerRP.AreaInfo = &headerarea;
 headerRP.TmpRas = &headertmp;
 headerRP.Layer = NULL;

 /* initialize our pen colors */
 SetAPen(&headerRP,DETAILPEN);
  
 /* fill area with color */
 SetRast(&headerRP,DETAILPEN);
/*
 RectFill(&headerRP,0,0,hWidth,hHeight);
*/

 SetFont(&headerRP,regFont);
         
 return (TRUE);
}

/**************************************************************
* RegRenderHeader()
*
*       Renders the header titles above the register
*
***************************************************************/
void RegRenderHeader(void)
{
 ULONG x, top;
 
 /* set pen color */
 SetAPen(&headerRP,BLOCKPEN);
 
 top = regFont->tf_Baseline;
   
 if (regFont->tf_YSize <= hHeight) {
 	for (x=0;x<numCols;x++) {
       		Move(&headerRP,REGGRID(0,x).cellCol->rpX + COLBUFFER,top);
		Text(&headerRP,headers[x],strlen(headers[x]));
 	}
 }

}

/**************************************************************
* RegRPInit()
*
* initialize our playground 
*
**************************************************************/
BOOL RegRPInit()
{
UWORD x;

 /* define geometry (-2 accounts for shadowing border) */
 regWidth =  AC_MainSpecs[GID_REGFIELD]->gs_Gadget->Width - 4;
 regHeight=  AC_MainSpecs[GID_REGFIELD]->gs_Gadget->Height - 2;
 regTop   =  AC_MainSpecs[GID_REGFIELD]->gs_Gadget->TopEdge + 1;
 regLeft  =  AC_MainSpecs[GID_REGFIELD]->gs_Gadget->LeftEdge + 2;

 /* the one and only delicate hack of our program */
 AC_MainSpecs[GID_REGFIELD]->gs_Gadget->Activation |= GACT_IMMEDIATE;

 /* 
  * extract information about our main window 
  */
 
   
 GF_GetGUIAttr(gui, GUI_Window, &guiWindow, TAG_DONE);

 activeRP = guiWindow->RPort;
 depth = activeRP->BitMap->Depth;  
            
 /* 
  * create working rast port area 
  */
 
 /* create bitmap */
 InitBitMap(&workbuffer,depth, regWidth, regHeight);

 for (x=0;x<depth;x++) {
        workbuffer.Planes[x] = (PLANEPTR)AllocRaster(regWidth+1, 
                                        regHeight+1);
        if (workbuffer.Planes[x] == NULL) return (FALSE);
 }

 /* create area buffer */
 for (x=0;x<AREA_SIZE;x++) areaBuffer[x] =0;
 InitArea(&workarea, areaBuffer, (AREA_SIZE*2)/5);

 /* create TmpRas */
 tmprp = (void *)AllocVec((regWidth+1)*(regHeight+1), MEMF_CHIP);
 if (tmprp == NULL) return (FALSE);
 
 InitTmpRas(&worktmp,tmprp,(regWidth+1)*(regHeight+1));

 /* link in to our work rastport */
 InitRastPort(&workRP);
 workRP.BitMap = &workbuffer;
 workRP.AreaInfo = &workarea;
 workRP.TmpRas = &worktmp;
 workRP.Layer = NULL;
 
 return (TRUE);
}

/*************************************************************
* RegLayout()
*
* calculates the number of possible entries, the starting
* position of each item.
*
**************************************************************/
BOOL RegLayout(void)
{
struct TextAttr regAttr;
UWORD regFontHeight;
#if 0
char regFontName[50];
BOOL regScreen;
#endif
ULONG height;
int x,temp,desc;
char *colFixes[NUMCOLS] = {
        "Withdrawal",
        "DEC000000", 
        "",
        "V",
        "C",
	"T",
        "000000000.00",
};


 GF_GetGUIAttr(gui,
        GUI_ActualFont,&regAttr,
        TAG_DONE);
        

#if 0
 /* grab information from guifront on fonts */
 GF_GetPrefsAttr(gui,
        PRF_GadgetScreenFont,&regAttr,
        TAG_DONE);
        
 /* are we using the screen font? */
 if (regScreen)
        regAttr = guiWindow->WScreen->Font;
 else {
        if ( GF_GetPrefsAttr(gui,
                PRF_GadgetFontYSize, &regFontHeight,
                PRF_GadgetFontName,  regFontName,
                TAG_DONE) != 2) {

		regAttr = guiWindow->WScreen->Font;
	}
	else  regAttr = guiWindow->WScreen->Font;
 }

#endif

 /* at last, open the damn thing */
 regFont = OpenDiskFont(&regAttr);
 if (regFont == NULL) return (FALSE);
 
 SetFont(&workRP,regFont);  
 
 /* calculate entry separators */
 height = regFont->tf_YSize + regFont->tf_YSize/8 + 1;
 /* * * * */  
 if (showMemo) height *= 2;

 numRows = regHeight/height;

 /* tweek height to absorb excess space - numRows is same */
 entryHeight = height + (regHeight%height)/numRows;
 temp = regHeight%entryHeight;

 /* install rows */
 registerRows =  (regRowType *)AllocVec(sizeof(regRowType)*numRows,
                                        MEMF_ANY); 
 if (registerRows == NULL) return (FALSE);

 height = (entryHeight / 2) - (regFont->tf_YSize/2) +
           regFont->tf_Baseline;


#if 0
 registerRows[0].fontY = height + 1;
 registerRows[0].rpY = 0; /*entryHeight + ((temp>0)?1:0);*/
 registerRows[0].height = entryHeight + ((temp>0)?1:0);
 
 for (x=1;x<numRows;x++) {
	
       	registerRows[x].fontY = height + 1;
	registerRows[x].height = entryHeight + ((x<temp)?1:0);
        registerRows[x].rpY = registerRows[x-1].rpY + registerRows[x-1].height;
 }
 
#endif
 registerRows[0].fontY = height + 1;
 registerRows[0].rpY = 0;
 registerRows[0].height = entryHeight; /* should never get extra line... cest la vi! */

 for (x=1;x<numRows;x++) {
	
       	registerRows[x].fontY = height + 1;
	registerRows[x].height = entryHeight + ( (x > numRows-temp )?1:0);
        registerRows[x].rpY = registerRows[x-1].rpY + registerRows[x-1].height;
 }

 /* install cols */
 temp = desc = 0;
 for (x=0;x<numCols;x++) {
        registerCols[x].fontX = COLBUFFER;
        if (x == 0) registerCols[x].rpX = 0;
        else        registerCols[x].rpX = registerCols[x-1].rpX +
                                          registerCols[x-1].width + 1;

        /* need to give description all extra space */
        if (strlen(colFixes[x]) == 0) {
		desc = x;
		registerCols[x].width = 0;
	}
        else {
                registerCols[x].width = TextLength(&workRP,
                                   colFixes[x],strlen(colFixes[x])) +
                                COLBUFFER*2 ;
                                
                temp += registerCols[x].width;
        }                             
 }  

 /* now fix for description list */
 registerCols[desc].width = regWidth - temp;
 for (x=desc+1;x<numCols;x++) {
        registerCols[x].rpX += registerCols[desc].width; 
 }
 
 /* now build the cell grid */
 regGrid = (regCellType *)AllocVec(sizeof(regCellType)*numCols*numRows,
                               MEMF_ANY);
 if (regGrid == NULL) return (FALSE);
 
 for (x=0;x<numRows;x++) {
        for (temp=0;temp<numCols;temp++) {
                REGGRID(x,temp).cellRow = &registerRows[x];
                REGGRID(x,temp).cellCol = &registerCols[temp];
        }
 }       
 
 return (TRUE);
}

/*************************************************************
* RegRenderCellFlush()
*
*	Flush the text (right justify)
**************************************************************/
void RegRenderCellFlush(ULONG row, ULONG col, char *str, char pen,
long *printchars)
{
  ULONG x,y,b, width, length;
  struct TextExtent resultExtent;
  
  /* set pen */
  SetAPen(&workRP,pen);
  
  /* quick pull out some info on cell */
  y = REGGRID(row,col).cellRow->rpY;
  b = REGGRID(row,col).cellRow->fontY;
  x = REGGRID(row,col).cellCol->rpX + REGGRID(row,col).cellCol->fontX;
  width = REGGRID(row,col).cellCol->width - COLBUFFER;
  
  /* clip str to make fit... */
  if (*printchars == -1)
    *printchars = TextFit(&workRP,str,strlen(str),&resultExtent,NULL,1,
        REGGRID(row,col).cellCol->width-(COLBUFFER*3) + 1,
        REGGRID(row,col).cellRow->height );

  length = TextLength(&workRP,str,*printchars);
  if (length < width-(COLBUFFER*3)+1)
	x += (width-(COLBUFFER*3)+1) - length;

  /* stuf it in */
  Move(&workRP,x,y+b);
  Text(&workRP,str, *printchars);

  if (row == numRows-1) {
	SetAPen(&workRP,TEXTPEN);
	Move(&workRP,1,regHeight-1);
	Draw(&workRP,regWidth-1,regHeight-1);
  }
}

/*************************************************************
* RegRenderCell()
*
*       Renders string contents into a given cell. Does
*       necessary clipping of the string.
*
***************************************************************/
void RegRenderCell(ULONG row, ULONG col, char *str, char pen, 
long *printchars, BOOL memo)
{
  ULONG x,y,b, width;
  struct TextExtent resultExtent;
  
  /* set pen */
  SetAPen(&workRP,pen);

  /* quick pull out some info on cell */
  y = REGGRID(row,col).cellRow->rpY;
  b = REGGRID(row,col).cellRow->fontY;
  x = REGGRID(row,col).cellCol->rpX + REGGRID(row,col).cellCol->fontX;
  width = REGGRID(row,col).cellCol->width - COLBUFFER;

  /* clip str to make fit... */
  if (*printchars == -1) {
    *printchars = TextFit(&workRP,str,strlen(str),&resultExtent,NULL,1,
        REGGRID(row,col).cellCol->width-(COLBUFFER*2) + 1,
        REGGRID(row,col).cellRow->height );
  }

  /* special case description/memo stuff */
  if (showMemo == TRUE && col == 2 ) {
	if (memo == FALSE) {

 b = (entryHeight / 4) - (regFont->tf_YSize/2) +
           regFont->tf_Baseline;

/*		b -= (regFont->tf_YSize/2);*/
	}
	else {
		if (*printchars > strlen(str))
			*printchars = strlen(str);

 b = (entryHeight ) - (regFont->tf_YSize/2) + 1 /* -
           regFont->tf_Baseline*/;

/*		b  += (regFont->tf_YSize/2);*/
	}
  }
  
  /* stuf it in */
  Move(&workRP,x,y+b);
  Text(&workRP,str,*printchars);

  if (row == numRows-1) {
	SetAPen(&workRP,TEXTPEN);
	Move(&workRP,1,regHeight-1);
	Draw(&workRP,regWidth-1,regHeight-1);
  }

}

/*************************************************************
* RegRenderGrid()
*
* Renders the register grid.  Simple enough...
*
**************************************************************/
void RegRenderGrid(void)
{
 int x;

 /* set our pens */
 SetAPen(&workRP,BLOCKPEN);
 
 /* horizontals */ 
 for (x=0;x<numRows;x++) {
        Move(&workRP,0,registerRows[x].rpY);
        Draw(&workRP,regWidth,registerRows[x].rpY);

 	if (showMemo) {
		SetDrPt(&workRP,0xAAAA);
		Move(&workRP,registerCols[2].rpX,registerRows[x].rpY + entryHeight/2);
        	Draw(&workRP,registerCols[3].rpX,registerRows[x].rpY + entryHeight/2);
		SetDrPt(&workRP,~0);

		if (x != 0 && x < numRows){
        		Move(&workRP,0,registerRows[x].rpY-1);
        		Draw(&workRP,regWidth,registerRows[x].rpY-1);
		}

 	}
 }
 
 /* verticals */
 for (x=0;x<numCols;x++) {
        Move(&workRP,registerCols[x].rpX,0);
        Draw(&workRP,registerCols[x].rpX,regHeight);
 }

 /* set highlight pen */
 SetAPen(&workRP,TEXTPEN);

 /* put depth into our register window */
 Move(&workRP,1,regHeight-1);
 Draw(&workRP,regWidth,regHeight-1);
 Move(&workRP,regWidth-1,1);
 Draw(&workRP,regWidth-1,regHeight);
}

/*************************************************************
* RegRefresh()
*
* Blits the work area into the window
*
**************************************************************/
void RegRefresh(BOOL header)
{

#if 0  
 /* blit over the working register bitmap into window */
 ClipBlit(&workRP,0,0,activeRP,regLeft,regTop,
        regWidth,regHeight,(ULONG)0xc0);
#endif


/*
BltBitMapRastPort(workRP.BitMap,0,0,activeRP,regLeft,regTop,
        regWidth,regHeight,0xc0);
WaitBlit();*/

ClipBlit(&workRP,0,0,activeRP,regLeft,regTop,regWidth,regHeight,(ULONG)0xc0);

if (header) {
	/*
	BltBitMapRastPort(headerRP.BitMap,0,0,activeRP,hLeft,hTop,
        	hWidth,hHeight,0xc0);
	WaitBlit();*/

	ClipBlit(&headerRP,0,0,activeRP,hLeft,hTop,hWidth,hHeight,(ULONG)0xc0);
}
}

/*************************************************************
* RegSelect()
*
*       marks a row as being selected.  
*
**************************************************************/
void RegSelect(ULONG row, BOOL towin)
{
BYTE oldmode, oldmask;
ULONG height, top;

/* is it currently selected? */
if (selRow == regTopRow + row) return;

/* remove old one */
RegDeselect(towin);

height = registerRows[row].height - 2;
top = registerRows[row].rpY + 1;

/* render it to the actual window first (avoids flicker later) */ 
if (towin) {
	oldmode = activeRP->DrawMode;
	oldmask = activeRP->Mask;
	SetDrMd(activeRP,COMPLEMENT);
	SafeSetWriteMask(activeRP,0x3);

	RectFill(activeRP,
        	1 + regLeft, top + regTop,
        	regWidth - 2 + regLeft,top + height + regTop );

	WaitBlit();

	SetDrMd(activeRP,oldmode);
	SafeSetWriteMask(activeRP,oldmask);
}

/* now render it "for real" so refreshes will see it */
oldmode = workRP.DrawMode;
oldmask = workRP.Mask;
SetDrMd(&workRP,COMPLEMENT);
SafeSetWriteMask(&workRP,0x3);

RectFill(&workRP,
        1, top,
        regWidth-2,top + height );

WaitBlit();

SetDrMd(&workRP,oldmode);
SafeSetWriteMask(&workRP,oldmask);

/* remember this selection for later */
selRow = regTopRow + row;
}

/*************************************************************
* RegDeselect()
*
*       clears selection of a row.
*
**************************************************************/
void RegDeselect(BOOL towin)
{
 BYTE oldmode, oldmask;
 ULONG height,top, row = selRow;

 if (selRow == -1) return;
 selRow = -1;

 /* if not visible, skip graphical rendering */
 if (row < regTopRow || row > regTopRow+numRows-1)
		return; 

 /* make zero based */
 row -= regTopRow; 

 height = registerRows[row].height - 2;
 top = registerRows[row].rpY + 1;
 
 if (towin) {
 	oldmode = activeRP->DrawMode;
 	oldmask = activeRP->Mask;
	SetDrMd(activeRP,COMPLEMENT);
 	SafeSetWriteMask(activeRP,0x3);

 	RectFill(activeRP,
        	1+regLeft, top + regTop,
        	regWidth-2+regLeft,top + height + regTop );

 	WaitBlit();

 	SetDrMd(activeRP,oldmode);
 	SafeSetWriteMask(activeRP,oldmask);
 }

 oldmode = workRP.DrawMode;
 oldmask = workRP.Mask;
 SetDrMd(&workRP,COMPLEMENT);
 SafeSetWriteMask(&workRP,0x3);

 RectFill(&workRP,
        1,  top,
        regWidth-2,top + height );

 WaitBlit();

 SetDrMd(&workRP,oldmode);
 SafeSetWriteMask(&workRP,oldmask);         
}

/*************************************************************
* RegFreeAll()
*
* Free up ALL register gadget resources (not linked lists).
* A small attempt is done to do this safely (checks for NULL)
*
**************************************************************/
void RegFreeAll(void)
{
int x;

 /* free up arrays */
 if (registerRows != NULL) {
        FreeVec(registerRows);
 }
        
 if (regGrid != NULL) {
        FreeVec(regGrid);
 }
        
  
 /*
  * workRP stuff
  */
       
 /* free up bitmap */
 for (x=0;x<depth;x++)
        if (workbuffer.Planes[x] != NULL) {
                 FreeRaster(workbuffer.Planes[x],regWidth+1,regHeight+1);  
        }
        
 /* free TmpRas */
 if (tmprp != NULL) {
        FreeVec(tmprp);
 }

 /*
  * headerRP stuff
  */
  
 /* free up bitmap */
 for (x=0;x<depth;x++)
       if (headerbuffer.Planes[x] != NULL) { 
                FreeRaster(headerbuffer.Planes[x],hWidth+1,hHeight+1);  
        }
        
 /* free TmpRas */
 if (headertmprp != NULL) {
          FreeVec(headertmprp);
 }

 /*
  * misc 
  */
  
 /* free font */
 if (regFont != NULL) {
          CloseFont(regFont);
 }

}

/****************************************************************
* RegHitTest()
*
*       Takes a mouse hit and determines what to do.
*
****************************************************************/
BOOL RegHitTest(ULONG Micros, ULONG Seconds, ULONG x, ULONG y)
{
 ULONG t,row;
 static LONG lastrow = -1;
 BOOL retval = FALSE;
 static ULONG micros = 0, secs = 0;

 
 /* quick clip of register gadget */
 if (x < regLeft || x > regWidth+regLeft ||
     y < regTop  || y > regHeight+regTop)
     
     return (FALSE);
 
 /* calc row */
 y -= regTop;
 row = 0;
 for (t=0;t<numRows-1;t++) {
	if (y > registerRows[t].rpY +registerRows[t].height)
		row++;

	else	break;
 }
 
 /* ok, is it a valid row? */
 if (row < regValidRows) {
        RegSelect(row, TRUE);
	RegEditDisable(FALSE);
	
	if (DoubleClick(secs,micros,Seconds,Micros) && lastrow == row)
		retval = TRUE;
 }
 
 micros = Micros;
 secs = Seconds;
 lastrow = row;

 AmiMoveSwap();
 return (retval);
}

/****************************************************************
* RegClearGrid()
*
*       Clears the register and then draws the grid.
*
*****************************************************************/
void RegClearGrid()
{
 /*SetAPen(&workRP,DETAILPEN);*/

 /* fill area with color */
/*
 RectFill(&workRP,0,0,regWidth,regHeight);
*/
 SetRast(&workRP,DETAILPEN);

 /* draw register grid */        
 RegRenderGrid();
}

/**************************************************************
* RegScrollSetup()
*
*	Sets the scroller correctly
***************************************************************/
void RegScrollSetup(ULONG top,ULONG total,ULONG visible)
{
 GF_SetGadgetAttrs(gui,AC_MainSpecs[GID_REGVSCROL]->gs_Gadget,
	GTSC_Top,top,
	GTSC_Total, total,
	GTSC_Visible,visible,
	TAG_DONE);
}

/************************************************************
* RegInsertEntry()
*
*	Add entry to list
*************************************************************/
void RegInsertEntry(UWORD row, filterNode *en)
{
	entryCount++;

	if (regValidRows < numRows) {
		if (regTopNode == NULL)
			regTopNode = en;

		regValidRows++;
	}
	
	if (row >= regTopRow && row < regTopRow+regValidRows) {
		RegMoveList(&filtered,regTopRow);
		RegRefresh(FALSE);
	}

	RegScrollSetup(regTopRow,entryCount,regValidRows);
	RegEditDisable(TRUE);
}

/************************************************************
* RegAddEntry()
*
*	Add entry to list
*************************************************************/
void RegAddEntry(filterNode *en)
{
	entryCount++;

	if (regValidRows < numRows) {
		RegDeselect(TRUE);
		if (regTopNode == NULL)
			regTopNode = en;

		regValidRows++;
		RegFillRow(en,regValidRows-1);
	}
	else {
		/* remove curr selection */
		selRow = -1;
		RegMoveList(&filtered,entryCount-regValidRows);
		RegRefresh(FALSE);
	}

	RegScrollSetup(regTopRow,entryCount,regValidRows);
	RegRefresh(FALSE);
	RegEditDisable(TRUE);
}

/************************************************************
* RegKeyScroll()
*
*	scrolls selected item on keyboard commands
*************************************************************/
void RegKeyScroll(UWORD code)
{
 ULONG currRow;

 if (entryCount == 0)
	return;

  /* is the selected area off screen? */
 if (selRow != -1 && (selRow < regTopRow || selRow >= regTopRow+numRows) ) {
	RegMoveToSel(&filtered);
 }

 currRow = selRow;

 switch (code) {
	case UP:
		if (currRow == 0)
			return;

		/* are we not selected? */
		if (currRow == -1)
			currRow = regTopRow+1;
		
		if (currRow == regTopRow) {
			selRow = -1;
			RegScrollListDown();
			RegRefresh(FALSE);
		}
		else RegDeselect(TRUE);
	
		currRow--;		
		RegSelect(currRow-regTopRow, TRUE);
		break;

	case SHIFTUP:
		/* is there a current selection? */
		if (currRow == -1) {
			RegSelect(0,TRUE);
		}

		/* are we already near the top? */
		else if (regTopRow < regValidRows) {
			if (regTopRow == 0)
				RegDeselect(TRUE);
			else	selRow = -1;

			RegMoveList(&filtered,0);
			RegRefresh(FALSE);
			RegSelect(0, TRUE);
		}
		
		/* then just scroll there */
		else {	
			selRow = -1;
			currRow -= regTopRow;
			RegMoveList(&filtered,regTopRow-regValidRows+1);
			RegRefresh(FALSE);
			RegSelect(currRow, TRUE);
		}
		break;

	case CTRLUP:
		if (currRow == 0) 
			return;

		/* remove current selection */
		if (regTopRow == 0)
			RegDeselect(TRUE);
		else {
			selRow = -1;
			RegMoveList(&filtered,0);
			RegRefresh(FALSE);
		}

		/* grab top */
		RegSelect(0, TRUE);
		break;

	case DOWN:
		if (currRow == entryCount-1) {
			return;
		}

		/* are we selected? */
		if (currRow == -1) {
			currRow = regTopRow-1;
		}
		
		/* scroll down or jump down */
		if (currRow == regTopRow + regValidRows - 1) {
			selRow = -1;
			RegScrollListUp();
			RegRefresh(FALSE);
		}
		else {
			RegDeselect(TRUE);
		}

		WaitBlit();
		currRow++;	
		RegSelect(currRow-regTopRow, TRUE);
		break;

	case SHIFTDOWN:
		/* is there a current selection? */
		if (currRow == -1) {
			RegSelect(0, TRUE);
		}

		/* are there not enough entries to page down? */
		else if (entryCount - (regTopRow + regValidRows-1) <= regValidRows) {
			if (regTopRow == entryCount-regValidRows)
				RegDeselect(TRUE);
			else selRow = -1;

			RegMoveList(&filtered,entryCount-regValidRows);
			RegRefresh(FALSE);
			RegSelect(regValidRows-1, TRUE);
		}

		/* just scroll there */
		else {	
			selRow = -1;
			currRow -= regTopRow;
			RegMoveList(&filtered,regTopRow+regValidRows-1);
			RegRefresh(FALSE);
			RegSelect(currRow, TRUE);
		}
		break;

	case CTRLDOWN:
		if (selRow == entryCount-1)
			return;

		/* remove curr selection */
		if (regTopRow == entryCount - regValidRows) 
			RegDeselect(TRUE);
		else {
			selRow = -1;
			/*RegMoveList(&filtered,entryCount-regValidRows); */
			RegLastPage();
			RegRefresh(FALSE);
		}


		/* grab bottom */
		RegSelect(regValidRows-1, TRUE);
		break;
 }

 RegScrollSetup(regTopRow, entryCount, regValidRows);
 RegEditDisable(FALSE);
}

/************************************************************
* void RegEditDisable()
*
*	Enable/disable the edit gadget
*************************************************************/
void RegEditDisable(BOOL ok)
{
 /*
 if (editDisable != ok) 
	AmiDisableSel(ok);
  	editDisable = ok;
 }
 */

 AmiDisableSel(ok);
}

/************************************************************
* RegRedrawRow()
*
*	Updates alternate row
*************************************************************/
void RegRedrawRow(filterNode *en)
{
 ULONG height,top,row;
 UBYTE oldmode, oldmask;

 row = DataFindNode((struct List *)&filtered,(struct Node *)en);
 if (row < regTopRow || row >= regTopRow + numRows)
	return;

 row -= regTopRow;
 height = registerRows[row].height - 2;
 top = registerRows[row].rpY + 1;

 SetAPen(&workRP,DETAILPEN);

 /* erase row */
 RectFill(&workRP,
        1,  top,
        regWidth-2,top + height );

 WaitBlit();

 /* fill in the row */
 RegFillRow(en,row);

 /* now redraw the grid */
 RegRenderGrid();

 /* now select locally */
 oldmode = workRP.DrawMode;
 oldmask = workRP.Mask;

 /* update display */
 RegRefresh(FALSE);
}

/************************************************************
* RegRedrawSel()
*
*	Updates the selected row
*************************************************************/
void RegRedrawSel(filterNode *fn)
{
 int x;
 ULONG height,top,row;
 UBYTE oldmode, oldmask;

 row = selRow-regTopRow;
 height = registerRows[row].height - 2;
 top = registerRows[row].rpY + 1;

 SetAPen(&workRP,DETAILPEN);

 /* erase row */
 RectFill(&workRP,
        1,  top,
        regWidth-2,top + height );

 WaitBlit();

 /* fill in the row */
 for (x=0;x<4;x++)
	fn->printChars[x]= -1;
 RegFillRow(fn,row);

 /* now redraw the grid */
 RegRenderGrid();

 /* now select locally */
 oldmode = workRP.DrawMode;
 oldmask = workRP.Mask;
 SetDrMd(&workRP,COMPLEMENT);
 SafeSetWriteMask(&workRP,0x3);

 RectFill(&workRP,
        1,  top,
        regWidth-2,top + height );

 SetDrMd(&workRP,oldmode);
 SafeSetWriteMask(&workRP,oldmask);

 /* update display */
 RegRefresh(FALSE);
}

/************************************************************
* RegMoveList()
*
*	Moves visible list area to new top
*************************************************************/
void RegMoveList(struct MinList *filters, UWORD top)
{
 ULONG x, row;
 filterNode *ft = (filterNode *)filters->mlh_Head;

 /* empty list? */
 if (filters->mlh_TailPred == filters) {
	RegClearGrid();
	RegRenderGrid();
	return;
 }

#if 0
 /* are we already there? */
 if (top == regTopRow) 
	return;
#endif

 if (top > entryCount - regValidRows) {
	/*printf("WHOA! top = %d\n",top);*/
	return;
 }

 /* deselect item */
 row = selRow;
 selRow = -1;
 
 /* obtain new top */
 for (x=0;x<top;x++) 
	ft = (filterNode *)ft->node.mln_Succ;

 regTopRow = top;
 regTopNode = ft;
 
 /* now render */
 RegClearGrid();
 for (x=0;x<regValidRows; x++) {
	RegFillRow(ft,x);
	ft = (filterNode *)ft->node.mln_Succ;
 }
 RegRenderGrid();

 /* select item again*/
 if (row >= regTopRow && row <= regTopRow + regValidRows-1) {
	/*printf("selecting!\n");*/
	RegSelect(row-regTopRow, FALSE);
  }
 else selRow = row;
}

/************************************************************
* RegScrollListUp()
*
*	quick move of register up by size of one row 
*************************************************************/
void RegScrollListUp(void)
{
 WORD x;
 filterNode *filt;

 regTopRow++;
 
 filt=regTopNode = (filterNode *)regTopNode->node.mln_Succ;
  
 RegClearGrid();
 for (x=0;x < regValidRows; x++) {
	RegFillRow(filt,x);
	WaitBlit();
	filt = (filterNode *)filt->node.mln_Succ;
 }
 RegRenderGrid();

}

/************************************************************
* RegScrollListDown()
*
*	Quick scrolling of window down one
*************************************************************/
void RegScrollListDown(void)
{
 WORD x;
 filterNode *filt;

 /* are we at the top? */
 if (regTopRow == 0)
	return; 

 regTopRow--;
 filt=regTopNode = (filterNode *)regTopNode->node.mln_Pred;
 
 /* render */
 RegClearGrid();
 for (x=0;x <regValidRows; x++) {
	RegFillRow(filt,x);
	filt = (filterNode *)filt->node.mln_Succ;
 }
 RegRenderGrid();

}

/************************************************************
* RegFillRow()
*
*	Fill row with node data
*************************************************************/
void RegFillRow(filterNode *fn, WORD row)
{
 entryNode *en = fn->entry;
 char checktype[CHECKSIZE];
 char datestr[DATESIZE];
 char amntstr[AMNTSIZE];
 char textpen;
 long tempprint = -1;;

 /* now assumes row is blank */
 if (en->flags & VOIDED)
	textpen = SHINEPEN;
 else textpen = BLOCKPEN;

 /* fill in type */
 if (en->type != CHECKTYPE)
 	RegRenderCell(row,0,regTypeArray[en->type],textpen,&regPrintChars[en->type],FALSE);
 else {
	sprintf(checktype,"%06d",en->check);
	RegRenderCell(row,0,checktype,textpen, &fn->printChars[0],FALSE);
 }

 /* fill in date */
 DataBuildRegDate(&en->date,datestr);
 RegRenderCell(row,1,datestr,textpen, &fn->printChars[1],FALSE);

 /* fill in name */
 RegRenderCell(row,2,en->name,textpen, &fn->printChars[2],FALSE);

 /* memo? */
 if (showMemo)
	RegRenderCell(row,2,en->memo,textpen,&tempprint,TRUE);
	
 /* voided? */
 if (en->flags & VOIDED)
 	RegRenderCell(row,3,"V", textpen, &regPrintFlags[0],FALSE);

 /* Cleared/tax deductible?? */
 if (en->flags & CLEARED)
	RegRenderCell(row,4,"C",textpen, &regPrintFlags[1],FALSE);

 if (en->flags & TAXDEDUCT)
	RegRenderCell(row,5,"T",textpen, &regPrintFlags[2],FALSE);

 /* amount? */
 DataBuildAmnt(TRUE, &en->amount,amntstr);

 if (en->type == DEPOSITTYPE && !(en->flags & VOIDED) )
	textpen=TEXTPEN;

 /* right justify this val */
 RegRenderCellFlush(row,6,amntstr,textpen, &fn->printChars[3]);
 WaitBlit();
}

/********************************************************
* RegLastPage()
*
*	Shows last page of register
*********************************************************/
void RegLastPage(void)
{
 WORD x;
 filterNode *filt;

#if 0
 /* test to see if we have too few entries */
 if (entryCount <= regValidRows) {
	RegMoveList(&filtered,0);
	return;
 }
#endif

 /* clear display */
 RegClearGrid();

 /* work from bottom */
 if (filtered.mlh_TailPred != &filtered) {
 	filt = (filterNode *)filtered.mlh_TailPred;
	for (x=regValidRows;x > 0; x--) {
		RegFillRow(filt,x-1);
		regTopNode = filt;
		filt = (filterNode *)filt->node.mln_Pred;
 	}

  	regTopRow = entryCount - regValidRows;
 }

 RegRenderGrid();
}

/********************************************************
* RegRemoveSel()
*
*	Remove the filter entry of selected row,
*	redraw register
*********************************************************/
void RegRemoveSel(void)
{
 /*entryCount--;*/
 if (entryCount < regValidRows) {
	regValidRows--;
	selRow = -1;
	RegLastPage();
 }
 else {
	/* should we not move up? */
	if (regTopRow + regValidRows > entryCount)
		regTopRow--;

 	selRow = -1;
 	RegMoveList(&filtered,regTopRow);
 }
 
 RegRefresh(FALSE);
 RegScrollSetup(regTopRow, entryCount, regValidRows);
 RegEditDisable(TRUE);
}

/********************************************************
* RegNewFilter()
*
* 	Resets the filter in the register
*********************************************************/
void RegNewFilter(void)
{
 selRow = -1;
 regTopRow = 0;
 if (entryCount < numRows)
	regValidRows = entryCount;
 else regValidRows = numRows;

 RegLastPage();
 RegRefresh(FALSE);
 RegEditDisable(TRUE);
 RegScrollSetup(regTopRow, entryCount, regValidRows);
}

/*********************************************************
* RegResetOffsets()
*
*	Reset the printlength offsets
**********************************************************/
void RegResetOffsets(int type)
{
 filterNode *filt = NULL;

 while ( (filt = (filterNode *)DataGetNext((struct List *)&filtered,
	(struct Node *)filt)) != NULL) {
		filt->printChars[type] = -1;
 }
}

/*********************************************************
* RegRedrawFilter()
*
*	Resets filter, but maintains selected state if needed
**********************************************************/
void RegRedrawFilter(ULONG filtentries)
{
 /* handle any unexpected new entries too */
 if (filtentries > 0) {
	entryCount += filtentries;
	if (regValidRows < numRows) {
		if (numRows-regValidRows <= filtentries)
			regValidRows = numRows;
		else	regValidRows += filtentries;
	}
 }

 if (selRow != -1)
	RegMoveToSel(&filtered);
 else 	RegNewFilter();
}

/************************************************************
* RegMoveToSel()
*
*	Moves visible list area to show selected item in center
*************************************************************/
void RegMoveToSel(struct MinList *filters)
{
 ULONG top;

 /* empty list? */
 if (filters->mlh_TailPred == filters) {
	RegClearGrid();
	RegRenderGrid();
	return;
 }
 
 if (selRow == -1)
	return;

 /* calculate new top */
 if (selRow < numRows)
	top = 0; 			/* on first page */
 else if (selRow > entryCount - numRows)
	top = entryCount - numRows;	/* on last page */
 else if (selRow >= regTopRow && selRow < regTopRow+numRows)
	top = regTopRow;
 else top = selRow - numRows/2;		/* put sel in middle */
 
 RegMoveList(filters,top);
 RegScrollSetup(regTopRow, entryCount, regValidRows);
 RegRefresh(FALSE);
}
