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



/* Preliminaries remarks.
 * This file contains the header file for  a library of functions for the
 * Spreadsheet widget. The name of functions that are "public" start with
 * AtSW. This file contains also macros that operates on the spreadsheet
 * record data type.
 * The purpose is:
 *  to encapsulate into macros the access to individual elements of an array.
 *  to calculate values from fields of the spreadsheet widget record.
 *  to implement the inheritance mechanism for cells' attributes.
 *  to provide check on index validity.
 *  to provide macro relevant for the geometry of the widget.
 *
 * Naming convention :
 * Access to fields or arrays have the prefix Get.
 * Calculation of values from the spreadsheet record have the prefix Calc.
 * Predicat have the suffix P.
 * Inheritance macros have the prefix Eval.
 * Geometry computation is prefixed Geom.
 *      - For the side column containing row label the prefix is GeomRwL
 *      - For the above row containing the column label the prefix is GeomCoL
 *      - For the cell the prefix is GeomC
 *
 * For the geometry there are few atoms used to build name:
 *      - Xpos refers to the x posistion of an area.
 *      - Ypos refers to the y position of an area.
 *      - XStep refers to an increase in X coordinate from cell to next cell.
 *      - YStep refers to an increase in Y coordinate from cell to next cell
 *      - HSpace stands for horizontal space.
 *      - VSpace stands for vertical space.
 *      - Write is used for the space available to write.
 *      - Inner is used for the space between grid lines.
 *      - Total is used for the inner space plus grid lines.
 *
 * Procedure used to initialize or modify ressources are prefixed by SWI.
 * Procedure used to draw or write are prefixed SWD.
 *
 */



#ifndef _AtSpreadsheetL_h

#define _AtSpreadsheetL_h      /* Sign Up in the preprocessor. */

/* Few simple macros to make the preprocessor happier */

#define XtNewArray(T, N)	((T *) XtCalloc(sizeof(T), N))
#define Stringlength(S)		((S)? strlen(S) : 0)

/* 
 * Access key fields from the Widget.
 */

#define WtSpreadsheet(W)    (&((W)->spreadsheet))
#define WtCore(W)           (&((W)->core))
#define WtDisplay(W)        XtDisplay((Widget)(W))
#define WtWindow(W)         XtWindow((Widget)(W))
#define WtHeight(W)         ((W)->core.height)
#define WtWidth(W)          ((W)->core.width)
#define WtBckGrnd(W)	    ((W)->core.background_pixel)

/*
 * Get info. on core widget.
 */

#define SpreadsheetCore(S) ((AtSpreadsheetWidget)((char *)(S)-(S)->OffsetCore))
#define SpreadsheetDisplay(S) WtDisplay(SpreadsheetCore(S))
#define SpreadsheetWindow(S)  WtWindow(SpreadsheetCore(S))
#define SpreadsheetBckGrnd(S) WtBckGrnd(SpreadsheetCore(S))
#define SpreadsheetWidth(S)   WtWidth(SpreadsheetCore(S))
#define SpreadsheetHeight(S)  WtHeight(SpreadsheetCore(S))

/* */


#define GetCellIdx(S, IROW, ICOL) (IROW + (S->nRow) * ICOL)


/* Check bound */

#define CheckRowP(S, IROW)        ((IROW < (S)->nRow) && (IROW >= 0))
#define CheckColumnP(S, ICOL)     ((ICOL < (S)->nColumn) && (ICOL >= 0))
#define CheckCellP(S, IROW, ICOL) (CheckRowP(S, IROW) && CheckColumnP(S, ICOL))
#define CheckRangeP(S, TROW, LCOL, BROW, RCOL) \
	 ((TROW <= BROW) && (LCOL <= RCOL)     \
	  && CheckCellP(S, TROW, LCOL)         \
	  && CheckCellP(S, BROW, RCOL))

/* Inheritance */

/* Priority is given to the cell, then to the column, then to the row
 * and finally the table value is used.
 */

/* Attribute inheritance on cells */

