//
// $Id: cell.cc,v 1.44 1998/10/20 11:49:23 cthulhu Exp $
//


#include "cell.hh"
#define DEBUG_FORMULA 1

char Cell::buffer[256];

Cell::Cell() {
    iter = -1;
    tag = 0;
    dirty = 0;
    type = CODE_BLANK;
    value = 0;
    formula = NULL;
    label = NULL;
    has_rectangle = FALSE;
    modified = TRUE;
    item = NULL;
    border_item = NO_ITEM;
    error_code = NO_ERROR;
    wid = 0;
};


Cell::Cell(short c, short r) {
    col = c;
    row = r;
    iter = -1;
    tag = 0;
    dirty = 0;
    type = CODE_BLANK;
    value = 0;
    formula = NULL;
    label = NULL;
    has_rectangle = FALSE;
    modified = TRUE;
    item = NULL;
    border_item = NO_ITEM;
    error_code = NO_ERROR;
    wid = 0;
}

Cell::~Cell() {
    
    delete formula;
    delete label;
}

int Cell::filllevel() {
    return (format.bg.filllevel);
}

void Cell::set(char *sti,Parser &p) {
    int i;
    int code;

    if (sti[0] != 0) {
        p.cell = this;
        formula = new Formula();/* AML : to be changed */
        
        code = p.parse(sti,col,row);
    } else {
        code = CODE_BLANK;
    }
        
    type = code;
    switch(code) {
      case CODE_BLANK:
        break;
      case CODE_FORMULA:
        break;
      case CODE_NUMBER:
        value = p.number;
        delete formula;/* AML : to be changed */
        formula = NULL;
        break;
      case CODE_INTEGER:
        ivalue = p.integ;
        delete formula;/* AML : to be changed */
        formula = NULL;
        break;
      case CODE_LABEL:
        label = strsave(sti);
        type = CODE_LABEL;
        delete formula; /* AML : to be changed */
        formula = NULL;
        break;
      case PARSE_ERROR:
        label = new char[strlen(sti)+2];
        if (sti[0] == '\'')
          sprintf(label,"%s",sti);
        else 
          sprintf(label,"'%s",sti);
        type = CODE_LABEL;
        delete formula; /* AML : to be changed */
        formula = NULL;
        break;
    }
    modified = TRUE;
}

void Cell::operator=(const Cell &right) 
{
  type = right.type;
  label = strsave(right.label);
  format = right.format;
  if (right.formula != NULL)
    formula = new Formula(*right.formula);
  value = right.value;
  ivalue = right.ivalue;
  dirty = FALSE;      
  iter = -1;
  /* Row and column are not copied */
}

void Cell::loadvalue(const Cell &right)
{
  type = right.type;
  label = strsave(right.label);
  format = right.format;
  if (right.formula != NULL)
    formula = new Formula(*right.formula);
  value = right.value;
  ivalue = right.ivalue;
  dirty = FALSE;      
  iter = -1;
  /* Row and column are not copied */
}

char *Cell::display() {

    switch(error_code) {
      case NO_ERROR:
        break;
      case ERROR_DIV_BY_0:
        strcpy(buffer,ERROR_DIV_BY_0_STRING);
        return(buffer);
      case ERROR_BAD_VALUE:
        strcpy(buffer,ERROR_BAD_VALUE_STRING);
        return(buffer);
      case ERROR_BAD_REF:
        strcpy(buffer,ERROR_BAD_REF_STRING);
        return(buffer);
      default:
        internal_error();
    }
        

    switch(type) {
      case CODE_INTEGER:
        print_formatted(FORM_INT);
        return(buffer);
      case CODE_FORMULA:
        switch(formula->typ) {
          default:
          case FORM_FP:
          case FORM_INT:
            print_formatted(formula->typ);
            return(buffer);
            break;
          case FORM_STRING:
            sprintf(buffer,"%s",formula->st_value);
            return(buffer);
            break;
        }
        break;
        
      case CODE_NUMBER:
        print_formatted(FORM_FP);
        return(buffer);
      case CODE_BLANK:
        return("");
      case CODE_LABEL:
        return(label+1);
    }

}


