/*****************************************************************************/
/*                           Jean-Eloi Dussartre                             */
/*                          M.I.T. Project Athena.                           */
/*                              January 1990.                                */
/*                   The Spreadsheet Widget implementation.                  */
/*                     The AtTable Object implementation.                    */
/*****************************************************************************/


/* Preliminaries remarks.
 * This file contains a library of functions for the Spreadsheet widgets. The
 * name of functions that are "public" (i.e. public to the Spreadsheet widget
 * program) start by AtSW. The name starting by AtSWI are ressources
 * initialization procedures. These are used upon initialization or upon
 * a change in argument.
 */


#define SpreadsheetL_C    /* Sign up in the preprocessor */

/* Include files */

#include <string.h>
#include <X11/Xatom.h>
#include <X11/X.h>
#include <At/FontFamily.h>


#include "SpreadsheetP.h"
#include "Spreadsheet.h"
#include "SpreadsheetL.h"


 /* AtSWI_GetColumnExposed function compute the number of visible items
  * according to the space available. This function is describe for a column
  * case (ie. computing the number of visible column), but the computation
  * is the same for a row case.
  * List of inputs :
  *    AtSpreadsheet s : The spreadsheet record.
  *    int           w : The width of the avaiable window.
  */


short int AtSWI_GetRightColumnExposed (AtSpreadsheet s, Dimension w)


{
  short int icol;
  Dimension w1;
  
  DBG_Trace(AtSWI_GetRightColumnExposed, "Entering..");
  /* Loop on the column up to the last column or the width remaining is < 0 */
  for (icol = s->leftColumn, w1 = GeomRwLOuterH(s);
       ((w >= w1) && (icol < s->nColumn));
       w1 += ( (! HiddenColumnP(s, icol))
              ? (GeomCoLInnerH(s, icol) + GeomLineWidth(s))
	      : 0),
       icol++);

  DBG_Print((stderr, "Ending on %d\n", icol));

  return (icol - 1);

}

short int AtSWI_GetLeftColumnExposed (AtSpreadsheet s, Dimension w)


{
  short int icol;
  Dimension w1;
  
  DBG_Trace(AtSWI_GetLeftColumnExposed, "Entering..");
  /* Loop on the column up to the last column or the width remaining is < 0 */
  for (icol = s->rightColumn, w1 = GeomRwLOuterH(s);
       ((w >= w1) && (icol >= 0));
       w1 += ( (! HiddenColumnP(s, icol))
              ? (GeomCoLInnerH(s, icol) + GeomLineWidth(s))
	      : 0),
       icol--);

  DBG_Print((stderr, "Ending on %d\n", icol));

  return (icol + 1);

}


short int AtSWI_GetBottomRowExposed (AtSpreadsheet s, Dimension h)

{
  short int irow;
  Dimension h1;
  
  DBG_Trace(AtSWI_GetBottomRowExposed, "Entering");
  
  for (irow = s->topRow, h1  = GeomCoLOuterV(s);
       ((h >= h1) && (irow < s->nRow));
       h1 += ( (! HiddenRowP(s, irow))
	      ? (GeomRwLInnerV(s, irow) + GeomLineWidth(s))
	      : 0),
       irow++);
  
  DBG_Print((stderr,"Ending on %d\n", irow));
  return (irow - 1);

}


short int AtSWI_GetTopRowExposed (AtSpreadsheet s, Dimension h)

{
  short int irow;
  Dimension h1;
  
  DBG_Trace(AtSWI_GetTopRowExposed, "Entering");
  
  for (irow = s->bottomRow, h1  = GeomCoLOuterV(s);
       ((h >= h1) && (irow >= 0));
       h1 += ( (! HiddenRowP(s, irow))
	      ? (GeomRwLInnerV(s, irow) + GeomLineWidth(s))
	      : 0),
       irow--);

  DBG_Print((stderr,"Ending on %d\n", irow));
  return (irow + 1);  
}


short int AtSW_GetRowIndex(AtSpreadsheet s, Dimension y)