#define EvalCellField(S, F, D, IROW, ICOL)                              \
                 (((S)->cellAtt [GetCellIdx(S, IROW, ICOL)].F)          \
                  ? (S)->cellAtt [GetCellIdx(S, IROW, ICOL)].F          \
		  : (((S)->columnAtt [ICOL].Att.F)                      \
		     ? (S)->columnAtt [ICOL].Att.F                      \
		     : (((S)->rowAtt [IROW].Att.F)                      \
			? (S)->rowAtt [IROW].Att.F                      \
			: (D))))

#define EvalCellJustification(S, IROW, ICOL)                                  \
                EvalCellField(S, justification, (S)->justification, IROW, ICOL)

#define EvalCellFace(S, IROW, ICOL)                                        \
                EvalCellField(S, face, AtSpreadsheetFaceDEFAULT, IROW, ICOL)


#define EvalEditable(S, IROW, ICOL)                                        \
                 (((S)->cellAtt [GetCellIdx(S, IROW, ICOL)].editable)       \
		  ? ((S)->cellAtt [GetCellIdx(S, IROW, ICOL)].editable != 1)\
                  : (((S)->columnAtt [ICOL].Att.editable)                   \
		     ? ((S)->columnAtt [ICOL].Att.editable != 1)            \
		     : (((S)->rowAtt [IROW].Att.editable)                   \
			? ((S)->rowAtt [IROW].Att.editable != 1)            \
			: S->Editable)))

/* Inheritance on rows an columns */

#define EvalColumnWidth(S, ICOL)                                              \
                  (((S)->columnAtt [ICOL].width != AtSpreadsheetWidthDEFAULT) \
		   ? (S)->columnAtt [ICOL].width                              \
		   : S->columnWidth)

#define EvalRowHeight(S, IROW)                                               \
                  (((S)->rowAtt [IROW].height != AtSpreadsheetHeightDEFAULT) \
		   ? (S)->rowAtt [IROW].height                               \
		   : S->rowHeight)

#define EvalRowFace(S, IROW)		((S)->rowAtt [IROW].Att.face)
#define EvalColumnFace(S, ICOL)		((S)->columnAtt [ICOL].Att.face)
		   

/* Define predicat on cells, rows and columns */


#define EditableCellP(S, IROW, ICOL) (EvalEditable(S, IROW, ICOL))
#define EditableTableP(S)            (S->editable)

#define NonHiddenRowP(S, IROW)        (EvalRowHeight(S, IROW))
#define NonHiddenColumnP(S, ICOL)	(EvalColumnWidth(S, ICOL))
#define HiddenRowP(S, IROW)		(!(NonHiddenRowP(S, IROW)))
#define HiddenColumnP(S, ICOL)		(!(NonHiddenColumnP(S, ICOL)))

/* Get special values */

#define GetCellFontStruct(S, IROW, ICOL)			        \
		((S)->fontTable [EvalCellFace(S, IROW, ICOL)])
#define GetCellFontID(S, IROW, ICOL)                                    \
                (GetCellFontStruct(S , IROW, ICOL)->fid)
#define GetRowFontStruct(S, IROW)    ((S)->fontTable [EvalRowFace(S, IROW)])
#define GetRowFontID(S, IROW)        (GetRowFontStruct(S, IROW)->fid)
#define GetColumnFontStruct(S, ICOL)					\
		((S)->fontTable [EvalColumnFace(S, ICOL)])
#define GetColumnFontID(S, ICOL)     (GetColumnFontStruct(S, ICOL)->fid)


/* Calculate */

/* Number on row on the screen */
#define CalcShowRows(S)    ((S)->bottomRow - (S)->topRow + 1)
#define CalcShowColumns(S) ((S)->rightColumn - (S)->leftColumn + 1)

/* Geometry computation the " What is where ? " */