Stack_elem *Cell::val() {
    static Stack_elem v;
    
    switch(type) {
      case CODE_BLANK:
      case CODE_LABEL:
      case CODE_FORMULA:
      case CODE_NUMBER:
        v.type = FORM_FP;
        v.contents.fp_val = value;
        break;
      case CODE_INTEGER:
        v.type = FORM_INT;
        v.contents.int_val = ivalue;
        break;
    }
    return(&v);
}

void Cell::setformat(int form) {
  switch(form) {
  case DEFAULT_DATE_FORMAT:
    format.form = form;
    break;
  default:
    internal_error();
  }
}

void Cell::append(void *pt,int type) {
    double *pt_fp,*pt_fp1;
    Ref *pt_ref,*pt_ref1;
    short *pt_int,*pt_int1;
    char *pt_st,*pt_st1;
    Range *pt_rg,*pt_rg1;
    static unsigned short rel_mask =  (1 << 15);
    short r,c;
    int *pa,a; short b;
    Cell *bcell;
    int size;

    char *aux;

    aux = formula->tmp_formula + formula->size;
    
    *aux = (char) type;
    aux++;
    formula->size++;
    

    switch(type) {
      case FORM_FP:
        pt_fp = (double*) aux;
        pt_fp1 = (double*) pt;
        
        /*
         *pt_fp = *pt_fp1; 
         */

        memcpy(pt_fp,pt_fp1,sizeof(double));
        formula->size += FORM_FP_SIZE;
        break;

      case FORM_REF:
        pt_ref = (Ref*) aux;
        pt_ref1 = (Ref*) pt;

        /* Now, change the references in case this is relative */

        if (pt_ref1->col & rel_mask) 
            change_col_to_relative(pt_ref1,col);
        
        if (pt_ref1->row & rel_mask) 
            change_row_to_relative(pt_ref1,row);
        
        /*
         *pt_ref = *pt_ref1;
         */
        
        memcpy(pt_ref,pt_ref1,sizeof(Ref));
               
        formula->size += FORM_REF_SIZE;

        /* Now, insert reference */
        
        break;
        
        
      case FORM_INT:
        pt_int = (short*) aux;
        pa = (int*) pt;
        b = (short) (*pa);
        /*
         *pt_int = *pt_int1;
         */
        memcpy(pt_int,&b,sizeof(short));

        formula->size += FORM_INT_SIZE;
        break;

      case FORM_RANGE:
        pt_rg = (Range*) aux;
        pt_rg1 = (Range*) pt;

        /* Now, change the references in case this is relative */

        if (pt_rg1->start.col & rel_mask) 
            change_col_to_relative(&(pt_rg1->start),col);
        if (pt_rg1->start.row & rel_mask) 
            change_row_to_relative(&(pt_rg1->start),row);

        if (pt_rg1->end.col & rel_mask) 
            change_col_to_relative(&(pt_rg1->end),col);
        if (pt_rg1->end.row & rel_mask) 
            change_row_to_relative(&(pt_rg1->end),row);

        /**pt_rg = *pt_rg1;*/
        memcpy(pt_rg,pt_rg1,sizeof(Range));
        formula->size += FORM_RANGE_SIZE;
        break;

      case FORM_RETURN:
        /* Now save formula in the right place */
        int i;
        formula->formula = new char[formula->size];
        memcpy(formula->formula,formula->tmp_formula,formula->size);
        delete [] formula->tmp_formula;
        formula->tmp_formula = NULL;
        break;
        
      case FORM_PAR:
        break;

      case FORM_STRING:
        pt_st = (char*) pt;
        size = strlen(pt_st)-2;
        memcpy(aux,pt_st+1,size+1);
        aux[size] = 0;
        formula->size += size+1;
        break;
        
      case FORM_UNMINUS:
        internal_error();

      case FORM_PLUS:
      case FORM_SUB:
      case FORM_MULT:
      case FORM_DIV:
      case FORM_POWER:
      case FORM_GREATER:
      case FORM_SMALLER:
      case FORM_EQUAL:
      case FORM_GREATEREQ:
      case FORM_SMALLEREQ:
      case FORM_NEQUAL:
      case FORM_AND:
      case FORM_OR:
      case FORM_NOT:
      case FORM_UNARY_MINUS:
      case FORM_ABS:
      case FORM_INTF:
      case FORM_SQRT:
      case FORM_LOG:
      case FORM_LN:
      case FORM_PI:
      case FORM_SIN:
      case FORM_COS:
      case FORM_TAN:
      case FORM_ATAN2:
      case FORM_ATAN:
      case FORM_ASIN:
      case FORM_ACOS:
      case FORM_EXP:
      case FORM_MOD:
      case FORM_MONTH:
      case FORM_DAY:
      case FORM_HOUR:
      case FORM_MINUTE:
      case FORM_SECOND:
        break;

      case FORM_UPPER:
      case FORM_LOWER:
      case FORM_PROPER:
        break;

      case FORM_SUM:
      case FORM_SUMIF:
      case FORM_AVG:
      case FORM_COUNT:
      case FORM_MAX:
      case FORM_MIN:
      case FORM_IF:
        pt_st = (char *)pt;
        *aux = *pt_st;
        formula->size++;
        break;

      case FORM_ROUND:
      case FORM_VLOOKUP:
      case FORM_HLOOKUP:
        break;

      default:
        internal_error();
    }

}