{
  short irow;
  XSegment *Ys = s->segmentH + 2;

  if ((Ys - 1)->y1 > y)
    return(IDX_columnLabel);

  for (irow = s->topRow; (irow <= s->bottomRow); irow++, Ys++)
    if (Ys->y1 > y)
      return (irow);
  return (IDX_Outside);
}

short int AtSW_GetColumnIndex(AtSpreadsheet s, Dimension x)

{
  short icol;
  XSegment *Xs = s->segmentV + 2;

  if ((Xs - 1)->x1 > x)
    return (IDX_rowLabel);
    
  for (icol = s->leftColumn;  (icol <= s->rightColumn); icol++, Xs++)
    if (Xs->x1 > x)
      return (icol);
  return (IDX_Outside);
}


		   /****************************/
		   /* Ressource initialization */
		   /****************************/




/*
 * Eval the shift induce by a justification....
 */

int AtSWJustification(short flag, char *s, int sl, XFontStruct *font, int w)

{
  DBG_Trace(AtSWJustification, "Entering...");


  if (flag != AtSpreadsheetJustificationLEFT)
    {  
      int tw = XTextWidth(font, s, sl);  
      switch (flag)
	{
	case AtSpreadsheetJustificationRIGHT :
	     DBG_Trace(AtSWJustification, "Exiting...");
	     return (w - tw);
	case AtSpreadsheetJustificationCENTER :
	     DBG_Trace(AtSWJustification, "Exiting...");
	     return ((w - tw) / 2);
	};
    };
  return (0);
}



/*
 * AtSWD_clearCell : clear the write area of a cell.
 */

void AtSWD_clearCell(Display *d,
		     Window w,
		     AtSpreadsheet s,
		     short int trow,
		     short int lcol,
		     short int brow,
		     short int rcol)
{
  /* The simplest way is : Clear everything and redraw the grid */
  fprintf(stderr, "Clear cell @ %d, %d, %d, %d\n With H: %d, W:%d\n",
	  trow, lcol, brow, rcol,
	  GeomCellInnerV(s, brow), GeomCellInnerH(s, rcol));
  XClearArea (d, w, 
	      s->segmentV [1 + lcol - s->leftColumn] .x1,
	      s->segmentH [1 + trow - s->topRow] .y1,
	        s->segmentV [1 + rcol - s->leftColumn] .x1
	      - s->segmentV [1 + lcol - s->leftColumn] .x1
	      + GeomCellInnerH(s, rcol),
	        s->segmentH [1 + brow - s->topRow] .y1
	      - s->segmentH [1 + trow - s->topRow] .y1
	      + GeomCellInnerV(s, brow));
      
}


			 
void AtSWD_cursorLabelInvert (Display *d,
			      Window w,
			      Pixel  fg,
			      AtSpreadsheet s)

{
  
  /* Set the foreground of the GC */
  XSetForeground(d, s->gc, fg);
  /* Set the function to XOR */
  XSetFunction (d, s->gc, GXxor);
  /* Reset the clip mask */
  XSetClipMask (d, s->gc, None);


  /* Row label */
  XFillRectangle (d, w, s->gc,
		  0,
		    s->segmentH [1 + CursorRow(s) - s->topRow].y1
		  + GeomLinePostSemiWidth(s),
		  GeomRwLInnerH(s),
		  GeomRwLInnerV(s, CursorRow(s)));

  /* column label */
  XFillRectangle (d, w, s->gc,
		    s->segmentV [1 + CursorColumn(s) - s->leftColumn].x1
		  + GeomLinePostSemiWidth(s),
		  0,
		  GeomCoLInnerH(s, CursorColumn(s)), GeomCoLInnerV(s));
  
  
  
  /* Reset GC stuff. */
  XSetFunction (d, s->gc, GXcopy);


}

void cursorBlink (AtSpreadsheetWidget w, XtIntervalId *id)