/* Some stuff about lines */
#define GeomLineWidth(S)          (((S)->lineWidth) ? (S)->lineWidth : 1)
#define GeomLinePreSemiWidth(S)   (GeomLineWidth(S) / 2)
#define GeomLinePostSemiWidth(S)  ((GeomLineWidth(S) + 1) / 2)

/* The blank space between two labels */

#define GeomRwLBlankV(S)   (2 * (S)->rowNameMarginWidth     + GeomLineWidth(S))
#define GeomCoLBlankH(S)   (2 * (S)->columnNameMarginHeight + GeomLineWidth(S))

/* Number of pixel to write for the labels. */

#define GeomRwLWriteH(S)          ((S)->rowNameWidth * (S)->ems)
#define GeomRwLWriteV(S, IROW)    EvalRowHeight(S, IROW)

#define GeomCoLWriteH(S, ICOL)    (EvalColumnWidth(S, ICOL) * (S)->ems)
#define GeomCoLWriteV(S)          ((S)->columnNameHeight)

/* Number of pixel for the margin */

#define GeomRwLMarginH(S)              ((S)->rowNameMarginWidth)
#define GeomRwLMarginV(S)              ((S)->marginHeight)
#define GeomCoLMarginH(S)              ((S)->marginWidth)
#define GeomCoLMarginV(S)              ((S)->columnNameMarginHeight)

/* Inner space for the labels */

#define GeomRwLInnerH(S)       (2 * GeomRwLMarginH(S) + GeomRwLWriteH(S))
#define GeomRwLInnerV(S, IROW) (2 * GeomRwLMarginV(S) + GeomRwLWriteV(S, IROW))
#define GeomCoLInnerH(S, ICOL) (2 * GeomCoLMarginH(S) + GeomCoLWriteH(S, ICOL))
#define GeomCoLInnerV(S)       (2 * GeomCoLMarginV(S) + GeomCoLWriteV(S))

/* Outer space for the labels */

#define GeomRwLOuterH(S)           (  GeomRwLInnerH(S)             \
				     + 2 * GeomLineWidth(S))
#define GeomRwLOuterV(S, IROW)     (  GeomRwLInnerV(S, IROW)       \
				     + 2 * GeomLineWidth(S)        \
				     + (S)->borderSpace)

#define GeomCoLOuterH(S, ICOL)     (  GeomCoLInnerH(S, ICOL)       \
				     + 2 * GeomLineWidth(S))
#define GeomCoLOuterV(S)           (  GeomCoLInnerV(S)             \
				     + 2 * GeomLineWidth(S)        \
				     + (S)->borderSpace)

/* The X and Y position of the write space for labels */

#define GeomRwLPosWX(S)            GeomRwLMarginH(S)
#define GeomRwLPosGCX(S)           GeomRwLMarginH(S)
#define GeomRwLDeltaGCY(S)      (GeomLinePostSemiWidth(S) + GeomRwLMarginV(S))
#define GeomRwLDeltaWY(S, IROW) (GeomRwLDeltaGCY(S) + ((S)->ascent +          \
						       (S)->descent +         \
						       GeomRwLWriteV(S, IROW))\
				                       / 2 )
#define GeomCoLPosWY(S)		 (GeomCoLMarginV(S) + ((S)->ascent +          \
						       (S)->descent +         \
						       GeomCoLWriteV(S)) / 2)
#define GeomCoLPosGCY(S)	 GeomCoLMarginV(S)
#define GeomCoLDeltaGCX(S)	 (GeomLinePostSemiWidth(S) + GeomCoLMarginH(S))
#define GeomCoLDeltaWX(S, ICOL)  GeomCoLDeltaGCX(S)



/* The space unused between two successive rows or two successive columns */

#define GeomCellBlankH(S) (2 * (S)->marginWidth + GeomLineWidth(S))
#define GeomCellBlankV(S) (2 * (S)->marginHeight + GeomLineWidth(S))

/* The write space for agiven cell */

#define GeomCellWriteH(S, ICOL)    ((S)->ems * EvalColumnWidth(S, ICOL))
#define GeomCellWriteV(S, IROW)    (EvalRowHeight(S, IROW))