double Cell::getValueAsDouble()
{
  switch(type)
    {
    case CODE_INTEGER:
      return((double) ivalue);
    case CODE_NUMBER:
      return(value);
    case CODE_FORMULA:
      return(formula->value);
    default:
      internal_error();
      break;
    }
}
void Cell::addDependence(void *dep, int type) {
    int i;
    Cell *pt;

    for (i=0; i<deps.cnt; i++) {
        if (deps[i].cell == dep)
            return;
    }
    deps[i].cell = dep;
    deps[i].type = type;
    deps.cnt++;
     

}

void Cell::removeDependence(void *dep, int type) {
  int i;
  Cell *pt;
  
  for (i=0; i<deps.cnt; i++) {
    if (deps[i].cell == dep)
      if (deps.cnt > 0) {
	deps[i] = deps[deps.cnt-1];
	deps.cnt--;
      }
  }
}

void Cell::changeCellReference(unsigned char *aux ,Oper_info *info,short col2, short row2){
  
  short *pt;

  pt = (short*) aux;

  switch (info->type) {
  case COL_OPERATION:
    /* Orig above, other below, ref is relative */
    if ((col >= info->ref_line && col2 < info->ref_line && ((*pt) & Refmask)))
      (*pt) -= info->n;

    /* Orig below, other above */
    else if ((col < info->ref_line && col2 >= info->ref_line) )
      (*pt) += info->n;
    
    /* other above, ref is absolute */
    else if (col2 >= info->ref_line && !((*pt)&Refmask))
      (*pt) += info->n;
    break;
  case ROW_OPERATION:
    /* Orig above, other below, ref is relative */
    pt++;
    if ((row >= info->ref_line && row2 < info->ref_line && ((*pt) & Refmask)))
      (*pt) -= info->n;

    /* Orig below, other above */
    else if ((row < info->ref_line && row2 >= info->ref_line) )
      (*pt) += info->n;
    
    /* other above, ref is absolute */
    else if (row2 >= info->ref_line && !((*pt)&Refmask))
      (*pt) += info->n;
    break;
  default:
    internal_error();  
  }
  
    
}


