// // $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 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.timestamptimestamp) { 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.timestamptimestamp) { 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.timestamptimestamp) { 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.timestamptimestamp) { 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.timestamptimestamp) { 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.timestamptimestamp) { 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.timestamptimestamp) { 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.timestamptimestamp) { 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.timestamptimestamp) { 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.timestamptimestamp) { 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.timestamptimestamp) { 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 // //