{
  int cursorWidth = (w->spreadsheet.marginHeight > w->spreadsheet.marginWidth)
		       ? w->spreadsheet.marginWidth
		       : w->spreadsheet.marginHeight;
  AtSpreadsheet s = WtSpreadsheet(w);

  if (s->cursorTimeOutID != (*id))
    return; /* I have been killed or replaced...*/

  s->cursorDashOffset =    (s->cursorDashOffset + 3)
                       % (s->cursorDashWidth [0] + s->cursorDashWidth [1]);
  XSetDashes(WtDisplay(w),
	      s->gc,
	      s->cursorDashOffset,
	      s->cursorDashWidth,
	      2);
    
  XDrawRectangle (WtDisplay(w), WtWindow(w), s->gc,
		    s->segmentV [1 + CursorColumn(s) - s->leftColumn].x1
		  + GeomLinePostSemiWidth(s) + cursorWidth / 2,
		    s->segmentH [1 + CursorRow(s) - s->topRow].y1
		  + GeomLinePostSemiWidth(s) + cursorWidth / 2,
		  GeomCellWriteH(s, CursorColumn(s)) + cursorWidth,
		  GeomCellWriteV(s, CursorRow(s)) + cursorWidth);


  s->cursorTimeOutID = XtAddTimeOut(s->cursorFrequency,
				    cursorBlink,
				    (caddr_t)w);
}

void AtSWD_cursorErase (Display *d, Window w, Pixel bg, AtSpreadsheet s)

{
  if (s->cursorFrequency)
    {
      XtRemoveTimeOut(s->cursorTimeOutID);
      s->cursorTimeOutID--; /*=== KLUDGE ====*/
    };

  XClearArea (d, w,
	      0,
	        s->segmentH [1 + CursorRow(s) - s->topRow].y1
	      + GeomLinePostSemiWidth(s),
	      GeomRwLInnerH(s),
	      GeomRwLInnerV(s, CursorRow(s)));
  XClearArea (d, w,
	        s->segmentV [1 + CursorColumn(s) - s->leftColumn].x1
	      + GeomLinePostSemiWidth(s),
	      0,
	      GeomCoLInnerH(s, CursorColumn(s)), GeomCoLInnerV(s));

  XClearArea (d, w,
	        s->segmentV [1 + CursorColumn(s) - s->leftColumn].x1
	      + GeomLinePostSemiWidth(s),
	        s->segmentH [1 + CursorRow(s) - s->topRow].y1
	      + GeomLinePostSemiWidth(s),
	      GeomCellWriteH(s, CursorColumn(s)),
	      GeomCellWriteV(s, CursorRow(s)));
}

      
void AtSWD_cursorLeave (Display *d, Window w, Pixel bg, AtSpreadsheet s)

{
  int cursorWidth =  (s->marginHeight > s->marginWidth)
		       ? s->marginWidth : s->marginHeight;

  if (s->cursorFrequency)
    {
      XtRemoveTimeOut(s->cursorTimeOutID);
      s->cursorTimeOutID--; /*=== KLUDGE ====*/
    };
 
 
  AtSWD_cursorLabelInvert(d, w, bg, s);
  /* Erase the cursor */
  XDrawRectangle (d, w, s->gc,
		    s->segmentV [1 + CursorColumn(s) - s->leftColumn].x1
		  + GeomLinePostSemiWidth(s) + cursorWidth / 2,
		    s->segmentH [1 + CursorRow(s) - s->topRow].y1
		  + GeomLinePostSemiWidth(s) + cursorWidth / 2,
		  GeomCellWriteH(s, CursorColumn(s)) + cursorWidth,
		  GeomCellWriteV(s, CursorRow(s)) + cursorWidth);
  /* Reset Stuff for line drawing */
  XSetLineAttributes(d, s->gc,
		     s->lineWidth,
		     LineSolid,
		     CapNotLast,
		     JoinMiter);

}

void AtSWD_cursorShow (Display *d, Window w, AtSpreadsheet s, Pixel bg)