/* The margins for a given cell */

#define GeomCellMarginH(S)	   ((S)->marginWidth)
#define GeomCellMarginV(S)         ((S)->marginHeight)


/* The inner space for a given cell */

#define GeomCellInnerH(S, ICOL)    (HiddenColumnP(S, ICOL)	\
				    ? 0				\
				    : 2 * GeomCellMarginH(S)    \
				      + GeomCellWriteH(S, ICOL))
#define GeomCellInnerV(S, IROW)    (HiddenRowP(S, IROW)		\
				    ? 0				\
				    : 2 * GeomCellMarginV(S)	\
				      + GeomCellWriteV(S, IROW))

/* The outer space for a given cell */

#define GeomCellOuterH(S, ICOL)    (HiddenColumnP(S, ICOL)	\
				    ? 0				\
				    :   2 * GeomCellMarginH(S)	\
				      + GeomCellWriteH(S, IROW) \
				      + 2 * GeomLineWidth(S))
#define GeomCellOuterV(S, IROW)    (HiddenRowP(S, IROW)		\
				    ? 0				\
				    :   2 * GeomCellMarginV(S)	\
				      + GeomCellWriteV(S, IROW) \
				      + 2 * GeomLineWidth(S))


/* write position (X, Y) macros */

#define GeomCellDeltaGCX(S)	(GeomLinePostSemiWidth(S) + GeomCellMarginH(S))
#define GeomCellDeltaGCY(S)     (GeomLinePostSemiWidth(S) + GeomCellMarginV(S))
#define GeomCellDeltaWX(S, ICOL) GeomCellDeltaGCX(S)
#define GeomCellDeltaWY(S, IROW) (GeomCellDeltaGCY(S)+((S)->ascent +          \
						       (S)->descent +         \
						       GeomCellWriteV(S, IROW)\
						       ) / 2 )

#define GeomBaseLine(S, IROW) ((  EvalRowHeight(S, IROW)          \
				 + (S)->ascent - (S)->descent) / 2)

/* Edges of a cell */
#define GeomCellInnerTopY(S, IROW) 					\
  		(  (S)->segmentH [1 + IROW - (S)->topRow].y1		\
		 + GeomLinePostSemiWidth(S))
#define GeomCellInnerBottomY(S, IROW)					\
		(  GeomCellInnerTopY(S, IROW)				\
		 + GeomCellInnerV(S, IROW))
#define GeomCellInnerLeftX(S, ICOL)					\
		(  (S)->segmentV [1 + ICOL - (S)->leftColumn].x1	\
		 + GeomLinePostSemiWidth(S))
#define GeomCellInnerRightX(S, ICOL)					\
  		(  GeomCellInnerLeftX(S, ICOL)				\
		 + GeomCellInnerH(S, ICOL))
#define GeomCellOuterTopY(S, IROW)					\
		(  (S)->segmentH [1 + IROW - (S)->topRow].y1		\
		 - GeomLinePreSemiWidth(S))
#define GeomCellOuterBottomY(S, IROW)					\
  		(  (S)->segmentH [1 + IROW - (S)->topRow].y1		\
		 + GeomCellInnerV (S, IROW)				\
		 + GeomLineWidth(S)					\
		 + GeomLinePostSemiWidth(S))
#define GeomCellOuterLeftX(S, ICOL)					\
		(  (S)->segmentV [1 + ICOL - (S)->leftColumn].x1	\
		 - GeomLinePreSemiWidth(S))
#define GeomCellOuterRightX(S, ICOL)					\
  		(  (S)->segmentV [1 + ICOL - (S)->leftColumn].x1	\
	         + GeomCellInnerH(S, ICOL)				\
		 + GeomCellLineWidth(S)					\
		 + GeomLinePreSemiWidth(S))

/* IDX  value for special location */

#define IDX_Outside		-2
#define IDX_rowLabel		-1
#define IDX_columnLabel		-1

