// $Id: sheet.cc,v 1.53 1998/10/20 19:08:48 cthulhu Exp $ #include "sheet.hh" #include "canvas.hh" #include "list.hh" Sheet::Sheet(char *nam,Tcl_Interp *intrp) { char *st; char command[256]; int code; int i; interp = intrp; print_range.start.col = 0; print_range.end.col = 0; print_range.start.row = 0; print_range.end.row = 0; for(i=0; iresult); sprintf(command,"defCellHeight"); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); def_cell_height = atoi(interp->result); canvas_info = new Canvas; global_canvas_info = canvas_info; canvas_info->cmdPtr = NULL; canvas_info->sheet = (void*)this; iteration = 0; automatic_recalc = 255; recalculate_before_saving = TRUE; empty = TRUE; event_it=NULL; undo_on=0; redo_on=0; } void Sheet::add_graph_deps(Graph *gr) { Range_iter rang(gr->range); Cell *cell2; Ref rf; for(rang.first(rf); !rang.last(); rang.next(rf)){ cell2 = cellFind(rf.col,rf.row); cell2->addDependence(gr, GRAPH_DEP); } } void Sheet::remove_graph_deps(Graph *gr) { Range_iter rang(gr->range); Cell *cell2; Ref rf; for(rang.first(rf); !rang.last(); rang.next(rf)){ cell2 = cellFind(rf.col,rf.row); cell2->removeDependence(gr, GRAPH_DEP); } } void Sheet::fill_widget_names(char *nam) { char command[256]; int code; name = nam; sprintf(command,"canvasFromSheet %s",nam); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); sprintf(canvas,"%s",interp->result); sprintf(command,"hScrollFromSheet %s",nam); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); sprintf(hscroll,"%s",interp->result); sprintf(command,"vScrollFromSheet %s",nam); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); sprintf(vscroll,"%s",interp->result); sprintf(command,"colLabelsFromSheet %s",nam); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); sprintf(col_labels,"%s",interp->result); sprintf(command,"rowLabelsFromSheet %s",nam); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); sprintf(row_labels,"%s",interp->result); } Sheet::~Sheet() { Tcl_HashSearch pt; Tcl_HashEntry *entry; Cell *cell; Format *fmt; for( entry = Tcl_FirstHashEntry(&cells,&pt); entry ; entry = Tcl_NextHashEntry(&pt)) { cell = (Cell*)Tcl_GetHashValue(entry); delete cell; } for( entry = Tcl_FirstHashEntry(&col_formats,&pt); entry ; entry = Tcl_NextHashEntry(&pt)) { fmt = (Format*)Tcl_GetHashValue(entry); delete fmt; } for( entry = Tcl_FirstHashEntry(&row_formats,&pt); entry ; entry = Tcl_NextHashEntry(&pt)) { fmt = (Format*)Tcl_GetHashValue(entry); delete fmt; } delete [] stack; Tcl_DeleteHashTable(&cells); } void Sheet::update() { static char *command="update"; Tcl_Eval(interp,command); } void Sheet::redraw() { Tcl_HashSearch pt; Tcl_HashEntry *entry; Cell *cell; int i; int cnt=0; char command[256]; int code; /* Need to invalidade item names */ for( entry = Tcl_FirstHashEntry(&(cells),&pt); entry ; entry = Tcl_NextHashEntry(&pt)) { cell = (Cell*)Tcl_GetHashValue(entry); cell->item = NULL; cell->format.bg.item = NO_ITEM; cell->border_item = NO_ITEM; } for( entry = Tcl_FirstHashEntry(&cells,&pt); entry ; entry = Tcl_NextHashEntry(&pt)) { cell = (Cell*)Tcl_GetHashValue(entry); display(cell,FALSE); cnt++; // if ((cnt % UPDATE_RATE) == 0) // update(); } } void Sheet::set_col_width(int col, int wid) { int diff; int i; Sheet_format frm; frm.type = CODE_COLW1; frm.c_val1 = wid / DEFAULT_FONT_WIDTH+1; frm.sh_val1 = col; formats += frm; frm.type = CODE_COLW2; frm.c_val1 = wid; frm.sh_val1 = col; formats += frm; diff = wid-cell_width(col); for(i=col+1; icol_origin[i] += diff; } } void Sheet::set_row_height(int row, int heig) { int diff; int i; Sheet_format frm; frm.type = CODE_ROWH1; frm.c_val1 = heig; frm.sh_val1 = row; formats += frm; // diff = heig-cell_height(row)+INTERNAL_CELL_BORDER; diff = heig-cell_height(row); for(i=row+1; irow_origin[i] += diff; } } int Sheet::recalc(array &cells) { Cell *cell2; int i; Tcl_HashEntry *entry; Tcl_HashSearch pt; for(i=0; icol:%d\tcell->row:%d\ncell->val:%d\ncell->error_code:%d\ncell->formval:%f\n****************\n",cell->col,cell->row,cell->val,cell->error_code,(cell->type==CODE_FORMULA ? cell->formula->value : 0.1998)); /* Now, for the dirty cells */ for( entry = Tcl_FirstHashEntry(&dirty_cells,&pt); entry ; entry = Tcl_NextHashEntry(&pt)) { cell2 = (Cell*)Tcl_GetHashValue(entry); depth_first_search(cell2); } if (recalculate_list_of_cells() == FALSE) { evalCell(cell); display(cell); } /* cerr << "List : "; for (i=0; icol << "," << cells_to_eval[i].cell->row << ") "; }*/ } int Sheet::recalculate_list_of_cells() { int i; Cell *cell2; Graph *graph2; static char command[256]; int code; /*******************************************************************/ if (automatic_recalc) { for(i=cells_to_eval.cnt-1; i>=0; i--) { // for(i=0;i<=cells_to_eval.cnt-1; i++) { if (cells_to_eval[i].type == CELL_DEP) { cell2 = (Cell*)cells_to_eval[i].cell; evalCell(cell2); display(cell2); } else if (cells_to_eval[i].type == GRAPH_DEP) { graph2 = (Graph*)cells_to_eval[i].cell; sprintf(command,"doRedrawGraph %d",graph2->number); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); } } cells_to_eval.cnt = 0; iteration++; return(TRUE); } return(FALSE); /* No cells recalculated */ /**********************************************************************/ } void Sheet::depth_first_search(Cell *cell) { int i; Cell *cell2; cell->tag = 1; for(i=0; ideps.cnt; i++) { if (cell->deps[i].type == CELL_DEP) { cell2 = (Cell *)cell->deps[i].cell; if (cell2->tag) { char buf[256]; int code; if (! cell2->dirty) { sprintf((char*)buf, "warnUser {Dependence loop detected for cell %s!\nResults will not be correct for some cells. }",cell_name_rc(cell2->col,cell2->row)); code = Tcl_Eval(interp,(char*)buf); } cell2->dirty = TRUE; add_to_dirty(cell2); } else { if (cell2->iter == iteration) continue; depth_first_search(cell2); } } else { Graph *graph; graph = (Graph*)cell->deps[i].cell; if (graph->iter != iteration) cells_to_eval[cells_to_eval.cnt++] = cell->deps[i]; graph->iter = iteration; } } cell->tag = 0; if (cell->iter != iteration) { cells_to_eval[cells_to_eval.cnt].cell = cell; cells_to_eval[cells_to_eval.cnt++].type = CELL_DEP; cell->iter = iteration; } } void Sheet::global_recalc() { Tcl_HashSearch pt; Tcl_HashEntry *entry; Cell *cell; int i; for( entry = Tcl_FirstHashEntry(&cells,&pt); entry ; entry = Tcl_NextHashEntry(&pt)) { cell = (Cell*)Tcl_GetHashValue(entry); evalCell(cell); } } void Sheet::display(Cell *cell, int clear) { char command[256]; int x1,x2,x,y1,y2,y,ox1,ox2; int code; Stack_elem *res; char *form; char *st; Tk_Item *item; Cell *cell2; short c1,c2 = -1,c; int needs_rectangle=FALSE; int i,j; char *ft; int cx1,cx2; int dx; /* Has same role as in tkCanvas.c */ int position; ox1 = x1 = x_coord(cell->col,canvas_info); ox2 = x2 = x_coord(cell->col+1,canvas_info); y1 = y_coord(cell->row,canvas_info); y2 = y_coord(cell->row+1,canvas_info); y = (y1+y2)/2+1; /* Now, draw all cells aligned at right. This will change when cell formats are in */ if (clear) /* apagar o conteudo antigo da celula */ { if (cell->item) { sprintf(command,"%s delete %d",canvas,cell->item->id); code = Tcl_Eval(interp,command); cell->item = NULL; } if (cell->format.bg.item != NO_ITEM) { sprintf(command,"%s delete %d",canvas,cell->format.bg.item); code = Tcl_Eval(interp,command); cell->format.bg.item = NO_ITEM; } if (cell->border_item != NO_ITEM) { sprintf(command,"%s delete %d",canvas,cell->border_item); code = Tcl_Eval(interp,command); cell->border_item = NO_ITEM; } } if (cell->type != CODE_BLANK) { st = (char *) cell->display(); if (cell->format.alignment > RIGHT_ALIGNMENT) cell->format.alignment = DEFAULT_ALIGNMENT; if (cell->format.alignment==LEFT_ALIGNMENT || (cell->format.alignment==DEFAULT_ALIGNMENT && (cell->type==CODE_LABEL || (cell->type==CODE_FORMULA && cell->formula->typ==FORM_STRING)))) { /* alinhamento a esquerda*/ x = x1+INTERNAL_CELL_BORDER+1; dx = x; position = WEST; } else if (cell->format.alignment==RIGHT_ALIGNMENT || cell->format.alignment==DEFAULT_ALIGNMENT) { /* alinhamento a direita */ x = x2-INTERNAL_CELL_BORDER; dx = x; position = EAST; } else if (cell->format.alignment == CENTER_ALIGNMENT) { /* alinhamento ao centro */ x = (x1+x2)/2; dx = x; position = CENTER; } else { x = x2-INTERNAL_CELL_BORDER; position = EAST; } if (cell->format.font.isdefault()) { ft = default_font_name; } else ft = getFont(interp,cell->format.font); if (cell->type != CODE_BLANK) { cell->item = draw_text(st,x,y,position,cell->col,cell->row,ft); cx1 = cell->item->x1+dx; cx2 = cell->item->x2+dx; cell->wid = cell->item->x2-cell->item->x1; } else { cx1 = x_coord(cell->col,canvas_info)+dx; cx2 = x_coord(cell->col+1,canvas_info)+dx; cell->wid = 0; } /* First, simple case. Cell needs rectangle because it overflows */ if (cx1 < x1 || cx2 > x2) needs_rectangle = TRUE; if (cell->type != CODE_LABEL && needs_rectangle && (cell->type != CODE_FORMULA || cell->formula->typ != FORM_STRING)) { static char buf[20]; needs_rectangle = FALSE; c = (int)((float)(cell_width(cell->col))/(cx2-cx1)*strlen(st)-1); cx1 = x1+1; cx2 = x2-1; for(i=0; iitem->id); code = Tcl_Eval(interp,command); cell->item = draw_text(buf,x,y,position,cell->col,cell->row,ft); } /* Second, more complex case. Cell needs rectangle because another cell overflows into it Cells to the left. We will arbitrarly scan only 5 cells to our left */ i = findIndex(rows[cell->row],cell->col,1); for (j=i-1; j>=0 && i-j<8; j--) { cell2 = rows[cell->row].cells[j]; if (cell2->item == NULL) continue; if (cell2->item && cell2->item->x2 - cell2->item->x1 > col_origin(cell->col)- col_origin(cell2->col)) { needs_rectangle = TRUE; break; } } for (j=i+1; jrow].cells.cnt && j-i<8; j++) { int dx1; cell2 = rows[cell->row].cells[j]; if (cell2->item == NULL) continue; if (cell2->item && cell2->item->x1+x2 < x_coord(cell->col+1,canvas_info)) { /* AML - Probably wrong */ /*needs_rectangle = TRUE;*/ break; } } if ((needs_rectangle && cell->type != CODE_BLANK) || !cell->format.bg.isdefault() || !cell->format.borders.isdefault()) { /* Compute coordinates */ cell->has_rectangle = TRUE; for (c1 = cell->col; c1>0 && x_coord(c1,canvas_info) > cx1; ) c1--; for(c2=cell->col+1;c2col; c2 = c1+1; } if((needs_rectangle && cell->type!=CODE_BLANK)|| !cell->format.bg.isdefault()) { /* Draw fill area */ int fl = cell->filllevel(); if (fl != 100) sprintf(command, "%s create rec %d %d %d %d X %d %d %d %d -fill gray%d -outline gray%d " ,canvas,0,0,0,0,c1,cell->row,c2,cell->row+1, cell->filllevel(),cell->filllevel()); else sprintf(command, "%s create rec %d %d %d %d X %d %d %d %d -fill white -outline white " ,canvas,0,0,0,0,c1,cell->row,c2,cell->row+1); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); cell->format.bg.item = atoi(interp->result); if (display_cell_borders) { sprintf(command,"%s raise %s %d",canvas, interp->result,borderItemId); code = Tcl_Eval(interp,command); } else { sprintf(command,"%s lower %s",canvas, interp->result); code = Tcl_Eval(interp,command); } if (display_cell_borders && cell->format.bg.item != NO_ITEM) { sprintf(command, "%s create rect %d %d %d %d X %d %d %d %d -outline blue -tag cellborders" ,canvas,0,0,0,0, c1,cell->row,c2,cell->row+1); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); if (cell->format.bg.item != NO_ITEM) { sprintf(command,"%s raise %s %d",canvas, interp->result,cell->format.bg.item); code = Tcl_Eval(interp,command); } cell->border_item = atoi(interp->result); } else cell->border_item = NO_ITEM; } else /* Doesn't need rectangle */ cell->format.bg.item = NO_ITEM; if (!cell->format.borders.isdefault()) /* Draw borders */ draw_borders(cell,x1,y1,x2,y2); /* And now, redraw the ones to the right, if needed */ if (cell->type != CODE_BLANK) if (cx1 < ox1 || cx2 > ox2) for (j=i+1; jrow].cells.cnt && j-i<5; j++) { cell2 = rows[cell->row].cells[j]; if (cell2->item == NULL) continue; if (cx2 > x_coord(cell2->col,canvas_info)) display(cell2,TRUE); else break; } code = TCL_OK; if (code != TCL_OK) internal_error(); } void Sheet::display(array &cells,int clear) { for(int i=0; iformat.borders.top != DEFAULT_BORDER) { if (cell->format.borders.top_item != NO_ITEM) { sprintf(command,"%s delete %d", canvas,cell->format.borders.top_item ); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); /* Now, the case for the double lines */ if (cell->format.borders.top_item_double ) { sprintf(command,"%s delete %d", canvas,cell->format.borders.top_item-1); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); } } } if (cell->format.borders.bottom != DEFAULT_BORDER) { if (cell->format.borders.bottom_item != NO_ITEM) { sprintf(command,"%s delete %d", canvas,cell->format.borders.bottom_item ); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); /* Now, the case for the double lines */ if (cell->format.borders.bottom_item_double ) { sprintf(command,"%s delete %d", canvas,cell->format.borders.bottom_item-1); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); } } } if (cell->format.borders.left != DEFAULT_BORDER) { if (cell->format.borders.left_item != NO_ITEM) { sprintf(command,"%s delete %d", canvas,cell->format.borders.left_item ); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); /* Now, the case for the double lines */ if (cell->format.borders.left_item_double ) { sprintf(command,"%s delete %d", canvas,cell->format.borders.left_item-1); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); } } } if (cell->format.borders.right != DEFAULT_BORDER) { if (cell->format.borders.right_item != NO_ITEM) { sprintf(command,"%s delete %d", canvas,cell->format.borders.right_item ); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); /* Now, the case for the double lines */ if (cell->format.borders.right_item_double ) { sprintf(command,"%s delete %d", canvas,cell->format.borders.right_item-1); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); } } } if (cell->format.borders.top != DEFAULT_BORDER) cell->format.borders.top_item_double = FALSE; if (cell->format.borders.bottom != DEFAULT_BORDER) cell->format.borders.bottom_item_double = FALSE; if (cell->format.borders.left != DEFAULT_BORDER) cell->format.borders.left_item_double = FALSE; if (cell->format.borders.right != DEFAULT_BORDER) cell->format.borders.right_item_double = FALSE; switch(cell->format.borders.top) { case DEFAULT_BORDER: break; case 0: cell->format.borders.top_item = NO_ITEM; break; case 1: sprintf(command, "%s create line %d %d %d %d X %d %d %d %d -width %d", canvas,x1,y1,x2,y1, cell->col,cell->row,cell->col+1,cell->row, (int)cell->format.borders.top); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); cell->format.borders.top_item = atoi(interp->result); break; case 2: sprintf(command, "%s create line %d %d %d %d X %d %d %d %d -width %d", canvas,x1,y1+1,x2,y1+1, cell->col,cell->row,cell->col+1,cell->row, (int)cell->format.borders.top); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); cell->format.borders.top_item = atoi(interp->result); break; case 3: sprintf(command, "%s create line %d %d %d %d X %d %d %d %d -width %d", canvas,x1,y1+1,x2,y1+1, cell->col,cell->row,cell->col+1,cell->row, (int)cell->format.borders.top); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); cell->format.borders.top_item = atoi(interp->result); break; case 5: sprintf(command, "%s create line %d %d %d %d X %d %d %d %d -width %d", canvas,x1,y1,x2,y1, cell->col,cell->row,cell->col+1,cell->row,1); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); sprintf(command, "%s create line %d %d %d %d X %d %d %d %d -width %d", canvas,x1,y1+2,x2,y1+2, cell->col,cell->row,cell->col+1,cell->row,1); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); cell->format.borders.top_item_double = TRUE; cell->format.borders.top_item = atoi(interp->result); break; default: cerr << "Unknown border type\n"; } switch(cell->format.borders.bottom) { case DEFAULT_BORDER: break; case 0: cell->format.borders.bottom_item = NO_ITEM; break; case 1: sprintf(command, "%s create line %d %d %d %d X %d %d %d %d -width %d", canvas,x1,y2-1,x2,y2-1, cell->col,cell->row+1,cell->col+1,cell->row+1, (int)cell->format.borders.bottom); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); cell->format.borders.bottom_item = atoi(interp->result); break; case 2: sprintf(command, "%s create line %d %d %d %d X %d %d %d %d -width %d", canvas,x1,y2-1,x2,y2-1, cell->col,cell->row+1,cell->col+1,cell->row+1, (int)cell->format.borders.bottom); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); cell->format.borders.bottom_item = atoi(interp->result); break; case 3: sprintf(command, "%s create line %d %d %d %d X %d %d %d %d -width %d", canvas,x1,y2-2,x2,y2-2, cell->col,cell->row+1,cell->col+1,cell->row+1, (int)cell->format.borders.bottom); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); cell->format.borders.bottom_item = atoi(interp->result); break; case 5: sprintf(command, "%s create line %d %d %d %d X %d %d %d %d -width %d", canvas,x1,y2-1,x2,y2-1, cell->col,cell->row+1,cell->col+1,cell->row+1, 1); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); sprintf(command, "%s create line %d %d %d %d X %d %d %d %d -width %d", canvas,x1,y2-3,x2,y2-3, cell->col,cell->row+1,cell->col+1,cell->row+1, 1); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); cell->format.borders.bottom_item_double = TRUE; cell->format.borders.bottom_item = atoi(interp->result); break; default: cerr << "Unknown border type\n"; } switch(cell->format.borders.left) { case DEFAULT_BORDER: break; case 0: cell->format.borders.left_item = NO_ITEM; break; case 1: sprintf(command, "%s create line %d %d %d %d X %d %d %d %d -width %d", canvas,x1,y1,x1,y2, cell->col,cell->row,cell->col,cell->row+1, (int)cell->format.borders.left); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); cell->format.borders.left_item = atoi(interp->result); break; case 2: sprintf(command, "%s create line %d %d %d %d X %d %d %d %d -width %d", canvas,x1+1,y1,x1+1,y2, cell->col,cell->row,cell->col,cell->row+1, (int)cell->format.borders.left); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); cell->format.borders.left_item = atoi(interp->result); break; case 3: sprintf(command, "%s create line %d %d %d %d X %d %d %d %d -width %d", canvas,x1+1,y1,x1+1,y2, cell->col,cell->row,cell->col,cell->row+1, (int)cell->format.borders.left); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); cell->format.borders.left_item = atoi(interp->result); break; case 5: sprintf(command, "%s create line %d %d %d %d X %d %d %d %d -width %d", canvas,x1,y1,x1,y2, cell->col,cell->row,cell->col,cell->row+1,1); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); sprintf(command, "%s create line %d %d %d %d X %d %d %d %d -width %d", canvas,x1+2,y1,x1+2,y2, cell->col,cell->row,cell->col,cell->row+1,1); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); cell->format.borders.left_item_double = TRUE; cell->format.borders.left_item = atoi(interp->result); break; default: cerr << "Unknown border type\n"; } switch(cell->format.borders.right) { case DEFAULT_BORDER: break; case 0: cell->format.borders.right_item = NO_ITEM; break; case 1: sprintf(command, "%s create line %d %d %d %d X %d %d %d %d -width %d", canvas,x2-1,y1,x2-1,y2, cell->col+1,cell->row,cell->col+1,cell->row+1, (int)cell->format.borders.right); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); cell->format.borders.right_item = atoi(interp->result); break; case 2: sprintf(command, "%s create line %d %d %d %d X %d %d %d %d -width %d", canvas,x2-1,y1,x2-1,y2, cell->col+1,cell->row,cell->col+1,cell->row+1, (int)cell->format.borders.right); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); cell->format.borders.right_item = atoi(interp->result); break; case 3: sprintf(command, "%s create line %d %d %d %d X %d %d %d %d -width %d", canvas,x2-2,y1,x2-2,y2, cell->col+1,cell->row,cell->col+1,cell->row+1, (int)cell->format.borders.right); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); cell->format.borders.right_item = atoi(interp->result); break; case 5: sprintf(command, "%s create line %d %d %d %d X %d %d %d %d -width %d", canvas,x2-1,y1,x2-1,y2, cell->col+1,cell->row,cell->col+1,cell->row+1,1); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); sprintf(command, "%s create line %d %d %d %d X %d %d %d %d -width %d", canvas,x2-3,y1,x2-3,y2, cell->col+1,cell->row,cell->col+1,cell->row+1,1); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); cell->format.borders.right_item_double = TRUE; cell->format.borders.right_item = atoi(interp->result); break; default: cerr << "Unknown border type\n"; } } int Sheet::evalCell(Cell *cell) { Stack_elem *result; double prev_value; Cell *c; cell->error_code = NO_ERROR; switch(cell->type) { case CODE_BLANK: cell->value = 0; break; case CODE_FORMULA: prev_value = cell->value; result = evaluate(cell); if ((cell->error_code = result->contents.error_code) != NO_ERROR) {} else switch(result->type) { case FORM_REF: c = cellFind(result->contents.ref_val); switch(c->type) { case CODE_LABEL: if (cell->formula->st_value == NULL || strlen(cell->formula->st_value) < strlen(c->label)){ delete [] cell->formula->st_value; cell->formula->st_value = strsave(c->label+1); } else { strcpy(cell->formula->st_value,c->label+1); } cell->formula->typ = FORM_STRING; break; default: cell->value = cell->formula->value = stack_value(result); if (VALUE_DIFFERENT(prev_value,cell->value) ) cell->modified = TRUE; cell->formula->typ = FORM_FP; break; } break; case FORM_INT: case FORM_FP: cell->value = cell->formula->value = stack_value(result); if (VALUE_DIFFERENT(prev_value,cell->value) ) cell->modified = TRUE; cell->formula->typ = FORM_FP; break; case FORM_STRING: if (cell->formula->st_value == NULL || strlen(cell->formula->st_value) < strlen(result->contents.string_val)+1){ delete [] cell->formula->st_value; cell->formula->st_value = strsave(result->contents.string_val); cell->value = cell->formula->value = 0; } else { strcpy(cell->formula->st_value,result->contents.string_val); } cell->formula->typ = FORM_STRING; break; default: internal_error(); break; } break; case CODE_NUMBER: case CODE_INTEGER: break; case CODE_LABEL: cell->value = 0; break; default: internal_error(); } } Stack_elem *Sheet::cellRef(short col, short row) { Cell *cell; static Stack_elem sta; sta.contents.ref_val.col = col; sta.contents.ref_val.row = row; sta.contents.error_code = NO_ERROR; sta.type = FORM_REF; if (!CHECK_COL_BOUNDS(col) || !CHECK_ROW_BOUNDS(row)) { sta.contents.error_code = ERROR_BAD_REF; }; return(&sta); } void Sheet::add_to_dirty(Cell *cell) { int CellCoords[2]; Tcl_HashEntry *entry; int newv; CellCoords[0] = cell->col; CellCoords[1] = cell->row; entry = Tcl_CreateHashEntry(&dirty_cells,(char *)CellCoords,&newv); Tcl_SetHashValue(entry,cell); } void Sheet::remove_from_dirty(Cell *cell) { int CellCoords[2]; Tcl_HashEntry *entry; int newv; CellCoords[0] = cell->col; CellCoords[1] = cell->row; entry = Tcl_FindHashEntry(&dirty_cells,(char *)CellCoords); if (entry == NULL) internal_error(); Tcl_DeleteHashEntry(entry); } Cell *Sheet::cellSet(short col, short row, char *form) { Cell *cell; empty = FALSE; cell = cellFind(col,row); if (cell->formula){ manipulateReferences(cell,REMOVE_REFERENCES); delete cell->formula; cell->formula = NULL; } cell->set(form,parser); if (cell->type == CODE_FORMULA) manipulateReferences(cell,INSERT_REFERENCES); modified_after_save = TRUE; if (cell->dirty) remove_from_dirty(cell); cell->dirty = FALSE; cell->iter = -1; return(cell); } char *Sheet::stack_string(Stack_elem *p) { Cell *c; Stack_elem *s; switch(p->type) { case FORM_STRING: return(p->contents.string_val); break; case FORM_REF: c = cellFind(p->contents.ref_val); switch(c->type) { case CODE_LABEL: return(c->label+1); break; case CODE_FORMULA: return(c->formula->st_value); break; default: return(""); break; } default: break; } } double Sheet::stack_value(Stack_elem *p) { Cell *c; Stack_elem *s; switch (p->type) { case FORM_FP: return(p->contents.fp_val); case FORM_INT: return((double)p->contents.int_val); case FORM_REF: c = cellFind(p->contents.ref_val); s = c->val(); return(STACK_VALUE(s)); } } Cell *Sheet::cellExists(short col, short row) { int CellCoords[2]; Tcl_HashEntry *entry; int newv; Cell *cell; CellCoords[0] = col; /* AML */ CellCoords[1] = row; /* AML */ entry = Tcl_FindHashEntry(&cells,(char *)CellCoords); if (entry != NULL) { cell = (Cell*)Tcl_GetHashValue(entry); return(cell); } else { return(NULL); } } Cell *Sheet::cellFind(Ref &ref) { return(cellFind(ref.col,ref.row)); } Cell *Sheet::cellFind(short col, short row) { int CellCoords[2]; Tcl_HashEntry *entry,*format_entry; int newv; Cell *cell,*aux; Format *format=NULL; CellCoords[0] = col; /* AML */ CellCoords[1] = row; /* AML */ if (col > max_col) max_col = col; if (row > max_row) max_row = row; if (col > max_col_used) max_col_used = col; if (row > max_row_used) max_row_used = row; if (col < min_col_used) min_col_used = col; if (row < min_row_used) min_row_used = row; entry = Tcl_CreateHashEntry(&cells,(char *)CellCoords,&newv); if (newv == 1) /* criar entrada nova */ { cell = new Cell(col,row); if((format_entry=Tcl_FindHashEntry(&row_formats,(char *) row))!=NULL) cell->mergeformats((Format*) Tcl_GetHashValue(format_entry)); if((format_entry=Tcl_FindHashEntry(&col_formats,(char *) col))!=NULL) cell->mergeformats((Format*) Tcl_GetHashValue(format_entry)); Tcl_SetHashValue(entry,cell); insertCell(cell); modified_after_save = TRUE; } else /* aceder a entrada ja existente */ cell = (Cell*)Tcl_GetHashValue(entry); return(cell); } void Sheet::insertInCellList(Cell *cell) { int CellCoords[2]; Cell *cell2; int newv; Tcl_HashEntry *entry; CellCoords[0] = cell->col; /* AML */ CellCoords[1] = cell->row; /* AML */ if (cell->col > max_col) max_col = cell->col; if (cell->row > max_row) max_row = cell->row; if (cell->col > max_col_used) max_col_used = cell->col; if (cell->row > max_row_used) max_row_used = cell->row; if (cell->col < min_col_used) min_col_used = cell->col; if (cell->row < min_row_used) min_row_used = cell->row; entry = Tcl_CreateHashEntry(&cells,(char *)CellCoords,&newv); if (newv == 1) { Tcl_SetHashValue(entry,cell); modified_after_save = TRUE; } else { cell2 = (Cell*)Tcl_GetHashValue(entry); delete cell2; } } void Sheet::removeCell(Cell *cell) { int r,c; int i,j; Cell *pt; short size; r = cell->row; c = cell->col; if (r < 0 || c < 0) internal_error(); /* Locate position in row */ i = findIndex(rows[r],c,1); for(j=i;jrow; c = cell->col; if (r < 0 || c < 0) internal_error(); i = findIndex(rows[r],c,1); for (j=rows[r].cells.cnt; j>i; j--) { rows[r].cells[j] = rows[r].cells[j-1]; } rows[r].cells[i] = cell; rows[r].cells.cnt++; /* Locate position in col */ i = findIndex(cols[c],r,0); for (j=cols[c].cells.cnt; j>i; j--) { cols[c].cells[j] = cols[c].cells[j-1]; } cols[c].cells[i] = cell; cols[c].cells.cnt++; /* cerr << "New list of cells for row " << r << " " ; for (i=0; icol << "," << pt->row << ") "; } cerr << "\nNew list of cells for col " << c << " " ; for (i=0; icol << "," << pt->row << ") "; } cerr << "\n";*/ } void Sheet::copyToBuffer(Cell *cell, Cell_buffer &buffer) { buffer.cells += cell; } void Sheet::removeFromCellList(Cell *cell) { int CellCoords[2]; Cell *cell2; Tcl_HashEntry *entry; CellCoords[0] = cell->col; /* AML */ CellCoords[1] = cell->row; /* AML */ entry = Tcl_FindHashEntry(&cells,(char *)CellCoords); if (entry == NULL) { internal_error(); } else Tcl_DeleteHashEntry(entry); } short Sheet::findIndex(Rc_info &arr,short c, int rc /* 0 = row , 1 = col */) { short cnt,size,i; Cell *pt; short x; cnt = arr.cells.cnt; for (i = cnt/2, size = MAX(cnt/2,1); i != cnt ; ) { if (size > 1) size = size/2; pt = arr.cells[i]; x = rc ? pt->col : pt->row; if (x == c) return(i); if (x < c) { i = i+size; continue; } if (i == 0) break; pt = arr.cells[i-1]; x = rc ? pt->col : pt->row; if (x == c) return(i-1); if (x > c) { i = i-size; continue; } break; } return(i); } int Sheet::max_text_width(short col) { Cell *cell; int max = 0; int wid,i; for (i=0; itype != CODE_BLANK) wid = cell->wid; else wid = 0; max = MAX(max,wid); } return(max); } int Sheet::text_width(Cell *cell) { if (cell->type != CODE_BLANK) { if (cell->item == NULL) internal_error(); return(cell->item->x2-cell->item->x1); } else { return(0); } } int Sheet::propagate_error(Stack_elem *ptfrom, Stack_elem *ptto) { if (ptfrom->type == FORM_REF) { Cell *cell; if (CHECK_COL_BOUNDS(ptfrom->contents.ref_val.col) && CHECK_ROW_BOUNDS(ptfrom->contents.ref_val.row)) { cell = cellFind(ptfrom->contents.ref_val); if (cell->error_code != NO_ERROR) { ptto->contents.error_code = cell->error_code ; return(TRUE); } } else { ptto->contents.error_code = ERROR_BAD_REF; return(TRUE); } } if (ptfrom->contents.error_code != NO_ERROR) { ptto->contents.error_code = ptfrom->contents.error_code ; return(TRUE); } return(FALSE); } int Sheet::propagate_error(Cell *cell, Stack_elem *ptto) { if (cell->error_code != NO_ERROR) { ptto->contents.error_code = cell->error_code ; return(TRUE); } return(FALSE); } void Sheet::SetUndoRedoGUI(int UndoRedo, int OnOff) { char command[256]; int code; if((UndoRedo==0 ? undo_on : redo_on)==OnOff) return; sprintf(command, ".%s.fr.header.standard.g3.%s configure -state %s", (char*) name, UndoRedo==0 ? "undo" : "redo", OnOff==1 ? "normal" : "disabled"); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); sprintf(command, ".%s.fr.menubar.menu.edit.menu entryconfigure %s -state %s", (char*) name, UndoRedo==0 ? "Undo" : "Redo", OnOff==1 ? "normal" : "disabled"); code = Tcl_Eval(interp,command); if (code != TCL_OK) internal_error(); if(UndoRedo==0) undo_on=OnOff; else redo_on=OnOff; } void Sheet::RegisterEvent(Event *ev) { Event *aux; if(event_l.IsEmpty()) { if((event_it=event_l.AddHead(ev))==NULL) internal_error(); SetUndoRedoGUI(UNDO,ON); return; } if(event_it!=NULL) while(!event_it->IsFirst()) aux=event_l.RemoveHead(); else while(!event_l.IsEmpty()) aux=event_l.RemoveHead(); if((event_it=event_l.AddHead(ev))==NULL) internal_error(); if(event_l.GetCount()>UNDO_DEPTH) event_l.RemoveTail(); SetUndoRedoGUI(UNDO,ON); SetUndoRedoGUI(REDO,OFF); } void Sheet::Undo() { Event *aux; if(event_it==NULL) return; if((aux=event_l.GetNext(&event_it))==NULL) return; aux->UndoEvent(); SetUndoRedoGUI(REDO,ON); if(event_it==NULL) SetUndoRedoGUI(UNDO,OFF); } void Sheet::Redo() { Event *aux; int i; if(event_it==NULL) { if(event_l.IsEmpty()) return; aux=event_l.GetTail(); event_it=event_l.GetTailPosition(); } else if(event_it->IsFirst()) return; else { event_l.GetPrev(&event_it); aux=event_l.GetAt(event_it); } aux->RedoEvent(); SetUndoRedoGUI(UNDO,ON); if(event_it->IsFirst()) SetUndoRedoGUI(REDO,OFF); } void Sheet::RunScript(char *buf) { int code; code = Tcl_Eval(interp,buf); if (code != TCL_OK) internal_error(); } int Sheet::RunScriptFromFile(char *filename) { int code; return(Tcl_EvalFile(interp,filename)); } // $Log: sheet.cc,v $ // Revision 1.53 1998/10/20 19:08:48 cthulhu // Fixed bug when checking col and row bounds inside propagate_error. // // Revision 1.52 1998/08/25 20:57:28 cthulhu // Added RunScriptFromFile to run user-based scripts (macros). // // Revision 1.51 1998/08/10 12:19:42 aml // Fixed evaluation bug. // // Revision 1.50 1998/08/06 21:08:47 aml // Released alpha version of Abacus. // // Revision 1.49 1997/02/10 15:11:06 aml // Print settings now are saved to file. // Fixed buggy error message when loading sheets. // // Revision 1.48 1997/01/07 01:07:48 aml // Error propagation for formulas fixed. // Edit operations in place. // // Revision 1.47 1997/01/02 16:15:35 aml // Fixed unsufficient range of colunm width. // First cut of vlookup and hlookup functions. // Fixed bug in display routines. // // Revision 1.46 1996/12/31 17:38:45 aml // On-demand calculation improved. Loops are detected. // Automatic recalculation can now be disabled. // Printing was improved. // // Revision 1.45 1996/12/11 21:40:05 aml // Sumif implemented. // Diverse time functions implemented. // Fixed needtoscroll2 to avoid out of control scroll. // // Revision 1.44 1996/11/22 16:29:22 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.43 1996/10/09 13:55:10 aml // First cut of full print. // // Revision 1.42 1996/10/07 12:35:44 aml // First cut at error handling. // Date formats are in. // Fixed problem with blank cell drawing. // // Revision 1.41 1996/09/19 12:17:13 aml // Created row and column insert. // Fixed small problem with display of vergrown cells. // // Revision 1.40 1996/09/17 15:16:41 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.39 1996/09/16 18:43:02 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.38 1996/09/15 19:25:00 aml // Rulling and shading. // Optionally hide cell borders. // Works well, but is very slow for large spreadsheets. // // Revision 1.37 1996/09/14 23:56:18 aml // Created cell shading. // // Revision 1.36 1996/09/04 14:30:06 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.35 1996/09/02 10:51:54 aml // Cell fonts created, loaded and saved. // Row height created. // // Revision 1.34 1996/08/29 12:05:49 aml // Fixed problem with initialization of formulas. // Insertion in entry is now done properly. // Focus is slightly better handled. // Fixed serious problem when canvas changes name and old // references exist. Also removed double call to redraw that // was slowing things a lot. // // Revision 1.33 1996/08/28 17:18:00 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.32 1996/08/27 17:19:01 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.31 1996/08/26 17:22:32 aml // Function round fixed. // Many other functions added, from power to mod. // // Revision 1.30 1996/08/26 12:08:55 aml // Fixed problem with several sheets. Each canvas now has its own // canvas info. // Fixed scroll up and down. Implemented max_row and max_col. // Fairly stable version. // // Revision 1.29 1996/08/24 10:16:09 aml // Scroll almost fixed. We are missing scroll steps. // Several sheets work again, but need to be fixed right. // // Revision 1.28 1996/08/23 16:13:44 aml // Top window resizing now works well. // Range selection now uses a filled rectangle with overall good results. // Intermediate version, does not work well. // // Revision 1.27 1996/07/23 14:01:16 aml // Changed canvas widget to handle special items like cell borders. // Seems to work well. Spreadsheet size is no more limited now. // // Revision 1.26 1996/07/18 10:19:38 aml // Created formats for cells. // Load cell now makes copy of old file. // // Revision 1.25 1996/04/27 11:12:43 aml // Inserted check and delete button. // Font selection widget created. // Fixed bug canvas_information destructor. // // Revision 1.24 1996/04/23 09:43:07 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:21 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/04/19 10:46:42 aml // First cut at speeding up canvas critical functions. // CanvasWidgetCommand is now called directly from draw_sheet. // Fixed bug in reading values from datafiles. Also works // for Suns now. // Created canvas directory, replacing builtin command canvas. // // Revision 1.21 1996/03/29 21:46:03 aml // Changed key based scrolls to be synchronous. Work fine, but are somewhat slow. // Fixed abnormaly in state machine after range defition causing canvas scroll. // Solid, working version. // // Revision 1.20 1996/03/11 15:47:52 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.19 1996/03/09 08:32:45 aml // Fixed problem in string_single_arg macro // Created if function, boolean evaluations and so on. // Improved print controls. // // Revision 1.18 1996/03/08 19:00:36 aml // Fixed problem in string_single_arg macro // Created if function, boolean evaluations and so on. // // Revision 1.17 1996/03/07 20:33:11 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.16 1996/03/06 20:25:29 aml // Improved user waiting times during redraw and loads by calling update. // // Revision 1.15 1996/03/01 13:08:50 aml // Stack references are now pushed instead of double value. // Fixed problem with integers too large on formulas. // Scroll with cursors now works better. // // Revision 1.14 1996/02/19 15:47:40 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.13 1996/02/16 23:09:42 aml // Improved user interf state machine. // Centralized range definitions. // Range defined outiside current view work properly. // // Revision 1.12 1996/02/16 18:04:13 aml // Fixed a memory bug in formula copy with purify. // Fixed a few minor bugs. Functional, stable version. // // Revision 1.11 1996/02/13 12:03:59 aml // Fixed bug with range definition via mouse. // Fixed bug in range iterators. // // Revision 1.10 1996/01/30 16:05:43 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.9 1996/01/11 22:48:56 aml // Range iterators created. // Functions sum, max and min now work properly. // Negative numbers now allowed by flex (oops :-) // // Revision 1.8 1996/01/10 18:53:45 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.7 1996/01/09 18:34:57 aml // Load, save, open, close and exit now work properly (hopefuly). // Sheet utility functions also work : SheetExists, SheetEmpty, SheetModified // // Revision 1.6 1996/01/07 09:07:53 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.5 1996/01/05 23:06:04 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.4 1996/01/03 23:07:19 aml // Absolute and relative references to cells introduced. // They are parsed and reverse parsed, not yet evaluated. // // Revision 1.3 1996/01/02 16:22:13 aml // Formula compilation, evaluation and decompilation now work. // Cells can be of type label, numerical formula or numbers. // // Revision 1.2 1995/12/27 23:13:07 aml // First draft of Sheet::display // // Revision 1.1 1995/12/13 14:33:20 aml // Initial revision //