{
  AtSpreadsheetLocation cursorL = {s->table,
				   CursorRow(s),
				   CursorColumn(s),
				   s->cursorRowPrev,
				   s->cursorColumnPrev};
  int cursorWidth =  (s->marginHeight > s->marginWidth)
		       ? s->marginWidth : s->marginHeight;
  

  AtSWD_cursorLabelInvert(d, w,  s->cellColor ^ bg, s);
  /* Reset fg */
  /* Draw in the cell */
  XSetForeground(d, s->gc, s->lineColor);
  s->cursorDashOffset = 0;
  XSetDashes(d, s->gc, s->cursorDashOffset, s->cursorDashWidth, 2);
  XSetLineAttributes(d, s->gc,
		     cursorWidth,
    		     LineDoubleDash,
		     CapNotLast,
		     JoinRound);
  
  XDrawRectangle (d, w, s->gc,
		    s->segmentV [1 + CursorColumn(s) - s->leftColumn].x1
		  + GeomLinePostSemiWidth(s) + cursorWidth / 2,
		    s->segmentH [1 + CursorRow(s) - s->topRow].y1
		  + GeomLinePostSemiWidth(s) + cursorWidth / 2,
		  GeomCellWriteH(s, CursorColumn(s)) + cursorWidth,
		  GeomCellWriteV(s, CursorRow(s)) + cursorWidth);
  
  if (s->cursorFrequency)
    {
      s->cursorTimeOutID = XtAddTimeOut(s->cursorFrequency,
					cursorBlink,
					(caddr_t)(SpreadsheetCore(s)));
    };

  if (   (s->cursorRowPrev    != s->cursorRow)
      || (s->cursorColumnPrev != s->cursorColumn))
    {
      XtCallCallbacks(SpreadsheetCore(s),
		      XtNcursorMovedCallback,
		      &cursorL);
      s->cursorRowPrev = s->cursorRow;
      s->cursorColumnPrev = s->cursorColumn;
    };
      
}

void AtSWD_cursorSet (Display *d,
		      Window w,
		      AtSpreadsheet s,
		      short int irow,
		      short int icol,
		      Pixel bg)

{
  AtSWD_cursorLeave(d, w, bg, s);
  CursorRow(s) = irow;
  CursorColumn(s) = icol;
  AtSWD_cursorShow(d, w, s, bg);
}


void AtSW_cursorCheck(AtSpreadsheet s)

{
  /*======== Check if cursor is in a hidden row =======*/
}

short int AtSW_cursorMoveLeft(AtSpreadsheet s)

{
  register short int icol;

  for (icol = CursorColumn(s) - 1; (icol >= 0); icol--)
    if (! HiddenColumnP(s, icol))
      return(icol);
  return (CursorColumn(s));
}

short int AtSW_cursorMoveRight(AtSpreadsheet s)

{
  register short int icol;

  for (icol = CursorColumn(s) + 1; (icol < s->nColumn); icol++)
    if (! HiddenColumnP(s, icol))
      return(icol);
  return (CursorColumn(s));
}

short int AtSW_cursorMoveUp(AtSpreadsheet s)

{
  register short int irow;

  for (irow = CursorRow(s) - 1; (irow >= 0); irow--)
    if (! HiddenRowP(s, irow))
      return(irow);
  return (CursorRow(s));
}

short int AtSW_cursorMoveDown(AtSpreadsheet s)

{
  register short int irow;

  for (irow = CursorRow(s) + 1; (irow < s->nRow); irow++)
    if (! HiddenRowP(s, irow))
      return(irow);
  return(CursorRow(s));
}
  
void AtSWD_ScrollHorizontal (Display *d,
			     Window w,
			     AtSpreadsheet s,
			     Dimension h,
			     Dimension wi,
			     Pixel bg,
			     short ncol)