/* Cursor Stuff */
#define CursorRow(S)	((S)->cursorRow)
#define CursorColumn(S)	((S)->cursorColumn)


/* Some location predicat */

#define WindowTopMostRowP(S, IROW)					\
		(   (IROW >= (S)->topRow)				\
		  && (IROW <= (S)->bottomRow)		      		\
		  && (   (S)->segmentH[1 + IROW - (S)->topRow].y1	\
		      == (S)->segmentH[1].y1))
#define WindowLeftMostColumnP(S, ICOL)					\
  		(   (ICOL >= (S)->leftColumn)				\
		 && (ICOL <= (S)->rightColumn)				\
		 && (   (S)->segmentV[1 + ICOL - (S)->leftColumn].x1	\
		     == (S)->segmentV[1].x1))

#define WindowShowRowP(S, IROW)						\
		(   (IROW >= (S)->topRow)				\
		 && (IROW <= (S)->bottomRow)				\
		 && (NonHiddenRowP(S, IROW)))

#define WindowShowColumnP(S, ICOL)					\
  		(   (ICOL >= (S)->leftColumn)				\
		 && (ICOL <= (S)->rightColumn)				\
		 && (NonHiddenColumnP(S, ICOL)))

#define WindowFullShowRowP(S, IROW)					\
		(    WindowShowRowP(S, IROW)				\
		 &&  (   GeomCellInnerBottomY(S, IROW)			\
		      <= SpreadsheetHeight(S)))

#define WindowFullShowColumnP(S, ICOL)					\
		(    WindowShowColumnP(S, ICOL)				\
		 && (   GeomCellInnerRightX(S, ICOL)			\
		     <= SpreadsheetWidth(S)))

#define WindowFullShowCellP(S, IROW, ICOL)				\
		(   WindowFullShowRowP(S, IROW)				\
		 && WindowFullShowColumnP(S, ICOL))

/* Scrolling */


/* Calling foreign procedures */

#define CallGetCellValue(S, IROW, ICOL) ((*((S)->getCellValue))((S)->table,  \
					   		        IROW,        \
							        ICOL))
#define CallGetColumnLabel(S, ICOL) ((*((S)->getColumnLabel))((S)->table,ICOL))
#define CallGetRowLabel(S, IROW)      ((*((S)->getRowLabel))((S)->table, IROW))
#define CallGetColumnNumber(S)        ((*((S)->getColumnNumber))((S)->table))
#define CallGetRowNumber(S)           ((*((S)->getRowNumber))((S)->table))
#define CallSetNotify(W)   ((*((W)->spreadsheet.setNotify))                   \
			                        ((W)->spreadsheet.table,      \
						 (AtTableEventRowSize      |  \
						  AtTableEventColumnSize   |  \
						  AtTableEventRangeSet      ),\
						 (void *)W,                   \
						 trapTableEvents))
#define CallDelNotify(W)   ((*((W)->spreadsheet.delNotify))              \
			                        ((W)->spreadsheet.table, \
						 (void *)W,              \
						 trapTableEvents))
/*
 * Entry points
 */

#define AtRedisplay_NONE        0x00
#define AtRedisplay_CELL        0x01
#define AtRedisplay_GRID        0x02
#define AtRedisplay_ROWLABEL    0x04
#define AtRedisplay_COLUMNLABEL 0x08
#define AtRedisplay_CURSOR      0x10


#define AtRedisplay_ALL         (AtRedisplay_CELL        | \
				 AtRedisplay_GRID        | \
                                 AtRedisplay_ROWLABEL    | \
				 AtRedisplay_COLUMNLABEL | \
				 AtRedisplay_CURSOR         )
#define AtRedisplay_BUT_CELL    (AtRedisplay_GRID        | \
				 AtRedisplay_ROWLABEL    | \
				 AtRedisplay_COLUMNLABEL | \
				 AtRedisplay_CURSOR         )
#define AtRedisplay_BUT_GRID     (AtRedisplay_CELL       | \
                                 AtRedisplay_ROWLABEL    | \
				 AtRedisplay_COLUMNLABEL | \
				 AtRedisplay_CURSOR         )