void Cell::print_formatted(int val_type) {
  
  int c = format.form;
  double fv;
  char format_string[10];
  time_t  tt;
  struct tm *tm;


  if (val_type == FORM_FP){
    fv = value;
  }
  else {
    fv = ivalue;
  }

  if (fv == 0) fv = 0;
  

  int tp = (c & 0x70) >> 4;
  int dp = (c & 0x0f);

  switch(tp) {
  case 0:
    sprintf(format_string,"%%.%df",dp);
    sprintf(buffer,format_string,fv);
    break;
  case 1:
    sprintf(format_string,"%%.%dE",dp);
    sprintf(buffer,format_string,fv);
    break;
  case 2:    
    sprintf(format_string,"$ %%.%df",dp);
    sprintf(buffer,format_string,fv);
    break;
  case 3:
    sprintf(format_string,"%%.%df %%%%",dp);
    sprintf(buffer,format_string,fv*100);
    break;
  case 4:    
    sprintf(format_string,"%%.%df",dp);
    sprintf(buffer,format_string,fv);
    break;
  case 5:
    sprintf(buffer,"###");
    break;
  case 6:  
    sprintf(buffer,"###");
    break;
  case 7:
    switch(dp) {
    case 0:
      if (fv >= 0) 
        sprintf(buffer,"+");
      else
        sprintf(buffer,"-");
      break;
    case 1:
      sprintf(buffer,"%g",fv);
      break;
    case 2:
      fv = fv-DAYS_BETWEEN_1970_1900;
      tt = (long)(SECONDS_IN_A_DAY*fv);
      tm = localtime(&tt);
      sprintf(buffer,"%d-%s-%02d",
              tm->tm_mday,month_names[tm->tm_mon],tm->tm_year%100);
      break;
    case 3:
      fv = fv-DAYS_BETWEEN_1970_1900;
      tt = (long)(SECONDS_IN_A_DAY*fv);
      tm = localtime(&tt);
      sprintf(buffer,"%d-%s",tm->tm_mday,month_names[tm->tm_mon]);
      break;
    case 4:
      fv = fv-DAYS_BETWEEN_1970_1900;
      tt = (long)(SECONDS_IN_A_DAY*fv);
      tm = localtime(&tt);
      sprintf(buffer,"%s-%02d",month_names[tm->tm_mon],tm->tm_year%100);
      break;
    case 5:
      sprintf(buffer,"Not implemented");
      break;
    case 6:
      sprintf(buffer,"");
      break;
    case 7:
      fv = fv-DAYS_BETWEEN_1970_1900;
      tt = (long)(SECONDS_IN_A_DAY*fv);
      tm = localtime(&tt);
      sprintf(buffer,"%d-%s-%02d %02d:%02d:%02d",
              tm->tm_mday,month_names[tm->tm_mon],tm->tm_year%100,
              tm->tm_hour,tm->tm_min,tm->tm_sec);
      break;
    case 8:
      fv = fv-DAYS_BETWEEN_1970_1900;
      tt = (long)(SECONDS_IN_A_DAY*fv);
      tm = localtime(&tt);
      sprintf(buffer,"%d-%s-%02d %02d:%02d",
              tm->tm_mday,month_names[tm->tm_mon],tm->tm_year%100,
              tm->tm_hour,tm->tm_min);
      break;
    case 9:
      fv = fv-DAYS_BETWEEN_1970_1900;
      tt = (long)(SECONDS_IN_A_DAY*fv);
      tm = localtime(&tt);
      sprintf(buffer,"%d/%d/%d",tm->tm_mon+1,tm->tm_mday,tm->tm_year%100);
      break;
    case 10:
      fv = fv-DAYS_BETWEEN_1970_1900;
      tt = (long)(SECONDS_IN_A_DAY*fv);
      tm = localtime(&tt);
      sprintf(buffer,"%d/%d/%d",tm->tm_mday,tm->tm_mon+1,tm->tm_year%100);
      break;
    case 11:
      sprintf(buffer,"###");
      break;
    case 12:
      sprintf(buffer,"###");
      break;
    case 13:
      sprintf(buffer,"###");
      break;
    case 14:
      sprintf(buffer,"###");
      break;
    case 15:
      sprintf(buffer,"###");
      break;
    }

  }
}