{
/**************
   if (   ((ncol < 0) && (s->leftColumn == 0))
      || ((ncol > 0 ) && (s->rightColumn == (s->nColumn - 1)))
      || (ncol == 0))
    return;
**************/

  /* Clear Most of the screen */
  XSetClipMask (d, s->gc, None);
  XClearArea(d, w, s->segmentV[1].x1, 0, wi - s->segmentV [1].x1, h);

  /*-> Compute the new left column */
  s->leftColumn += ncol;

  /*-> Compute column exposed */
  s->rightColumn = AtSWI_GetRightColumnExposed(s, w);
  /*-> realloc column segment */
  /*==== Improvement possible : realloc and recompute only what is
    ==== necessary in the point table
    ==== */
  XtFree(s->segmentV);
  s->segmentV = XtNewArray(XSegment, CalcShowColumns(s) + 2);
  /*-> recompute column segment */
  AtSWBuildVerticalPts(s, h, s->segmentV);
  /* Check cursor Position */
  /*-> redisplay the stuff */

  AtSWD_Redisplay(s,
		    AtRedisplay_CELL
		  | AtRedisplay_GRID
		  | AtRedisplay_COLUMNLABEL,
		  s->topRow,
		  s->leftColumn,
		  s->bottomRow,
		  s->rightColumn);
}

void AtSWD_ScrollVertical (Display *d,
			   Window w,
			   AtSpreadsheet s,
			   Dimension h,
			   Dimension wi,
			   Pixel bg,
			   short nrow)

{
/*********
  if (   ((nrow < 0) && (s->topRow == 0))
      || ((nrow > 0) && (s->bottomRow == (s->nRow - 1)))
      || (nrow == 0))
    return;
*******/

  /* Clear Most of the screen */
  XSetClipMask (d, s->gc, None);
  XClearArea(d, w, 0, s->segmentH [1].y1, wi, h - s->segmentH [1].y1);
  
  /* --> Set the new topRow    */
   s->topRow += nrow;

  /* --> Compute row exposed   */
  s->bottomRow = AtSWI_GetBottomRowExposed(s, h);
  /* --> Realloc row segment */
  XtFree(s->segmentH);
  s->segmentH = XtNewArray(XSegment, CalcShowRows(s) + 2);
  /* --> Recompute Segment     */
  AtSWBuildHorizontalPts(s, wi, s->segmentH);
  /* Cursor Position */
  /* --> Redisplay the stuff   */

  AtSWD_Redisplay(s,
		    AtRedisplay_CELL
		  | AtRedisplay_GRID
		  | AtRedisplay_ROWLABEL,
		  s->topRow,
		  s->leftColumn,
		  s->bottomRow,
		  s->rightColumn);
}


void AtSWD_Redisplay(AtSpreadsheet s,
		     int           what,
		     short int     trow,
		     short int     lcol,
		     short int     brow,
		     short int     rcol)
{

    /* Check and draw vertical / horizontal grid      */
  if (what & AtRedisplay_GRID)
    AtSWD_drawGrid(SpreadsheetDisplay(s),
		   SpreadsheetWindow(s),
		   s);

  /* Write row labels on the screen     */
  if (what & AtRedisplay_ROWLABEL)
    AtSWD_writeRowLabel (SpreadsheetDisplay(s),
			 SpreadsheetWindow(s),
			 s,
			 trow,
			 brow);

  /* Write column labels on the screen  */
  if (what & AtRedisplay_COLUMNLABEL)
    AtSWD_writeColumnLabel  (SpreadsheetDisplay(s),
			     SpreadsheetWindow(s),
			     s,
			     lcol,
			     rcol); 

 /* Write in all cells on the screen   */
  if (what & AtRedisplay_CELL)
    AtSWD_writeCell(SpreadsheetDisplay(s),
		    SpreadsheetWindow(s),
		    s,
		    trow, lcol, brow, rcol);

  /* Show the cursor */

  if (what & AtRedisplay_CURSOR)
    AtSWD_cursorShow(SpreadsheetDisplay(s),
		     SpreadsheetWindow(s),
		     s,
		     SpreadsheetBckGrnd(s));
}

void AtSWD_updateCell(AtSpreadsheet s,
		      short int trow,
		      short int lcol,
		      short int brow,
		      short int rcol)

{
  AtSWD_clearCell(SpreadsheetDisplay(s),
		  SpreadsheetWindow(s),
		  s,
		  trow, lcol, brow, rcol);

  AtSWD_Redisplay(s,
		  AtRedisplay_GRID | AtRedisplay_CELL | AtRedisplay_CURSOR,
		  trow, lcol, brow, rcol);

}