#define AtRedisplay_BUT_ROWLABEL  (AtRedisplay_CELL        | \
				   AtRedisplay_GRID        | \
				   AtRedisplay_COLUMNLABEL | \
				   AtRedisplay_CURSOR         )
#define AtRedisplay_BUT_COLUMNLABEL (AtRedisplay_CELL         | \
				      AtRedisplay_GRID        | \
				      AtRedisplay_ROWLABEL    | \
				      AtRedisplay_CURSOR         )
#define AtRedisplay_BUT_CURSOR (AtRedisplay_CELL         | \
				 AtRedisplay_GRID        | \
                                 AtRedisplay_ROWLABEL    | \
				 AtRedisplay_COLUMNLABEL    )





extern short int AtSW_GetColumnIndex      (AtSpreadsheet, Dimension);
extern short int AtSW_GetRowIndex         (AtSpreadsheet, Dimension);
extern short AtSWI_GetRightColumnExposed  (AtSpreadsheet, Dimension);
extern short AtSWI_GetBottomRowExposed    (AtSpreadsheet, Dimension);
extern void  AtSWI_Segments               (AtSpreadsheet,
					   Dimension,
					   Dimension);
extern void  AtSWI_ItemSize               (AtSpreadsheet);
extern void  AtSWI_Font                   (AtSpreadsheet, char *, Display *);
extern void  AtSWI_BestFitting            (AtSpreadsheet);
extern void  AtSWI_Size                   (AtSpreadsheet,
					   Dimension *,
					   Dimension *);
extern void AtSWD_clearCell               (Display *,
					   Window,
					   AtSpreadsheet,
					   short int,
					   short int,
					   short int,
					   short int);

extern void AtSWD_updateCell              (AtSpreadsheet,
					   short int,
					   short int,
					   short int,
					   short int);

extern void  AtSWD_writeCell              (Display *,
					   Window,
					   AtSpreadsheet,
					   short int,
					   short int,
					   short int,
					   short int);
extern void  AtSWD_writeRowLabel          (Display *,
					   Window,
					   AtSpreadsheet,
					   short int,
					   short int);
extern void  AtSWD_writeColumnLabel       (Display *,
					   Window,
					   AtSpreadsheet,
					   short int,
					   short int);
extern void  AtSWD_drawGrid               (Display *, Window, AtSpreadsheet);
extern void  AtSWD_cursorErase            (Display *,
					   Window,
					   Pixel,
					   AtSpreadsheet);
extern void  AtSWD_cursorLeave            (Display *,
					   Window,
					   Pixel,
					   AtSpreadsheet);
extern void  AtSWD_cursorShow             (Display *,
					   Window,
					   AtSpreadsheet,
					   Pixel);
extern void  AtSW_cursorCheck             (AtSpreadsheet);
extern void  AtSWD_cursorSet              (Display *,
					   Window,
					   AtSpreadsheet,
					   short int,
					   short int,
					   Pixel);
extern void  AtSWD_ScrollVertical         (Display *,
					   Window,
					   AtSpreadsheet,
					   Dimension,
					   Dimension,
					   Pixel,
					   short);
extern void AtSWD_Redisplay               (AtSpreadsheet,
					   int,
					   short int,
					   short int,
					   short int,
					   short int);

extern void AtSWD_ScrollHorizontal        (Display *,
					   Window,
					   AtSpreadsheet,
					   Dimension,
					   Dimension,
					   Pixel,
					   short);





/* Debugging facilities... */

#ifdef DEBUG
#  define DBG_Trace(P, S)    fprintf(stderr, "[Spreadsheet Widget]" # P \
				             " : %s \n", S);
#else
#  define DBG_Trace(P, S)
#endif


#ifdef DEBUG
#define DBG_Print(X)	fprintf X
#else
#define DBG_Print(X)    
#endif

#endif /* AtSpreadsheetL_H */