void Cell::mergeformats(Format *fmt)
{
  if(fmt->form!=DEFAULT_FORMAT)
    {
      if(format.form==DEFAULT_FORMAT)
	{
	  format.form=fmt->form;
	  format.timestamp=fmt->timestamp;
	}
      else
	{
	  if(format.timestamp<fmt->timestamp)
	    {
	      format.form=fmt->form;
	      format.timestamp=fmt->timestamp;
	    }
	}
    }
  if(fmt->alignment!=DEFAULT_ALIGNMENT)
    {
      if(format.alignment==DEFAULT_ALIGNMENT)
	{
	  format.alignment=fmt->alignment;
	  format.timestamp=fmt->timestamp;
	}
      else
	{
	  if(format.timestamp<fmt->timestamp)
	    {
	      format.alignment=fmt->alignment;
	      format.timestamp=fmt->timestamp;
	    }
	}
    }
  if(!fmt->font.isdefault())
    {
      if(fmt->font.size!=DEFAULT_FONT_SIZE)
	{
	  if(format.font.size==DEFAULT_FONT_SIZE)
	    {
	      format.font.size=fmt->font.size;
	      format.timestamp=fmt->timestamp;
	    }
	  else
	    {
	      if(format.timestamp<fmt->timestamp)
		{
		  format.font.size=fmt->font.size;
		  format.timestamp=fmt->timestamp;
		}
	    }
	}
      if(fmt->font.italics!=DEFAULT_FONT_ITALICS)
	{
	  if(format.font.italics==DEFAULT_FONT_ITALICS)
	    {
	      format.font.italics=fmt->font.italics;
	      format.timestamp=fmt->timestamp;
	    }
	  else
	    {
	      if(format.timestamp<fmt->timestamp)
		{
		  format.font.italics=fmt->font.italics;
		  format.timestamp=fmt->timestamp;
		}
	    }
	}
      if(fmt->font.bold!=DEFAULT_FONT_BOLD)
	{
	  if(format.font.bold==DEFAULT_FONT_BOLD)
	    {
	      format.font.bold=fmt->font.bold;
	      format.timestamp=fmt->timestamp;
	    }
	  else
	    {
	      if(format.timestamp<fmt->timestamp)
		{
		  format.font.bold=fmt->font.bold;
		  format.timestamp=fmt->timestamp;
		}
	    }
	}
      if(fmt->font.family!=DEFAULT_FONT_FAMILY)
	{
	  if(format.font.family==DEFAULT_FONT_FAMILY)
	    {
	      format.font.family=fmt->font.family;
	      format.timestamp=fmt->timestamp;
	    }
	  else
	    {
	      if(format.timestamp<fmt->timestamp)
		{
		  format.font.family=fmt->font.family;
		  format.timestamp=fmt->timestamp;
		}
	    }
	}
    }
  if(!fmt->borders.isdefault())
    {
      if(fmt->borders.top!=DEFAULT_BORDER)
	{
	  if(format.borders.top==DEFAULT_BORDER)
	    {
	      format.borders.top=fmt->borders.top;
	      format.timestamp=fmt->timestamp;
	    }
	  else
	    {
	      if(format.timestamp<fmt->timestamp)
		{
		  format.borders.top=fmt->borders.top;
		  format.timestamp=fmt->timestamp;
		}
	    }
	}
      if(fmt->borders.bottom!=DEFAULT_BORDER)
	{
	  if(format.borders.bottom==DEFAULT_BORDER)
	    {
	      format.borders.bottom=fmt->borders.bottom;
	      format.timestamp=fmt->timestamp;
	    }
	  else
	    {
	      if(format.timestamp<fmt->timestamp)
		{
		  format.borders.bottom=fmt->borders.bottom;
		  format.timestamp=fmt->timestamp;
		}
	    }
	}
      if(fmt->borders.left!=DEFAULT_BORDER)
	{
	  if(format.borders.left==DEFAULT_BORDER)
	    {
	      format.borders.left=fmt->borders.left;
	      format.timestamp=fmt->timestamp;
	    }
	  else
	    {
	      if(format.timestamp<fmt->timestamp)
		{
		  format.borders.left=fmt->borders.left;
		  format.timestamp=fmt->timestamp;
		}
	    }
	}
      if(fmt->borders.right!=DEFAULT_BORDER)
	{
	  if(format.borders.right==DEFAULT_BORDER)
	    {
	      format.borders.right=fmt->borders.right;
	      format.timestamp=fmt->timestamp;
	    }
	  else
	    {
	      if(format.timestamp<fmt->timestamp)
		{
		  format.borders.right=fmt->borders.right;
		  format.timestamp=fmt->timestamp;
		}
	    }
	}
    }
  if(fmt->bg.filllevel!=DEFAULT_FILL)
    {
      if(format.bg.filllevel==DEFAULT_FILL)
	{
	  format.bg.filllevel=fmt->bg.filllevel;
	  format.timestamp=fmt->timestamp;
	}
      else
	{
	  if(format.timestamp<fmt->timestamp)
	    {
	      format.bg.filllevel=fmt->bg.filllevel;
	      format.timestamp=fmt->timestamp;
	    }
	}
    }
}

//
// $Log: cell.cc,v $
// Revision 1.44  1998/10/20 11:49:23  cthulhu
// Cell destructor was receiving parameters- cut & paste strike again!
//
// Revision 1.43  1998/08/06 21:08:41  aml
// Released alpha version of Abacus.
//
// Revision 1.42  1997/02/10  15:11:05  aml
// Print settings now are saved to file.
// Fixed buggy error message when loading sheets.
//
// Revision 1.41  1997/01/07 01:07:46  aml
// Error propagation for formulas fixed.
// Edit operations in place.
//
// Revision 1.40  1997/01/02  16:15:34  aml
// Fixed unsufficient range of colunm width.
// First cut of vlookup and hlookup functions.
// Fixed bug in display routines.
//
// Revision 1.39  1996/12/31 17:38:41  aml
// On-demand calculation improved. Loops are detected.
// Automatic recalculation can now be disabled.
// Printing was improved.
//
// Revision 1.38  1996/12/11 21:40:03  aml
// Sumif implemented.
// Diverse time functions implemented.
// Fixed needtoscroll2 to avoid out of control scroll.
//
// Revision 1.37  1996/11/22  16:29:19  aml
// First cut at transforming canvas into a true cell widget.
// Text, lines and rectangles are now relative to row and colunm numbers.
// It still has a bug with wrong estimation of column widths.
//
// Revision 1.36  1996/10/07 12:35:37  aml
// First cut at error handling.
// Date formats are in.
// Fixed problem with blank cell drawing.
//
// Revision 1.35  1996/09/19  12:17:04  aml
// Created row and column insert.
// Fixed small problem with display of vergrown cells.
//
// Revision 1.34  1996/09/17 15:16:24  aml
// Fixed problems with copying of cells with non-default formats.
// Created printing formats, alignment formats.
// Format toolbar now reflects format of active cell.
//
// Revision 1.33  1996/09/16 18:42:40  aml
// Some performance problems addressed by reducing tag use.
// Several performance problems remain when heavy use is made
// of borders and shading in large spreadsheets.
//
// Revision 1.32  1996/09/14  23:56:03  aml
// Created cell shading.
//
// Revision 1.31  1996/09/04  14:29:59  aml
// Fixed double redrawing of sheets that was taking place.
// Fixed a item reference problem when the sheet is redrawn.
// Fixed misplacement of row labels.
// Created first version of format toolbar.
//
// Revision 1.30  1996/09/02 10:51:38  aml
// Cell fonts created, loaded and saved.
// Row height created.
//
// Revision 1.29  1996/08/28  17:17:54  aml
// Load and save now accept string_value for formula cells.
// This fixes previous thought problem of formula values not
// being stored.
// Functions upper,lower and proper created.
// Function if can now return labels.
// Fixed problem with function count.
// Reasonably stable version, very used to manipulate notas.wk1.
//
// Revision 1.28  1996/08/27 17:18:55  aml
// First version of regressive tests created.
// Changes were made to allow for string functions.
// String function upper created. Raises the problem
// that formulas do NOT have a space for string values,
// and therefore have to be evaluated upon loading.
//
// Revision 1.27  1996/08/26 17:22:28  aml
// Function round fixed.
// Many other functions added, from power to mod.
//
// Revision 1.26  1996/07/18 10:19:31  aml
// Created formats for cells.
// Load cell now makes copy of old file.
//
// Revision 1.25  1996/04/27 11:37:09  aml
// Started changes for format.
//
// Revision 1.24  1996/04/23 09:43:04  aml
// Data structures for ordered scans of rows and columns are in place.
// Cell overlap is working.
// Forward cell dependences inserted. Automatic recalculation created.
// Uniformizaed label entry procedure.
//
// Revision 1.23  1996/04/21 13:28:13  aml
// Sped up scroll functions, caching keys presses.
// First cut at handling overflowing cells.
// Overflow into ajoining filled cells not solved.
//
// Revision 1.22  1996/03/11  15:47:49  aml
// Made redraw more efficient by removing at once all cells before redrawing.
// Fixed problem with unsufficient difinition of logical expressions.
// Added >=, <=, <>, @and and @or functions.
//
// Revision 1.21  1996/03/08 19:00:34  aml
// Fixed problem in string_single_arg macro
// Created if function, boolean evaluations and so on.
//
// Revision 1.20  1996/03/07 20:33:08  aml
// Created print range ability.
// Set in gray non-working menus.
// Created RangeKill command.
// Created round function and macros for single argument functions.
//
// Revision 1.19  1996/02/19  15:47:38  aml
// Fixed abnormality with mouse click.
// Variable width columns implemented, but not yet saved.
// Labels are now a lex element, fixing some aberrant behavior that existed.
//
// Revision 1.18  1996/02/16 23:09:38  aml
// Improved user interf state machine.
// Centralized range definitions.
// Range defined outiside current view work properly.
//
// Revision 1.17  1996/02/16  18:04:10  aml
// Fixed a memory bug in formula copy with purify.
// Fixed a few minor bugs. Functional, stable version.
//
// Revision 1.16  1996/02/13 12:03:49  aml
// Fixed bug with range definition via mouse.
// Fixed bug in range iterators.
//
// Revision 1.15  1996/01/30  16:05:41  aml
// User interface for cell and range copy created.
// Improved user interface state machine when entering and viewing cells.
// Fixed unaligned accesses during formula parsing and io operations.
//
// Revision 1.14  1996/01/27  23:10:31  aml
// CellCopy created.
// CellGet fixed.
// Tcl range operators created.
// User interface improved. Cell references and ranges
// can now be defined with the mouse.
//
// Revision 1.13  1996/01/11  22:48:52  aml
// Range iterators created.
// Functions sum, max and min now work properly.
// Negative numbers now allowed by flex (oops :-)
//
// Revision 1.12  1996/01/10  18:53:44  aml
// Fixed limited integer range of cells.
// Fixed incorrect code in spreadsheet type.
// Users interface improved. Cursors and mouse clicks
// now work in the most basic modes.
//
// Revision 1.11  1996/01/09  18:34:54  aml
// Load, save, open, close and exit now work properly (hopefuly).
// Sheet utility functions also work : SheetExists, SheetEmpty, SheetModified
//
// Revision 1.10  1996/01/07  09:07:43  aml
// Sheet::save and Sheet::load created.
// Program can now write and read wk1 files.
// Slight changes made to relative references. Bit 14 is now always 0.
//
// Revision 1.9  1996/01/05  23:06:00  aml
// Cell references evaluated.
// Spreadsheet is recalculated at every change, by an arbitrary order.
// Reformulated program structure. Evaluation and reverse parsing
// are member functions of Sheet.
//
// Revision 1.8  1996/01/04  20:27:18  aml
// Range references parsed and reverse parsed.
//
// Revision 1.7  1996/01/03  23:07:15  aml
// Absolute and relative references to cells introduced.
// They are parsed and reverse parsed, not yet evaluated.
//
// Revision 1.6  1996/01/02  16:22:11  aml
// Formula compilation, evaluation and decompilation now work.
// Cells can be of type label, numerical formula or numbers.
//
// Revision 1.5  1995/12/30  16:41:47  aml
// First cut of formula compilation.
//
// Revision 1.4  1995/12/28  19:20:39  aml
// Created skeleton to merge calculation engine
//
// Revision 1.3  1995/12/27  23:12:59  aml
// First draft of Sheet::display
//
// Revision 1.2  1995/12/13  14:32:43  aml
// Created Cell, set and val member functions
//
// Revision 1.1  1995/12/06  14:50:19  aml
// Initial revision
//
//
