// $Id: tcl_interf.cc,v 1.51 1998/10/25 23:38:39 cthulhu Exp $

#include "tcl_interf.hh"
#include "interf_aux.hh"

#define max(a,b) a > b ? a : b
#define min(a,b) a < b ? a : b

Tcl_Interp *it;

Tcl_HashTable sheet_hash;
Tcl_HashTable font_hash;

static char prev_format_result[256];
Paste_buffer paste_buffer;
int sort_dir=1;

void dd(void* pt) {
  Tcl_Eval(it,(char *)pt);
}

int Xxl_Test(ClientData clientData, Tcl_Interp *interp,   
	     int argc, char *argv[]) 
{
  return(TCL_OK);
}

int Xxl_RunScript(ClientData clientData, Tcl_Interp *interp,   
		  int argc, char *argv[]) 
{
  Sheet *sheet;
  char *file,*err;
  
  if((err=CheckArgs(argc,argv,F_RUN_SCRIPT))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet=SheetFromName(argv[1]);
  return(sheet->RunScriptFromFile(argv[2]));
}

int Xxl_UndoRedo(ClientData clientData, Tcl_Interp *interp,   
		 int argc, char *argv[]) {
  Sheet *sheet;
  int opt;
  char *err;
  
  if((err=CheckArgs(argc,argv,F_UNDO_REDO))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet=SheetFromName(argv[1]);
  opt=atoi(argv[2]);
  if((opt=atoi(argv[2]))==0)
    sheet->Undo();
  else
    sheet->Redo();
  return(TCL_OK);
}

int Xxl_ColName(ClientData clientData, Tcl_Interp *interp,   
                int argc, char *argv[]) {
  int col;
  char *err;

  if((err=CheckArgs(argc,argv,F_COL_NAME))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  col = atoi(argv[1]);
  sprintf(interp->result,"%s",coltoa(col));
  return(TCL_OK);
}

int Xxl_CellName(ClientData clientData, Tcl_Interp *interp,   
		 int argc, char *argv[]) {
  int col,row;
  char *err;

  if((err=CheckArgs(argc,argv,F_CELL_NAME))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  col = atoi(argv[1]);
  row = atoi(argv[2]);
  sprintf(interp->result,"%s",cell_name_rc(col,row));
  return(TCL_OK);
}

int Xxl_ParseRange(ClientData clientData, Tcl_Interp *interp,   
		   int argc, char *argv[]) {
  int col;
  unsigned short mask = (3 << 14);
  char *err;

  if((err=CheckArgs(argc,argv,F_PARSE_RANGE))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  Range *rg = range_reference(argv[1],argv[2]);
  sprintf(interp->result,"%d %d %d %d",
	  ~mask & rg->start.col, ~mask  & rg->start.row,
	  ~mask & rg->end.col, ~mask & rg->end.row);
  return(TCL_OK);
}

int Xxl_NewSheet(ClientData clientData, Tcl_Interp *interp,   
                 int argc, char *argv[]) {
  int newv;
  Sheet *sheet,*lixo;
  Tcl_HashEntry *entry;
  char *err;

  if((err=CheckArgs(argc,argv,F_NEW_SHEET))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet = CreateSheet(argv[1],interp); /* Ignore result */
  global_col_origin = sheet->canvas_info->col_origin;
  global_row_origin = sheet->canvas_info->row_origin;
  return(TCL_OK);
}

int Xxl_ChangeSheetName(ClientData clientData, Tcl_Interp *interp,   
			int argc, char *argv[]) {
  Sheet *sheet;
  int newv;
  int code;
  char command[120];
  Tcl_HashEntry *entry;
  Cell *cell;
  Tcl_HashSearch pt;
  char *err;

  if((err=CheckArgs(argc,argv,F_CHANGE_SHEET_NAME))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  entry = Tcl_FindHashEntry(&sheet_hash,argv[1]);
  if (entry == NULL) 
    return(TCL_ERROR);
  sheet = (Sheet*) Tcl_GetHashValue(entry);
  Tcl_DeleteHashEntry(entry);
  entry = Tcl_CreateHashEntry(&sheet_hash,argv[2],&newv);
  if (newv == 0)
    return(TCL_ERROR);
  Tcl_SetHashValue(entry,sheet);
  sheet->fill_widget_names(argv[2]);
  /* Need to invalidade item names */
  for( entry = Tcl_FirstHashEntry(&(sheet->cells),&pt); 
       entry ; entry = Tcl_NextHashEntry(&pt)) {
    cell = (Cell*)Tcl_GetHashValue(entry);
    cell->item = NULL;
  }
  deleteCanvas(sheet->canvas_info);
  return(TCL_OK);
}

int Xxl_SheetExists(ClientData clientData, Tcl_Interp *interp,   
		    int argc, char *argv[]) {
  Sheet *sheet;
  char *err;

  if((err=CheckArgs(argc,argv,F_SHEET_EXISTS))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet = SheetFromName(argv[1]);
  if (sheet == NULL)
    strcpy(interp->result,"0");
  else
    strcpy(interp->result,"1");
  return(TCL_OK);
}

int Xxl_SetPrintRange(ClientData clientData, Tcl_Interp *interp,   
		      int argc, char *argv[]) 
{
  Sheet *sheet;
  char *err;
  
  if((err=CheckArgs(argc,argv,F_SET_PRINT_RANGE))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet = SheetFromName(argv[1]);
  sheet->psettings.print_selection = atoi(argv[2]);
  if (sheet->psettings.print_selection == PRINT_PRINT_RANGE) 
    {
      sheet->print_range.start.col = atoi(argv[3]);
      sheet->print_range.start.row = atoi(argv[4]);
      sheet->print_range.end.col = atoi(argv[5]);
      sheet->print_range.end.row = atoi(argv[6]);
    }
  sheet->modified_after_save = TRUE;
  return(TCL_OK);
}

int Xxl_GetPrintRange(ClientData clientData, Tcl_Interp *interp,   
		      int argc, char *argv[]) {

  Sheet *sheet;
  Tcl_HashEntry *entry;
  Cell *cell;
  Tcl_HashSearch pt;
  char *err;
  
  if((err=CheckArgs(argc,argv,F_GET_PRINT_RANGE))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }

  sheet = SheetFromName(argv[1]);

  int x1=-1,y1=-1,x2=-1,y2=-1;
  int tmpx,tmpy;

  sheet->max_col_used = -1;
  sheet->max_row_used = -1;
  sheet->min_col_used = 100000;
  sheet->min_row_used = 100000;
    
    
  for( entry = Tcl_FirstHashEntry(&(sheet->cells),&pt); entry ; 
       entry = Tcl_NextHashEntry(&pt)) {
    cell = (Cell*)Tcl_GetHashValue(entry);
    if (cell->type != CODE_BLANK){
      if (cell->col > sheet->max_col_used) 
	sheet->max_col_used = cell->col;
      if (cell->row > sheet->max_row_used) 
	sheet->max_row_used = cell->row;
      if (cell->col < sheet->min_col_used) 
	sheet->min_col_used = cell->col;
      if (cell->row < sheet->min_row_used) 
	sheet->min_row_used = cell->row;
    }
  }

  for (int i=0; i<sheet->graphs_cnt; i++) {
    if (sheet->graphs[i]) {
      Graph *grp = sheet->graphs[i];
      if((grp->graphCol < x1)||(x1<0)){x1=grp->graphCol;}
      if((grp->graphRow < y1)||(y1<0)){y1=grp->graphRow;}
	
      tmpx=((sheet->canvas_info->cmdPtr == NULL) ?
	    (cell_col(grp->graphCol*DEFCELLWIDTH+grp->graphX+grp->graphWidth,sheet->canvas_info)):
	    (cell_col(sheet->canvas_info->col_origin[grp->graphCol]
		      +grp->graphX+grp->graphWidth,sheet->canvas_info)));
	
      tmpy=((sheet->canvas_info->cmdPtr == NULL) ?
	    (cell_row(grp->graphRow*DEFCELLHEIGHT+grp->graphY+grp->graphHeight,sheet->canvas_info)):
	    (cell_row(sheet->canvas_info->col_origin[grp->graphRow]
		      +grp->graphY+grp->graphHeight,sheet->canvas_info)));
	
      if(tmpx>x2){x2=tmpx;};
      if(tmpy>y2){y2=tmpy;};
	
    }
  }
  if ((x1!=-1)&&(x2!=-1)&&(y1!=-1)&&(y2!=-1))
    {
      sheet->min_col_used = ((sheet->min_col_used <= x1)?sheet->min_col_used : x1);
      sheet->min_row_used = ((sheet->min_row_used <= y1)?sheet->min_row_used : y1);
      sheet->max_col_used = ((sheet->max_col_used >= x2)?sheet->max_col_used : x2);
      sheet->max_row_used = ((sheet->max_row_used >= y2)?sheet->max_row_used : y2);
    }
    
  if (sheet->max_col_used != -1) 
    sprintf(interp->result,"%d %d %d %d %d %d %d %d %d",
	    sheet->psettings.print_selection,
	    sheet->min_col_used,
	    sheet->min_row_used,sheet->max_col_used,
	    sheet->max_row_used,
	    sheet->print_range.start.col,
	    sheet->print_range.start.row,
	    sheet->print_range.end.col,
	    sheet->print_range.end.row);
  else 
    sprintf(interp->result,"%d %d %d %d %d %d %d %d %d",
	    sheet->psettings.print_selection,
	    0,0,0,0,
	    sheet->print_range.start.col,
	    sheet->print_range.start.row,
	    sheet->print_range.end.col,
	    sheet->print_range.end.row);
    
        
        
  return(TCL_OK);
}

int Xxl_SetGraphParams(ClientData clientData, Tcl_Interp *interp,   
                       int argc, char *argv[]) {

  Graph *gr = new Graph;
  SetGraphEvent *evt;
  char *err;
  Sheet *sheet;

  if((err=CheckArgs(argc,argv,F_SET_GRAPH_PARAMS))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  
  sheet = SheetFromName(argv[1]);
  
  gr->used = TRUE;
  gr->graphType = atoi(argv[2]);
  
  gr->range.start.col = atoi(argv[3]);
  gr->range.start.row = atoi(argv[4]);
  gr->range.end.col = atoi(argv[5]);
  gr->range.end.row = atoi(argv[6]);
  gr->graphWidth = atoi(argv[7]);
  gr->graphHeight = atoi(argv[8]);
  gr->graphX = atoi(argv[9]);
  gr->graphY = atoi(argv[10]);
  gr->graphCol = atoi(argv[11]);
  gr->graphRow = atoi(argv[12]);

  if (gr->range.end.col  > sheet->max_col_used) 
    gr->range.end.col = sheet->max_col_used ;
  if (gr->range.end.row  > sheet->max_row_used) 
    gr->range.end.row = sheet->max_row_used ;
  if (gr->range.start.col < sheet->min_col_used) 
    gr->range.start.col = sheet->min_col_used ;
  if (gr->range.start.row < sheet->min_row_used) 
    gr->range.start.row = sheet->min_row_used ;
  
  int i;
  
  for(i=0; ;i++) {
    if (!sheet->graphs[i]){
      gr->number = i;
      sheet->graphs[i] = gr;

      sheet->graphs_cnt = MAX(sheet->graphs_cnt, i+1);
      break;
    }
  }
  
  evt=new SetGraphEvent(sheet,*gr);
  sheet->RegisterEvent(evt);   
  
  sheet->add_graph_deps(gr);

  sprintf(interp->result,"%d",i);
  return(TCL_OK);
}


int Xxl_ChangeGraphParams(ClientData clientData, Tcl_Interp *interp,   
			  int argc, char *argv[]) 
{
  Graph *gr;
  ChangeGraphEvent *evt;
  char *err;
  Sheet *sheet;

  if((err=CheckArgs(argc,argv,F_CHANGE_GRAPH_PARAMS))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  
  sheet = SheetFromName(argv[1]);
  int i = atoi(argv[2]);
  if(sheet->graphs[i]!=NULL)
    {
      gr=sheet->graphs[i];
      evt=new ChangeGraphEvent(sheet,*gr);
      sheet->RegisterEvent(evt);   
      
      gr->graphWidth = atoi(argv[3]);
      gr->graphHeight = atoi(argv[4]);
      gr->graphX = atoi(argv[5]);
      gr->graphY = atoi(argv[6]);
      gr->graphCol = atoi(argv[7]);
      gr->graphRow = atoi(argv[8]);
    } 
  else 
    {
      sprintf(interp->result,"Inexistent graph");
      return(TCL_ERROR);
    }
  return(TCL_OK);
}


int Xxl_GetGraphParams(ClientData clientData, Tcl_Interp *interp,   
                       int argc, char *argv[]) 
{
  char *err;
  Sheet *sheet;

  if((err=CheckArgs(argc,argv,F_GET_GRAPH_PARAMS))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }

  sheet = SheetFromName(argv[1]);
  int i = atoi(argv[2]);

  if (!sheet->graphs[i]) {
    sprintf(interp->result,"Inexistent graph");
    return(TCL_ERROR);
  }

  Graph *grp = sheet->graphs[i];
  sprintf(interp->result,"%d %d %d %d %d %d %d %d %d %d %d\n",
          grp->graphType,
          grp->range.start.col,
          grp->range.start.row,
          grp->range.end.col,
          grp->range.end.row,
          grp->graphWidth,
          grp->graphHeight,
          grp->graphX,
          grp->graphY,
          grp->graphCol,
          grp->graphRow);

  return(TCL_OK);
}

int Xxl_UsedGraphs(ClientData clientData, Tcl_Interp *interp,   
		   int argc, char *argv[]) 
{
  int i;
  int l;
  char *err;
  Sheet *sheet;

  if((err=CheckArgs(argc,argv,F_USED_GRAPHS))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }

  sheet = SheetFromName(argv[1]);

  l = 0;
  for (i=0; i<sheet->graphs_cnt; i++) {
    if (sheet->graphs[i]) {
      sprintf(interp->result+l," %d",i);
      l = strlen(interp->result);
    }
  }
  return(TCL_OK);
}

int Xxl_FindGraph(ClientData clientData, Tcl_Interp *interp,   
		  int argc, char *argv[]) 
{
  static int graph=-1;
  char *err;
  Sheet *sheet;

  if((err=CheckArgs(argc,argv,F_FIND_GRAPH))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  
  sheet = SheetFromName(argv[1]);

  if (sheet == NULL) 
    return(TCL_OK);

  int i=((graph<=0)? sheet->graphs_cnt-1:graph-1);
  int startx,endx,starty,endy; 
  
  int x = atoi(argv[2]);
  int y = atoi(argv[3]);
  
  for(int count=1;
      (count<=sheet->graphs_cnt)&&(i>=0); 
      count++,i=((i<=0)? sheet->graphs_cnt-1 : i-1))
    {
      if (sheet->graphs[i]) {
	
	Graph *grp = sheet->graphs[i];
	
	startx  = grp->graphCol; 
	starty  = grp->graphRow; 
	
	if (sheet->canvas_info->cmdPtr == NULL) 
	  {
	    startx = startx*DEFCELLWIDTH+grp->graphX;
	    endx = startx+grp->graphWidth;
	    starty = starty*DEFCELLHEIGHT+grp->graphY;
	    endy = starty+grp->graphHeight;
	  }
	else
	  {
	    startx = sheet->canvas_info->col_origin[startx]+grp->graphX;
	    endx = startx+grp->graphWidth;
	    starty = sheet->canvas_info->row_origin[starty]+grp->graphY;
	    endy = starty+grp->graphHeight;
	  }

	if ((startx <= x) && (endx >= x) && (starty <= y) && (endy >= y))
	  {
	    sprintf(interp->result,"%d",i);
	    graph=i;
	    return(TCL_OK);
	  }
      }
    }
  sprintf(interp->result,"-1");
  return(TCL_OK);
}


int Xxl_DeleteGraph(ClientData clientData, Tcl_Interp *interp,   
		    int argc, char *argv[]) 
{
  char *err;
  Sheet *sheet;

  if((err=CheckArgs(argc,argv,F_DELETE_GRAPH))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }

  sheet = SheetFromName(argv[1]);
  int i = atoi(argv[2]);
  
  if(sheet->graphs[i])
    {
      sheet->remove_graph_deps(sheet->graphs[i]);
      if (argc==4)
	{
	  DelGraphEvent *evt;
	  evt=new DelGraphEvent(sheet,*(sheet->graphs[i]));
	  sheet->RegisterEvent(evt);
	}

      delete sheet->graphs[i];
      sheet->graphs[i] = NULL;

      return(TCL_OK); 
    }

  sprintf(interp->result,"Inexistent graph");
  return(TCL_ERROR);
}

int Xxl_SetPrintParams(ClientData clientData, Tcl_Interp *interp,   
                       int argc, char *argv[]) 
{
  char *err;
  Sheet *sheet;

  if((err=CheckArgs(argc,argv,F_SET_PRINT_PARAMS))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }

  sheet = SheetFromName(argv[1]);
  
  sheet->psettings.leftMargin = atof(argv[2]);
  sheet->psettings.rightMargin = atof(argv[3]);
  sheet->psettings.topMargin = atof(argv[4]);
  sheet->psettings.bottomMargin = atof(argv[5]);
  sheet->psettings.scale = atof(argv[6]);
  
  switch(argv[7][0]) {
  case 'm':
    sheet->psettings.colorMode = 0;
    break;
  case 'g':
    sheet->psettings.colorMode = 1;
    break;
  case 'c':
    sheet->psettings.colorMode = 2;
    break;
  }

  sheet->psettings.typeView = atoi(argv[8]);

  sheet->psettings.fromPage = atoi(argv[9]);
  if (argv[10][0] != 0) 
    sheet->psettings.toPage = atoi(argv[10]);
  else 
    sheet->psettings.toPage = 0;

  if (argv[11][0] == 'A') 
    sheet->psettings.pageType = 0;
  else if (argv[11][0] == 'U') 
    sheet->psettings.pageType = 1;

  sheet->psettings.set = 1;
  sheet->modified_after_save = TRUE;

  return(TCL_OK);


}

int Xxl_GetPrintParams(ClientData clientData, Tcl_Interp *interp,   
                       int argc, char *argv[]) 
{
  Sheet *sheet;
  char *err;

  if((err=CheckArgs(argc,argv,F_GET_PRINT_PARAMS))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet = SheetFromName(argv[1]);

  char *colormode,*pagetype;

  if (sheet->psettings.set == 0) {
    sprintf(interp->result,"");
    return(TCL_OK);
  }


  switch(sheet->psettings.colorMode) {
  case 0:
    colormode = "mono";
    break;
  case 1:
    colormode = "gray";
    break;
  case 2:
    colormode = "color";
    break;
  }

  switch(sheet->psettings.pageType) {
  case 0:
    pagetype = "A4";
    break;
  case 1:
    pagetype = "USLetter";
    break;
  }


  sprintf(interp->result,"%g %g %g %g %g %s %d %d %d %s",
          sheet->psettings.leftMargin,sheet->psettings.rightMargin,
          sheet->psettings.topMargin,sheet->psettings.bottomMargin,
          sheet->psettings.scale,colormode,
          sheet->psettings.typeView,
          sheet->psettings.fromPage,
          sheet->psettings.toPage,
          pagetype);
          

  return(TCL_OK);

}


int Xxl_SheetEmpty(ClientData clientData, Tcl_Interp *interp,   
		   int argc, char *argv[]) {
  Sheet *sheet;
  char *err;

  if((err=CheckArgs(argc,argv,F_SHEET_EMPTY))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet = SheetFromName(argv[1]);
  if (sheet == NULL)
    return(TCL_ERROR);
  if (sheet->empty)
    strcpy(interp->result,"1");
  else
    strcpy(interp->result,"0");
  return(TCL_OK);
}

int Xxl_SheetModified(ClientData clientData, Tcl_Interp *interp,   
		      int argc, char *argv[]) {
  Sheet *sheet;
  char *err;

  if((err=CheckArgs(argc,argv,F_SHEET_MODIFIED))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet = SheetFromName(argv[1]);
  if (sheet == NULL)
    return(TCL_ERROR);

  if (sheet->modified_after_save)
    strcpy(interp->result,"1");
  else
    strcpy(interp->result,"0");

  return(TCL_OK);
}

int Xxl_RedrawSheet(ClientData clientData, Tcl_Interp *interp,   
		    int argc, char *argv[]) {
  Sheet *sheet;
  char *err;

  if((err=CheckArgs(argc,argv,F_REDRAW_SHEET))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet = SheetFromName(argv[1]);
  if (sheet == NULL)
    return(TCL_ERROR);
  sheet->redraw();
  return(TCL_OK);
}

int Xxl_DeleteSheet(ClientData clientData, Tcl_Interp *interp,   
		    int argc, char *argv[]) {
  Sheet *sheet;
  static char buf[256];
  int code;
  char *err;

  if((err=CheckArgs(argc,argv,F_DELETE_SHEET))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet = SheetFromName(argv[1]);
  if (sheet == NULL)
    return(TCL_ERROR);

  if (sheet->modified_after_save) {
    sprintf(buf,"queryUser {Sheet %s was modified.\nDo you want to close anyway ? } {Yes Cancel}", (char*)sheet->name);
    code = Tcl_Eval(interp,buf);
        
    if (code == TCL_OK) {
      return(TCL_OK);
    }
    else 
      internal_error();
    

    return(TCL_ERROR);
  }
  delete sheet;
  return(TCL_OK);
}


int Xxl_LoadSheet(ClientData clientData, Tcl_Interp *interp,   
		  int argc, char *argv[]) {
  Sheet *sheet;

  if (argc != 3) {
    return(TCL_ERROR);
  }

  sheet = SheetFromName(argv[1]);

  sheet->load(argv[2]);

  return(TCL_OK);


}

int Xxl_SaveSheet(ClientData clientData, Tcl_Interp *interp,
		  int argc, char *argv[]) 
{
  Sheet *sheet;
  char *err;
  int query;

  if((err=CheckArgs(argc,argv,F_SAVE_SHEET))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet = SheetFromName(argv[1]);
  if(argc==4)
    {
      if(sheet->save(argv[2],0)==XXL_BAD_FILE)
	return(TCL_ERROR);
    }
  else
    {
      if(sheet->save(argv[2])==XXL_BAD_FILE)
	return(TCL_ERROR);
    }
  return(TCL_OK);
}

int Xxl_RecalculateBeforeSaving(ClientData clientData, Tcl_Interp *interp,   
				int argc, char *argv[]) 
{
  Sheet *sheet;
  char *err;

  if((err=CheckArgs(argc,argv,F_RECALCULATE_BEFORE_SAVING))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet = SheetFromName(argv[1]);
  int mode = atoi(argv[2]);
  sheet->recalculate_before_saving = mode;
  return(TCL_OK);
}


int Xxl_SheetRecalc(ClientData clientData, Tcl_Interp *interp,   
		    int argc, char *argv[]) 
{
  int prev_mode;
  Tcl_HashEntry *entry;
  Tcl_HashSearch pt;
  Cell *cell2;
  char *err;

  if((err=CheckArgs(argc,argv,F_SHEET_RECALC))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  Sheet *sheet = SheetFromName(argv[1]);
  /* Now, for the dirty cells */
  for( entry = Tcl_FirstHashEntry(&sheet->dirty_cells,&pt); entry ;
       entry = Tcl_NextHashEntry(&pt)) 
    {
      cell2 = (Cell*)Tcl_GetHashValue(entry);
      sheet->depth_first_search(cell2);
    }
  prev_mode = sheet->automatic_recalc;
  sheet->automatic_recalc = 255;
  sheet->recalculate_list_of_cells();
  sheet->automatic_recalc = prev_mode;
  return(TCL_OK);
}


int Xxl_SetAutomaticRecalc(ClientData clientData, Tcl_Interp *interp,   
			   int argc, char *argv[]) 
{
  Sheet *sheet;
  int mode;
  char *err;

  if((err=CheckArgs(argc,argv,F_SET_AUTOMATIC_RECALC))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  mode=atoi(argv[2]);
  sheet = SheetFromName(argv[1]);
  sheet->automatic_recalc = mode;
  sheet->recalculate_list_of_cells();
  return(TCL_OK);
}

int Xxl_GetAutomaticRecalc(ClientData clientData, Tcl_Interp *interp,   
			   int argc, char *argv[]) 
{
  Sheet *sheet;
  char *err;

  if((err=CheckArgs(argc,argv,F_GET_AUTOMATIC_RECALC))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet = SheetFromName(argv[1]);
  sprintf(interp->result,"%d",(int)(sheet->automatic_recalc));
  return(TCL_OK);
}

int Xxl_SetFill(ClientData clientData, Tcl_Interp *interp,   
                int argc, char *argv[]) 
{
  int row,col,end;
  Cell *cell;
  Sheet *sheet;
  Ref rf;
  int filllevel;
  SetFormatEvent *evt;
  Tcl_HashEntry *entry;
  Format *fmt;
  int newv;
  char *err;

  prev_format_result[0] = 0;

  if((err=CheckArgs(argc,argv,F_SET_FILL))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  
  sheet = SheetFromName(argv[1]);
  
  sheet->modified_after_save = TRUE;
  filllevel = atoi(argv[2]);
    
  if (argc == 5) // cell
    {
      col = atoi(argv[3]);
      row = atoi(argv[4]);
    
      evt=new SetFormatEvent(sheet,col,row);
      sheet->RegisterEvent(evt);   

      cell = sheet->cellFind(col,row);
      cell->format.bg.filllevel = filllevel;
      sheet->display(cell);
    }
  else if (argc == 7) 
    {
      Range rg;
      rg.start.col = atoi(argv[3]);
      rg.start.row = atoi(argv[4]);
      rg.end.col = atoi(argv[5]);
      rg.end.row = atoi(argv[6]);
      
      Range_iter rang(rg);
      evt=new SetFormatEvent(sheet,rg.start.col,rg.start.row,
			     rg.end.col,rg.end.row);
      sheet->RegisterEvent(evt);
      
      if(rg.start.col==-1) // rows
	{
	  for(row=min(rg.start.row,rg.end.row),
		end=max(rg.start.row,rg.end.row);row<=end;row++)
	    {
	      entry=Tcl_CreateHashEntry(&sheet->row_formats,(char*) row,&newv);
	      if(newv==1)
		{
		  fmt=new Format();
		  fmt->bg.filllevel = filllevel;
		  Tcl_SetHashValue(entry,fmt);
		}
	      else
		{
		  fmt=(Format *) Tcl_GetHashValue(entry);
		  fmt->bg.filllevel = filllevel;
		  fmt->UpdateTimestamp();
		}
	      ApplyCRFormat(sheet,1,row,BG,fmt);
	    }
	}
      else if(rg.start.row==-1) // cols
	{
	  for(col=min(rg.start.col,rg.end.col),
		end=max(rg.start.col,rg.end.col);col<=end;col++)
	    {
	      entry=Tcl_CreateHashEntry(&sheet->col_formats,(char*) col,&newv);
	      if(newv==1)
		{
		  fmt=new Format();
		  fmt->bg.filllevel = filllevel;
		  Tcl_SetHashValue(entry,fmt);
		}
	      else
		{
		  fmt=(Format *) Tcl_GetHashValue(entry);
		  fmt->bg.filllevel = filllevel;
		  fmt->UpdateTimestamp();
		}
	      ApplyCRFormat(sheet,0,col,BG,fmt);
	    }
	}
      else // range
	{
	  for(rang.first(rf); !rang.last();  rang.next(rf))
	    {
	      cell = sheet->cellFind(rf);
	      cell->format.bg.filllevel = filllevel;
	      sheet->display(cell);
	    }
	} 
    }
  else 
    return(TCL_ERROR);
    
  return(TCL_OK);
}

int Xxl_SetFormat(ClientData clientData, Tcl_Interp *interp,   
		  int argc, char *argv[]) 
{
  int row,col,end;
  Cell *cell;
  Sheet *sheet;
  Ref rf;
  int fm;
  SetFormatEvent *evt;
  Tcl_HashEntry *entry;
  Format *fmt;
  int newv;
  char *err;

  prev_format_result[0] = 0;

  if((err=CheckArgs(argc,argv,F_SET_FORMAT))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  
  sheet = SheetFromName(argv[1]);
  sheet->modified_after_save = TRUE;
  fm = atoi(argv[2]);
  
  if (argc == 5) // cell
    {
      col = atoi(argv[3]);
      row = atoi(argv[4]);
      
      evt=new SetFormatEvent(sheet,col,row);
      sheet->RegisterEvent(evt);   
      
      cell = sheet->cellFind(col,row);
      cell->format.form = fm;
      sheet->display(cell);
    }
  else if (argc == 7) 
    {
      Range rg;
      rg.start.col = atoi(argv[3]);
      rg.start.row = atoi(argv[4]);
      rg.end.col = atoi(argv[5]);
      rg.end.row = atoi(argv[6]);
      
      Range_iter rang(rg);
      
      evt=new SetFormatEvent(sheet,rg.start.col,rg.start.row,
			     rg.end.col,rg.end.row);
      sheet->RegisterEvent(evt);
      
      if(rg.start.col==-1) // rows
	{
	  for(row=min(rg.start.row,rg.end.row),
		end=max(rg.start.row,rg.end.row);row<=end;row++)
	    {
	      entry=Tcl_CreateHashEntry(&sheet->row_formats,(char*) row,&newv);
	      if(newv==1)
		{
		  fmt=new Format();
		  fmt->form=fm;
		  Tcl_SetHashValue(entry,fmt);
		}
	      else
		{
		  fmt=(Format *) Tcl_GetHashValue(entry);
		  fmt->form=fm;
		  fmt->UpdateTimestamp();
		}
	      ApplyCRFormat(sheet,1,row,FORMAT,fmt);
	    }
	}
      else if(rg.start.row==-1) // cols
	{
	  for(col=min(rg.start.col,rg.end.col),
		end=max(rg.start.col,rg.end.col);col<=end;col++)
	    {
	      entry=Tcl_CreateHashEntry(&sheet->col_formats,(char*) col,&newv);
	      if(newv==1)
		{
		  fmt=new Format();
		  fmt->form=fm;
		  Tcl_SetHashValue(entry,fmt);
		}
	      else
		{
		  fmt=(Format *) Tcl_GetHashValue(entry);
		  fmt->form=fm;
		  fmt->UpdateTimestamp();
		}
	      ApplyCRFormat(sheet,0,col,FORMAT,fmt);
	    }
	}
      else // range
	{
	  for(rang.first(rf); !rang.last();  rang.next(rf))
	    {
	      cell = sheet->cellFind(rf);
	      cell->format.form = fm;
	      sheet->display(cell);
	    } 
	}
    }
  else 
    return(TCL_ERROR);

  return(TCL_OK);
}

int Xxl_SetAlignment(ClientData clientData, Tcl_Interp *interp,   
		     int argc, char *argv[]) {
  int row,col,end;
  Cell *cell;
  Sheet *sheet;
  Ref rf;
  int alig;
  SetFormatEvent *evt;
  Tcl_HashEntry *entry;
  Format *fmt;
  int newv;
  char *err;

  prev_format_result[0] = 0;

  if((err=CheckArgs(argc,argv,F_SET_ALIGNMENT))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  
  sheet = SheetFromName(argv[1]);

  sheet->modified_after_save = TRUE;
  alig = atoi(argv[2]);
  
  if (argc == 5) // cell
    {
      col = atoi(argv[3]);
      row = atoi(argv[4]);

      evt=new SetFormatEvent(sheet,col,row);
      sheet->RegisterEvent(evt);   

      cell=sheet->cellFind(col,row);
      cell->format.alignment = alig;
      sheet->display(cell);
    }
  else if (argc == 7) // cols, rows or range
    {
      Range rg;
      rg.start.col = atoi(argv[3]);
      rg.start.row = atoi(argv[4]);
      rg.end.col = atoi(argv[5]);
      rg.end.row = atoi(argv[6]);
      
      Range_iter rang(rg);
      
      evt=new SetFormatEvent(sheet,rg.start.col,rg.start.row,
			     rg.end.col,rg.end.row);
      sheet->RegisterEvent(evt);
      
      if(rg.start.col==-1) // rows
	{
	  for(row=min(rg.start.row,rg.end.row),
		end=max(rg.start.row,rg.end.row);row<=end;row++)
	    {
	      entry=Tcl_CreateHashEntry(&sheet->row_formats,(char*) row,&newv);
	      if(newv==1)
		{
		  fmt=new Format();
		  fmt->alignment=alig;
		  Tcl_SetHashValue(entry,fmt);
		}
	      else
		{
		  fmt=(Format *) Tcl_GetHashValue(entry);
		  fmt->alignment=alig;
		  fmt->UpdateTimestamp();
		}
	      ApplyCRFormat(sheet,ROW,row,ALIGNMENT,fmt);
	    }
	}
      else if(rg.start.row==-1) // cols
	{
	  for(col=min(rg.start.col,rg.end.col),
		end=max(rg.start.col,rg.end.col);col<=end;col++)
	    {
	      entry=Tcl_CreateHashEntry(&sheet->col_formats,(char*) col,&newv);
	      if(newv==1)
		{
		  fmt=new Format();
		  fmt->alignment=alig;
		  Tcl_SetHashValue(entry,fmt);
		}
	      else
		{
		  fmt=(Format *) Tcl_GetHashValue(entry);
		  fmt->alignment=alig;
		  fmt->UpdateTimestamp();
		}
	      ApplyCRFormat(sheet,COLUMN,col,ALIGNMENT,fmt);
	    }
	}
      else // range
	{
	  for(rang.first(rf); !rang.last();  rang.next(rf))
	    {
	      cell = sheet->cellFind(rf);
	      cell->format.alignment = alig;
	      sheet->display(cell);
	    }
	}
    } 
  else 
    return(TCL_ERROR);
  return(TCL_OK);
}

int Xxl_CellBorders(ClientData clientData, Tcl_Interp *interp,   
		    int argc, char *argv[]) {
  Sheet *sheet;
  char *err;

  if((err=CheckArgs(argc,argv,F_CELL_BORDERS))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet = SheetFromName(argv[1]);
  sprintf(interp->result,"%d",sheet->display_cell_borders);
  return(TCL_OK);  
}

int Xxl_PageBorders(ClientData clientData, Tcl_Interp *interp,   
		    int argc, char *argv[]) {
  Sheet *sheet;
  char *err;

  if((err=CheckArgs(argc,argv,F_PAGE_BORDERS))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet = SheetFromName(argv[1]);
  sprintf(interp->result,"%d",sheet->page_borders);
  return(TCL_OK);  
}

int Xxl_ToggleDisplayBorders(ClientData clientData, Tcl_Interp *interp,   
			     int argc, char *argv[]) 
{
  Sheet *sheet;
  char *err;

  if((err=CheckArgs(argc,argv,F_TOGGLE_DISPLAY_BORDERS))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet = SheetFromName(argv[1]);
  sheet->display_cell_borders = 1-sheet->display_cell_borders;
  return(TCL_OK);
}

int Xxl_TogglePageBorders(ClientData clientData, Tcl_Interp *interp,   
			  int argc, char *argv[]) {
  Sheet *sheet;
  char *err;

  if((err=CheckArgs(argc,argv,F_TOGGLE_PAGE_BORDERS))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet = SheetFromName(argv[1]);
  sheet->page_borders = 1-sheet->page_borders;
  return(TCL_OK);

}

int Xxl_SetBorders(ClientData clientData, Tcl_Interp *interp,   
		   int argc, char *argv[]) 
{
  int row,col,end;
  Cell *cell;
  Sheet *sheet;
  Ref rf;
  int top,bottom,left,right;
  SetFormatEvent *evt;
  Tcl_HashEntry *entry;
  Format *fmt;
  int newv;
  char *err;

  prev_format_result[0] = 0;
  
  if((err=CheckArgs(argc,argv,F_SET_BORDERS))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  
  sheet = SheetFromName(argv[1]);
  sheet->modified_after_save = TRUE;

  top = atoi(argv[2]);
  bottom = atoi(argv[3]);
  left = atoi(argv[4]);
  right = atoi(argv[5]);

  CR_Formats tmp=TOP;
  Border bd;
  
  if (top != DEFAULT_BORDER)
    {
      bd.top = top;
      tmp=TOP;
    }
  if (bottom != DEFAULT_BORDER)
    {
      bd.bottom = bottom; 
      tmp=BOTTOM;
    }
  if (left != DEFAULT_BORDER)
    {
      bd.left = left;
      tmp=LEFT;
    }
  if (right != DEFAULT_BORDER)
    {
      bd.right = right;
      tmp=RIGHT;
    }
  if (argc == 8) 
    {
      col = atoi(argv[6]);
      row = atoi(argv[7]);

      evt=new SetFormatEvent(sheet,col,row);
      sheet->RegisterEvent(evt);   
      
      cell=sheet->cellFind(col,row);
      cell->format.borders = bd;
      sheet->display(cell);
    } 
  else if (argc == 10) 
    {
      Range rg;
      
      rg.start.col = atoi(argv[6]);
      rg.start.row = atoi(argv[7]);
      rg.end.col = atoi(argv[8]);
      rg.end.row = atoi(argv[9]);
      
      Range_iter rang(rg);

      evt=new SetFormatEvent(sheet,rg.start.col,rg.start.row,
			     rg.end.col,rg.end.row);
      sheet->RegisterEvent(evt);

      if(rg.start.col==-1)
	{
	  for(row=min(rg.start.row,rg.end.row),
		end=max(rg.start.row,rg.end.row);row<=end;row++)
	    {
	      entry=Tcl_CreateHashEntry(&sheet->row_formats,(char*) row,&newv);
	      if(newv==1)
		{
		  fmt=new Format();
		  fmt->borders=bd;
		  Tcl_SetHashValue(entry,fmt);
		}
	      else
		{
		  fmt=(Format *) Tcl_GetHashValue(entry);
		  fmt->borders=bd;
		  fmt->UpdateTimestamp();
		}
	      ApplyCRFormat(sheet,1,row,tmp,fmt);
	    }
	}
      else if(row==-1)
	{
	  for(col=min(rg.start.col,rg.end.col),
		end=max(rg.start.col,rg.end.col);col<=end;col++)
	    {
	      entry=Tcl_CreateHashEntry(&sheet->col_formats,(char*) col,&newv);
	      if(newv==1)
		{
		  fmt=new Format();
		  fmt->borders=bd;
		  Tcl_SetHashValue(entry,fmt);
		}
	      else
		{
		  fmt=(Format *) Tcl_GetHashValue(entry);
		  fmt->borders=bd;
		  fmt->UpdateTimestamp();
		}
	      ApplyCRFormat(sheet,0,col,tmp,fmt);
	    }
	}
      else
	{
	  for(rang.first(rf); !rang.last();  rang.next(rf))
	    {
	      cell = sheet->cellFind(rf);
	      if (top != DEFAULT_BORDER)
		cell->format.borders.top = top;
	      if (bottom != DEFAULT_BORDER)
		cell->format.borders.bottom = bottom; 
	      if (left != DEFAULT_BORDER)
		cell->format.borders.left = left;
	      if (right != DEFAULT_BORDER)
		cell->format.borders.right = right;
	      sheet->display(cell);
	    }
	}
    }
  else 
    return(TCL_ERROR);
    
  return(TCL_OK);
    
}

int Xxl_GetFormat(ClientData clientData, Tcl_Interp *interp,
		  int argc, char *argv[]) 
{
  Cell *cell,*aux=NULL;
  Sheet *sheet;
  Format *f;
  short col,row;
  char cr_or_cell;
  static int cnt=0;
  Tcl_HashEntry *entry;
  char *err;

  if((err=CheckArgs(argc,argv,F_GET_FORMAT))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  
  if (cnt == 0) 
    {
      prev_format_result[0] = 0;
      cnt = 1;
    }
  
  sheet = SheetFromName(argv[1]);
  col = atoi(argv[2]);
  row = atoi(argv[3]);

  if((cell=sheet->cellExists(col,row))!=NULL)
    {
      aux=new Cell;
      *aux=*cell;
    }
  if((entry=Tcl_FindHashEntry(&(sheet->col_formats),(char *) col))!=NULL)
    {
      if(aux==NULL)
	aux=new Cell;
      aux->mergeformats((Format*) Tcl_GetHashValue(entry));
    }
  if((entry=Tcl_FindHashEntry(&(sheet->row_formats),(char *) row))!=NULL)
    {
      if(aux==NULL)
	aux=new Cell;
      aux->mergeformats((Format*) Tcl_GetHashValue(entry));
    }

  if(aux!=NULL) 
    {
      f = &(aux->format);
      sprintf(interp->result,"%d %d %d %d %d %d %d %d %d %d %d",
	      f->font.family,f->font.size,f->font.bold,f->font.italics,
	      f->borders.top,f->borders.bottom,
	      f->borders.left,f->borders.right,
	      f->bg.filllevel,f->alignment,f->form);
      delete aux;
    }
  else
    sprintf(interp->result,"%d %d %d %d %d %d %d %d %d %d %d",
	    DEFAULT_FONT_FAMILY,DEFAULT_FONT_SIZE,DEFAULT_FONT_BOLD,
	    DEFAULT_FONT_ITALICS,
	    DEFAULT_BORDER,DEFAULT_BORDER,DEFAULT_BORDER,DEFAULT_BORDER,
	    DEFAULT_FILL,DEFAULT_ALIGNMENT,DEFAULT_FORMAT);
  
  if (!strcmp(prev_format_result,interp->result))
    interp->result[0] = 0;
  else
    strcpy(prev_format_result,interp->result);


  return(TCL_OK);
}

int Xxl_SetFont(ClientData clientData, Tcl_Interp *interp,
		int argc, char *argv[]) 
{
  Fontinfo font;
  char *st;
  int row,col,end;
  CR_Formats modtype;
  Cell *cell,aux;
  Sheet *sheet;
  Ref rf;
  SetFormatEvent *evt;
  Tcl_HashEntry *entry;
  Format *fmt;
  int newv;
  char *err;

  prev_format_result[0] = 0;

  if((err=CheckArgs(argc,argv,F_SET_FONT))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }

  sheet = SheetFromName(argv[1]);
  sheet->modified_after_save = TRUE;
  
  font.family = atoi(argv[2]);
  font.size = atoi(argv[3]);
  font.italics = atoi(argv[4]);
  font.bold = atoi(argv[5]);
    
  if (argc==9)
    {
      col = atoi(argv[6]);
      row = atoi(argv[7]);
      
      if((atoi(argv[8]))==1)
	{
	  evt=new SetFormatEvent(sheet,col,row);
	  sheet->RegisterEvent(evt);   
	}
      
      cell = sheet->cellFind(col,row);
      cell->format.font = font;
      sheet->display(cell);
    }
  else if (argc==12)
    {
      Range rg;
      rg.start.col = atoi(argv[6]);
      rg.start.row = atoi(argv[7]);
      rg.end.col = atoi(argv[8]);
      rg.end.row = atoi(argv[9]);
      modtype=(CR_Formats) atoi(argv[11]);
      
      Range_iter rang(rg);
      
      if(atoi((argv[10]))==1)
	{
	  evt=new SetFormatEvent(sheet,rg.start.col,rg.start.row,
				 rg.end.col,rg.end.row);
	  sheet->RegisterEvent(evt);
	}
      
      if(rg.start.col==-1) // rows
	{
	  for(row=min(rg.start.row,rg.end.row),
		end=max(rg.start.row,rg.end.row);row<=end;row++)
	    {
	      entry=Tcl_CreateHashEntry(&sheet->row_formats,(char*) row,&newv);
	      if(newv==1)
		{
		  fmt=new Format();
		  fmt->font=font;
		  Tcl_SetHashValue(entry,fmt);
		}
	      else
		{
		  fmt=(Format *) Tcl_GetHashValue(entry);
		  fmt->font=font;
		  fmt->UpdateTimestamp();
		} 
	      ApplyCRFormat(sheet,1,row,modtype,fmt);
	    }
	}
      else if(rg.start.row==-1) // cols
	{
	  for(col=min(rg.start.col,rg.end.col),
		end=max(rg.start.col,rg.end.col);col<=end;col++)
	    {
	      entry=Tcl_CreateHashEntry(&sheet->col_formats,(char*) col,&newv);
	      if(newv==1)
		{
		  fmt=new Format();
		  fmt->font=font;
		  Tcl_SetHashValue(entry,fmt);
		}
	      else
		{
		  fmt=(Format *) Tcl_GetHashValue(entry);
		  fmt->font=font;
		  fmt->UpdateTimestamp();
		}
	      ApplyCRFormat(sheet,0,col,modtype,fmt);
	    }
	}
      else // range
	{
	  for(rang.first(rf); !rang.last();  rang.next(rf))
	    {
	      cell = sheet->cellFind(rf);
	      cell->format.font = font;
	      
	      if(font.family!=DEFAULT_FONT_FAMILY)
		cell->format.font.family=font.family;
	      if(font.size!=DEFAULT_FONT_SIZE)
		cell->format.font.size=font.size;
	      if(font.italics!=DEFAULT_FONT_ITALICS)
		cell->format.font.italics=font.italics;
	      if(font.bold!=DEFAULT_FONT_BOLD)
		cell->format.font.bold=font.bold;
	      sheet->display(cell);
	    }
	} 
    }
  else 
    return(TCL_ERROR);

  return(TCL_OK);
}

int Xxl_CellSet(ClientData clientData, Tcl_Interp *interp,   
                int argc, char *argv[]) {
  short row,col;
  Sheet *sheet;
  Cell *cell;
  CellSetEvent *evt;
  char *err;

  if((err=CheckArgs(argc,argv,F_CELL_SET))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }

  sheet = SheetFromName(argv[1]);
  col = atoi(argv[2]);
  row = atoi(argv[3]);
    
  cell=sheet->cellFind(col,row);
  evt=new CellSetEvent(sheet, cell);
  sheet->RegisterEvent(evt);

  cell = sheet->cellSet(col,row,argv[4]);

  if (cell->modified) {
    sheet->recalc(cell);
  }
    
  if (col > sheet->max_col)
    sheet->max_col = col;
  if (row > sheet->max_row)
    sheet->max_row = row;

  if (col > sheet->max_col_used) sheet->max_col_used = col;
  if (row > sheet->max_row_used) sheet->max_row_used = row;
  if (col < sheet->min_col_used) sheet->min_col_used = col;
  if (row < sheet->min_row_used) sheet->min_row_used = row;

  return(TCL_OK);
}

int Xxl_CellValue(ClientData clientData, Tcl_Interp *interp,   
		  int argc, char *argv[]) {
  Tcl_HashEntry *entry;
  int newv;
  int row,col;
  char *st;
  char aux[20];
  Cell *cell;
  Sheet *sheet;
  char *err;
 
  if((err=CheckArgs(argc,argv,F_CELL_VALUE))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }

  sheet = SheetFromName(argv[1]);    
  col = atoi(argv[2]);
  row = atoi(argv[3]);
    
  cell = sheet->cellExists(col,row);
  if (cell) 
    switch(cell->type) {
    case CODE_BLANK:
      strcpy(interp->result,"");
      break;
    case CODE_INTEGER:
      sprintf(interp->result,"%d",cell->ivalue);
      break;
    case CODE_NUMBER:
      sprintf(interp->result,"%g",cell->value);
      break;
    case CODE_LABEL:
      char *tmp;
      tmp=cell->label;
      tmp++;
      strcpy(interp->result,tmp);
      break;
    case CODE_FORMULA:
      sprintf(interp->result,"%g",cell->value);
      break;
    }
  else {
    strcpy(interp->result,"");
  }
  return(TCL_OK);

}

int Xxl_CellGetNumber(ClientData clientData, Tcl_Interp *interp,   
		      int argc, char *argv[]) {
  Tcl_HashEntry *entry;
  int newv;
  int row,col;
  char *st;
  char aux[20];
  Cell *cell;
  Sheet *sheet;
  char *err;
 
  if((err=CheckArgs(argc,argv,F_CELL_GET_NUMBER))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  
  sheet = SheetFromName(argv[1]);    
  col = atoi(argv[2]);
  row = atoi(argv[3]);
    
  cell = sheet->cellExists(col,row);
  if (cell) 
    switch(cell->type) {
    case CODE_BLANK:
      strcpy(interp->result,"");
      break;
    case CODE_INTEGER:
      sprintf(interp->result,"%d",cell->ivalue);
      break;
    case CODE_NUMBER:
      sprintf(interp->result,"%g",cell->value);
      break;
    case CODE_LABEL:
      strcpy(interp->result,"");
      break;
    case CODE_FORMULA:
      sprintf(interp->result,"%g",cell->value);
      break;
    }
  else {
    strcpy(interp->result,"");
  }
  return(TCL_OK);

}

int Xxl_CellGet(ClientData clientData, Tcl_Interp *interp,   
                int argc, char *argv[]) {
  Tcl_HashEntry *entry;
  int newv;
  int row,col;
  char *st;
  int CellCoords[2];
  char aux[20];
  Cell *cell;
  Sheet *sheet;
  char *err;
 
  if((err=CheckArgs(argc,argv,F_CELL_GET))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }

  sheet = SheetFromName(argv[1]);    
  col = atoi(argv[2]);
  row = atoi(argv[3]);

  cell = sheet->cellExists(col,row);
  if (cell) 
    switch(cell->type) {
    case CODE_BLANK:
      strcpy(interp->result,"");
      break;
    case CODE_INTEGER:
      sprintf(interp->result,"%d",cell->ivalue);
      break;
    case CODE_NUMBER:
      sprintf(interp->result,"%g",cell->value);
      break;
    case CODE_LABEL:
      strcpy(interp->result,cell->label);
      break;
    case CODE_FORMULA:
      sprintf(interp->result,"=%s",sheet->formula_string(cell));
      break;
    }
  else {
    strcpy(interp->result,"");
  }
  return(TCL_OK);
}

int Xxl_CellCopy(ClientData clientData, Tcl_Interp *interp,   
		 int argc, char *argv[]) {
  Tcl_HashEntry *entry;
  int newv;
  short row1,col1,row2,col2;
  char *st;
  int CellCoords[2];
  char aux[20];
  Cell *cell1,*cell2;
  Sheet *sheet1, *sheet2;
  CellSetEvent *evt;
  char *err;
 
  if((err=CheckArgs(argc,argv,F_CELL_COPY))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }

  sheet1 = SheetFromName(argv[1]);    
  sheet2 = SheetFromName(argv[4]);    

  col1 = atoi(argv[2]);
  row1 = atoi(argv[3]);

  col2 = atoi(argv[5]);
  row2 = atoi(argv[6]);

  cell1 = sheet1->cellFind(col1,row1);
  cell2 = sheet2->cellFind(col2,row2);
    
  evt=new CellSetEvent(sheet2,cell2);
  sheet2->RegisterEvent(evt);
    
  if (cell2->dirty)
    sheet1->remove_from_dirty(cell2);

  *cell2 = *cell1;
  if (cell2->type == CODE_FORMULA) 
    sheet2->manipulateReferences(cell2,INSERT_REFERENCES);
  //    sheet2->global_recalc();
  sheet2->recalc(cell2);

  return(TCL_OK);

}

int Xxl_DefHeightInCells(ClientData clientData, Tcl_Interp *interp,   
			 int argc, char *argv[]) {
    
  sprintf(interp->result,"%d",DEFAULTHEIGHTINCELLS);
  return(TCL_OK);
}

int Xxl_DefWidthInCells(ClientData clientData, Tcl_Interp *interp,   
			int argc, char *argv[]) 
{
  sprintf(interp->result,"%d",DEFAULTWIDTHINCELLS);
  return(TCL_OK);
}

int Xxl_DefSheetWidth(ClientData clientData, Tcl_Interp *interp,   
		      int argc, char *argv[]) {
    
  sprintf(interp->result,"%d",DEFSHEETWIDTH);
  return(TCL_OK);
}

int Xxl_DefSheetHeight(ClientData clientData, Tcl_Interp *interp,   
		       int argc, char *argv[]) {
    
  sprintf(interp->result,"%d",DEFSHEETHEIGHT);
  return(TCL_OK);
}

int Xxl_SetActiveSheet(ClientData clientData, Tcl_Interp *interp,   
		       int argc, char *argv[]) 
{
  Sheet *sheet;
  char *err;

  if((err=CheckArgs(argc,argv,F_SET_ACTIVE_SHEET))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet = SheetFromName(argv[1]);
  if (sheet == NULL) 
    return(TCL_OK);
  global_col_origin = sheet->canvas_info->col_origin;
  global_row_origin = sheet->canvas_info->row_origin;
  return(TCL_OK);
}


int Xxl_SetNewCanvasSize(ClientData clientData, Tcl_Interp *interp,   
			 int argc, char *argv[]) {
  Tcl_HashEntry *hPtr;    
  Sheet *sheet,*lixo;
  Namespace *namespc=NULL;
  char *err;

  if((err=CheckArgs(argc,argv,F_SET_NEW_CANVAS_SIZE))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }

  sheet = SheetFromName(argv[1]);
  if (sheet == NULL) {
    fprintf(stderr,"Sheet %s does not exist\n",argv[1]);
    return(TCL_OK);
  }

  /* We may get here without the canvas_info having been created yet
     if not there yet*/

  if (sheet->canvas_info->cmdPtr == NULL) {
    newCanvas(sheet->canvas_info,interp,sheet->canvas,
	      sheet->width(),sheet->height());
    register Interp *iPtr = (Interp *) interp;
    /* acrescentado durante o port para versao 8.0, temos que procurar
       no namespace do interpretador em vez de o fazermos directamente
       nos comandos que lhe estao associados. Sera que e preciso alterar
       para por isto a procurar nos namespaces acima ate ao global ?? */
    namespc=iPtr->globalNsPtr;
    hPtr = Tcl_FindHashEntry(&namespc->cmdTable, sheet->canvas);
    sheet->canvas_info->cmdPtr = (Command *) Tcl_GetHashValue(hPtr);
  }

  sheet->canvas_info->width = atoi(argv[2]);
  sheet->canvas_info->height = atoi(argv[3]);


  return(TCL_OK);

}

int reference_x_origin,reference_y_origin;

int Xxl_ResetScroll(ClientData clientData, Tcl_Interp *interp,   
		    int argc, char *argv[]) {
  Sheet *sheet;
  char *err;

  if((err=CheckArgs(argc,argv,F_RESET_SCROLL))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet = SheetFromName(argv[1]);
  reference_x_origin = sheet->canvas_info->x_origin;
  reference_y_origin = sheet->canvas_info->y_origin;
  return(TCL_OK);
}


int Xxl_ScrollFraction(ClientData clientData, Tcl_Interp *interp,   
		       int argc, char *argv[]) {
  Sheet *sheet;
  int x,y;
  float fx,fy;
  char mode;
  Canvas *c;
  char *err;

  if((err=CheckArgs(argc,argv,F_SCROLL_FRACTION))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet = SheetFromName(argv[1]);
  c = sheet->canvas_info;
  mode = argv[2][0];
  switch (mode) {
  case 'h':
    fx = atof(argv[3]);
    x = (int) (fx*c->col_origin[sheet->max_col]) ;
    x = c->col_origin[cell_col(x,c)]-4;
    y = c->y_origin;
    break;
  case 'v':
    fy = atof(argv[3]);
    y = (int) (fy*c->row_origin[sheet->max_row]) ;
    y = c->row_origin[cell_row(y,c)]-4;
    x = c->x_origin;
    break;
  case 'd':
    y = c->row_origin[MAX(0,cell_row(c->y_origin+4,c)+1)]-4;
    x = c->x_origin;
    break;
  case 'u':
    y = c->row_origin[MAX(0,cell_row(c->y_origin+4,c)-1)]-4;
    x = c->x_origin;
    break;
  case 'l':
    x = c->col_origin[MAX(0,cell_col(c->x_origin+4,c)-1)]-4;
    y = c->y_origin;
    break;
  case 'r': 
    x = c->col_origin[MAX(0,cell_col(c->x_origin+4,c)+1)]-4;
    y = c->y_origin;
    break;
  }

  /*    fprintf(stderr,"Got scroll to %d %d from %d %d",x,y,
            sheet->canvas_info->x_origin,
            sheet->canvas_info->y_origin);*/

  if (x < 0) x = -4;
  if (y < 0) y = -4;
  sheet->scroll_to(x,y,TRUE);
  return(TCL_OK);
}

int Xxl_Scroll(ClientData clientData, Tcl_Interp *interp,   
	       int argc, char *argv[]) {
  Sheet *sheet;
  int x,y;
  char *err;

  if((err=CheckArgs(argc,argv,F_SCROLL))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet = SheetFromName(argv[1]);
  x = sheet->canvas_info->col_origin[atoi(argv[2])]-4;
  y = sheet->canvas_info->row_origin[atoi(argv[3])]-4;
  sheet->scroll_to(x,y,TRUE);
  return(TCL_OK);
}

int Xxl_SheetXOrigin(ClientData clientData, Tcl_Interp *interp,   
		     int argc, char *argv[]) 
{
  Sheet *sheet;
  char *err;

  if((err=CheckArgs(argc,argv,F_SHEET_X_ORIGIN))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet=SheetFromName(argv[1]);
  sprintf(interp->result,"%d",sheet->canvas_info->x_origin);
  return(TCL_OK);
}

int Xxl_SheetYOrigin(ClientData clientData, Tcl_Interp *interp,   
		     int argc, char *argv[]) 
{
  Sheet *sheet;
  char *err;

  if((err=CheckArgs(argc,argv,F_SHEET_Y_ORIGIN))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet=SheetFromName(argv[1]);
  sprintf(interp->result,"%d",sheet->canvas_info->y_origin);
  return(TCL_OK);
}

int Xxl_NeedToScroll(ClientData clientData, Tcl_Interp *interp,   
		     int argc, char *argv[]) {
    
  Sheet *sheet,*lixo;
  int col,row;
  int x1,x2,y1,y2;
  int new_x_orig, new_y_orig;
  int need=0;
  char *err;

  if((err=CheckArgs(argc,argv,F_NEED_TO_SCROLL))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet = SheetFromName(argv[1]);
  col = atoi(argv[2]);
  row = atoi(argv[3]);
  if (col < 0) col = 0;
  if (row < 0) row = 0;
  x1 = sheet->canvas_info->col_origin[col];
  x2 = sheet->canvas_info->col_origin[col+1];
  y1 = sheet->canvas_info->row_origin[row];
  y2 = sheet->canvas_info->row_origin[row+1];
  new_x_orig = sheet->canvas_info->x_origin;
  new_y_orig = sheet->canvas_info->y_origin;
  if (x2 - sheet->canvas_info->x_origin > sheet->canvas_info->width) {
    new_x_orig = x2-sheet->canvas_info->width-3;
    need = 1;
  }
  if (x1 - sheet->canvas_info->x_origin < 0) {
    new_x_orig = x1-4;
    need = 1;
  }
  if (y2 - sheet->canvas_info->y_origin > sheet->canvas_info->height) {
    new_y_orig = y2-sheet->canvas_info->height-3;
    need = 1;
  }
  if (y1 - sheet->canvas_info->y_origin < 0) {
    new_y_orig = y1-4;
    need = 1;
  }
  sheet->scroll_to(new_x_orig,new_y_orig,TRUE);
  sprintf(interp->result,"%d",need);
  return(TCL_OK);
}

int Xxl_DefCellWidth(ClientData clientData, Tcl_Interp *interp,   
		     int argc, char *argv[]) {
    
  sprintf(interp->result,"%d",DEFCELLWIDTH);
  return(TCL_OK);
}

/* Gives the width of one character in a given font */
int Xxl_CharWidth(ClientData clientData, Tcl_Interp *interp,   
		  int argc, char *argv[]) {
  sprintf(interp->result,"%d",CharWidth());
  return(TCL_OK);
  
}

int Xxl_DefCellHeight(ClientData clientData, Tcl_Interp *interp,   
		      int argc, char *argv[]) {
    
  sprintf(interp->result,"%d",DEFCELLHEIGHT);
  return(TCL_OK);
}

int Xxl_ColOrigin(ClientData clientData, Tcl_Interp *interp,   
		  int argc, char *argv[]) 
{
  Sheet *sheet;
  char *err;

  if((err=CheckArgs(argc,argv,F_COL_ORIGIN))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }

  sheet=SheetFromName(argv[1]);    
  if (sheet == NULL) 
    return(TCL_OK);

  int x = atoi(argv[2]);

  if (sheet->canvas_info->cmdPtr == NULL) 
    x = x*DEFCELLWIDTH;
  else
    x = sheet->canvas_info->col_origin[x];

  sprintf(interp->result,"%d",x);
  return(TCL_OK);
}

int Xxl_RowOrigin(ClientData clientData, Tcl_Interp *interp,   
		  int argc, char *argv[]) 
{
  Sheet *sheet;
  char *err;

  if((err=CheckArgs(argc,argv,F_ROW_ORIGIN))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet=SheetFromName(argv[1]);
  if (sheet == NULL)
    return(TCL_OK);

  int y = atoi(argv[2]);

  if (sheet->canvas_info->cmdPtr == NULL) 
    y = y*DEFCELLHEIGHT;
  else 
    y = sheet->canvas_info->row_origin[y];

  sprintf(interp->result,"%d",y);
  return(TCL_OK);
}


int Xxl_CellCol(ClientData clientData, Tcl_Interp *interp,   
                int argc, char *argv[]) 
{
  int i,j;
  Sheet *sheet;
  int x;
  char *err;

  if((err=CheckArgs(argc,argv,F_CELL_COL))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }

  sheet =  SheetFromName(argv[1]);    
  x = atoi(argv[2]);
  i = cell_col(x,sheet->canvas_info);
  sprintf(interp->result,"%d",i);

  return(TCL_OK);
}

int Xxl_CellRow(ClientData clientData, Tcl_Interp *interp,   
                int argc, char *argv[]) 
{
  int i,j;
  Sheet *sheet;
  int y;
  char *err;

  if((err=CheckArgs(argc,argv,F_CELL_ROW))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }

  sheet =  SheetFromName(argv[1]);    
  y = atoi(argv[2]);
  i = cell_row(y,sheet->canvas_info);
  sprintf(interp->result,"%d",i);

  return(TCL_OK);
}

int Xxl_GetColWidth(ClientData clientData, Tcl_Interp *interp,   
		    int argc, char *argv[]) {
  Sheet *sheet;
  int diff;
  int i;
  char *err;

  if((err=CheckArgs(argc,argv,F_GET_COL_WIDTH))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet=SheetFromName(argv[1]);
  short col = atoi(argv[2]);
  sprintf(interp->result,"%d",sheet->cell_width(col));
  return(TCL_OK);
}

int Xxl_GetMaxTextWidth(ClientData clientData, Tcl_Interp *interp,   
			int argc, char *argv[]) {
  Sheet *sheet;
  int wid;
  int i;
  char *err;

  if((err=CheckArgs(argc,argv,F_MAX_TEXT_WIDTH))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet=SheetFromName(argv[1]);
  short col = atoi(argv[2]);
  wid = sheet->max_text_width(col);
  sprintf(interp->result,"%d",wid+2*INTERNAL_CELL_BORDER);
  return(TCL_OK);
}

int Xxl_GetRowHeight(ClientData clientData, Tcl_Interp *interp,   
		     int argc, char *argv[]) 
{
  Sheet *sheet;
  int diff;
  int i;
  char *err;

  if((err=CheckArgs(argc,argv,F_GET_ROW_HEIGHT))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet=SheetFromName(argv[1]);
  short row = atoi(argv[2]);
  sprintf(interp->result,"%d",sheet->cell_height(row));
  return(TCL_OK);
}

int Xxl_InsertRowOrColumn(ClientData clientData, Tcl_Interp *interp,   
			  int argc, char *argv[]) {
  int diff;
  int i;
  Cell *cell;
  Tcl_HashEntry *entry;
  Tcl_HashSearch pt;
  Oper_info info;
  Sheet *sheet;
  Cell_buffer buffer;
  Cell_buffer unmod;
  ColRowInsertEvent *evt;
  char command[512];
  ChangeGraphEvent *grp_evt; 
  char *err;

  if((err=CheckArgs(argc,argv,F_INSERT_ROW_OR_COLUMN))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }

  sheet = SheetFromName(argv[1]);
  info.ref_line = atoi(argv[2]);
  info.n = atoi(argv[3]);

  switch(argv[4][0]) {
  case 'c':
    info.type = COL_OPERATION;
      for(int count=0; count<=sheet->graphs_cnt; count++)
	if (sheet->graphs[count])
	  {
	    Graph *grp = sheet->graphs[count];
	    if (grp->range.end.col >= info.ref_line)
	      {
		sheet->remove_graph_deps(grp);
	      }
	  };

    break;
  case 'r':
    info.type = ROW_OPERATION;
      for(int count=0; count<=sheet->graphs_cnt; count++)
	if (sheet->graphs[count])
	  {
	    Graph *grp = sheet->graphs[count];
	    if (grp->range.end.row >= info.ref_line)
	      {
		sheet->remove_graph_deps(grp);
	      }
	  };

    break;
  }

  if(atoi(argv[5])==1)
    {
      evt=new ColRowInsertEvent(sheet, argv[4][0],info.ref_line,info.n);
      sheet->RegisterEvent(evt);
    }

  for( entry = Tcl_FirstHashEntry(&(sheet->cells),&pt); entry ; 
       entry = Tcl_NextHashEntry(&pt)) {
    cell = (Cell*)Tcl_GetHashValue(entry);
    if (cell->type == CODE_FORMULA)
      sheet->manipulateReferences(cell,INSERT_ROW_OR_COLUMN,&info);
  }

  for( entry = Tcl_FirstHashEntry(&(sheet->cells),&pt); entry ; 
       entry = Tcl_NextHashEntry(&pt)) {
    cell = (Cell*)Tcl_GetHashValue(entry);
    switch(argv[4][0]) {
    case 'c':
      if (cell->col >= info.ref_line){
	sheet->copyToBuffer(cell,buffer); 
	sheet->removeFromCellList(cell);
      } else {
	sheet->copyToBuffer(cell,unmod); 
      };

      break;
    case 'r':
      if (cell->row >= info.ref_line){
	sheet->copyToBuffer(cell,buffer); 
	sheet->removeFromCellList(cell);
      } else {
	sheet->copyToBuffer(cell,unmod); 
      }
      break;
    default:
      internal_error();
    }

  }
  for(i=0; i<buffer.cells.cnt; i++) {
    cell = buffer.cells[i];
    switch(argv[4][0]) {
    case 'c':
      cell->col += info.n;
      break;
    case 'r':
      cell->row += info.n;
      break;
    }
    sheet->insertInCellList(cell);
  }
  for(i=0; i<buffer.cells.cnt; i++) {
    cell = buffer.cells[i];
    if (cell->type == CODE_FORMULA)
      sheet->manipulateReferences(cell,INSERT_REFERENCES,NULL);
  }    
  for(i=0; i<unmod.cells.cnt; i++) {
    cell = unmod.cells[i];
    if (cell->type == CODE_FORMULA)
      sheet->manipulateReferences(cell,INSERT_REFERENCES,NULL);
  }    

  switch(argv[4][0]) {
  case 'c':
    for(i=sheet->cols.cnt; i>=info.ref_line; i--) {
      sheet->cols[i+info.n].cells = sheet->cols[i].cells;
    }
    for(i=info.ref_line; i-info.ref_line < info.n; i++) 
      sheet->cols[i].cells.cnt = 0;
    break;
  case 'r':
    for(i=sheet->rows.cnt; i>=info.ref_line; i--) {
      sheet->rows[i+info.n].cells = sheet->rows[i].cells;
    }
    for(i=info.ref_line; i-info.ref_line < info.n; i++) 
      sheet->rows[i].cells.cnt = 0;
    break;
  }



  switch(argv[4][0]) {
    
  case 'c':
    if(atoi(argv[5])==1)
      {
	for(int count=0; count<=sheet->graphs_cnt; count++)
	  
	  {
	    if (sheet->graphs[count]) {
	      
	      Graph *grp = sheet->graphs[count];
	      
	      int startcol  = grp->range.start.col; 
	      int endcol    = grp->range.end.col; 
	      
	      if (endcol>=info.ref_line)
		{
		  grp_evt=new ChangeGraphEvent(sheet,*grp);
		  evt->RegisterGraphEvent(grp_evt);
		}
	      
	      if (startcol>info.ref_line)
		{
		  startcol+=1;
		  endcol+=1;
		  
		  grp->range.start.col=startcol;
		  grp->range.end.col=endcol;
		  
		  sheet->add_graph_deps(grp);
		  
		  sprintf(command,
			  "[canvasFromSheet $activeSheet] delete graph_%d
                          doDrawGraph [GetGraphParams $activeSheet %d] %d ",
			  grp->number,grp->number,grp->number);
                    
		  sheet->RunScript(command);
		  
		}

	      else if ((startcol<=info.ref_line)&&(endcol>=info.ref_line))
		{
		  endcol+=1;
		  grp->range.end.col=endcol;
		  
		  sheet->add_graph_deps(grp);
		  
		  sprintf(command,
			  "[canvasFromSheet $activeSheet] delete graph_%d
                          doDrawGraph [GetGraphParams $activeSheet %d] %d ",
			  grp->number,grp->number,grp->number);
			
		  sheet->RunScript(command);
		}
	    }
	  }
      };

    break;
  case 'r':
    if(atoi(argv[5])==1)
      {
	for(int count=0; count<=sheet->graphs_cnt; count++)
	  
	  {
	    if (sheet->graphs[count]) {
	      
	      Graph *grp = sheet->graphs[count];
	      
	      int startrow  = grp->range.start.row; 
	      int endrow    = grp->range.end.row; 
	      
	      if (endrow>=info.ref_line)
		{
		  grp_evt=new ChangeGraphEvent(sheet,*grp);
		  evt->RegisterGraphEvent(grp_evt);
		}
	      
	      if (startrow>info.ref_line)
		{
		  startrow+=1;
		  endrow+=1;
		  
		  grp->range.start.row=startrow;
		  grp->range.end.row=endrow;
		  
		  sheet->add_graph_deps(grp);
		  
		  sprintf(command,
			  "[canvasFromSheet $activeSheet] delete graph_%d
                          doDrawGraph [GetGraphParams $activeSheet %d] %d ",
			  grp->number,grp->number,grp->number);
                    
		  sheet->RunScript(command);
		  
		}

	      else if ((startrow<=info.ref_line)&&(endrow>=info.ref_line))
		{
		  endrow+=1;
		  grp->range.end.row=endrow;
		  
		  sheet->add_graph_deps(grp);
		  
		  sprintf(command,
			  "[canvasFromSheet $activeSheet] delete graph_%d
                          doDrawGraph [GetGraphParams $activeSheet %d] %d ",
			  grp->number,grp->number,grp->number);
			
		  sheet->RunScript(command);
		}
	    }
	  }
      };

    break;
  };

  return(TCL_OK);
}

int Xxl_DeleteRowOrColumn(ClientData clientData, Tcl_Interp *interp,   
			  int argc, char *argv[]) 
{
  int c,r,cr_num, cr_tot;
  Sheet *sheet;
  Cell *cell,aux;
  Tcl_HashEntry *entry;
  Tcl_HashSearch pt;
  Cell_buffer buffer;
  array<Cell *> to_display;
  ColRowDeleteEvent *evt;
  char command[512];
  ChangeGraphEvent *grp_evt; 
  DelGraphEvent *grp_evt_del;
  char *err;

  if((err=CheckArgs(argc,argv,F_DELETE_ROW_OR_COLUMN))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }

  sheet = SheetFromName(argv[1]);
  cr_num=atoi(argv[2]); // numero da primeira coluna ou linha a apagar
  cr_tot=atoi(argv[3]); // total de colunas ou linhas a apagar
  
  if(atoi(argv[5])==1)
    {
      evt=new ColRowDeleteEvent(sheet, argv[4][0],cr_num,cr_tot);
      sheet->RegisterEvent(evt);
    }
  
  switch(argv[4][0])
    {
    case 'c':

      for(int count=0; count<=sheet->graphs_cnt; count++)
	if (sheet->graphs[count])
	  {
	    Graph *grp = sheet->graphs[count];
	    if (grp->range.end.col >= cr_num)
	      {
		sheet->remove_graph_deps(grp);
	      }
	  };
    
      for(c=cr_num;c<=sheet->max_col_used;c++)
	for(r=0;r<=sheet->max_row_used;r++)
	  {
	    if((cell=sheet->cellExists(c,r))!=NULL)
	      {
		if(cell->type==CODE_FORMULA)
		  sheet->manipulateReferences(cell,REMOVE_REFERENCES,NULL);
		sheet->cellSet(c,r,"");
		to_display+=cell;
	      }
	    if((cell=sheet->cellExists(c+cr_tot,r))!=NULL)
	      {
		if(cell->type==CODE_FORMULA)
		  sheet->manipulateReferences(cell,REMOVE_REFERENCES,NULL);
		aux.loadvalue(*cell);
		sheet->cellSet(c,r,"");
		to_display+=cell;
		cell=sheet->cellFind(c,r);
		cell->loadvalue(aux);
		if(cell->type==CODE_FORMULA)
		  sheet->manipulateReferences(cell,INSERT_REFERENCES,NULL);
		to_display+=cell;
	      }
	  }
      sheet->max_col_used-=cr_tot;

      /*********************/

      if(atoi(argv[5])==1)
	{
	  for(int count=0; count<=sheet->graphs_cnt; count++)
	  
	    {
	      if (sheet->graphs[count]) {
		
		Graph *grp = sheet->graphs[count];
		int delgraph=0;
		
		int startcol  = grp->range.start.col; 
		int endcol    = grp->range.end.col; 
		
		if (endcol>=cr_num)
		  {
		    grp_evt=new ChangeGraphEvent(sheet,*grp);
		    grp_evt_del=new DelGraphEvent(sheet,*grp);
		  }
		

		for(int i=cr_tot-1;i>=0 && !delgraph;i--)
		  {
		    if (startcol>i+cr_num)
		      {
			startcol-=1;
			endcol-=1;
			
			grp->range.start.col=startcol;
			grp->range.end.col=endcol;
			
			sheet->add_graph_deps(grp);
			
			if(startcol>endcol+startcol)
			  sprintf(command,
				  "[canvasFromSheet $activeSheet] delete graph_%d",
				  grp->number);
			else
			  sprintf(command,
				  "[canvasFromSheet $activeSheet] delete graph_%d \
                                   doDrawGraph [GetGraphParams $activeSheet %d] %d ",
				  grp->number,grp->number,grp->number);
			
			sheet->RunScript(command);
			
			if(delgraph)
			  {
			    evt->RegisterGraphEvent(grp_evt_del);
			    delete grp_evt;
			  }
			else
			  {		    
			    evt->RegisterGraphEvent(grp_evt);
			    delete grp_evt_del;
			  }
 		      }

		    else if ((startcol<=i+cr_num)&&(endcol>=i+cr_num))
		      {
			endcol-=1;
			
			if(endcol<startcol)
			  {
			    delgraph=1;

			    sprintf(command,
				    "[canvasFromSheet $activeSheet] delete graph_%d",
				    grp->number);
			    sheet->RunScript(command);

			    sheet->remove_graph_deps(sheet->graphs[count]);
			    delete sheet->graphs[count];
			    sheet->graphs[count] = NULL;
			  }
			else
			  {			
			    grp->range.end.col=endcol;

			    sheet->add_graph_deps(grp);
			    
			    if(startcol>endcol+startcol)
			      sprintf(command,
				      "[canvasFromSheet $activeSheet] delete graph_%d",
				      grp->number);
			    else
			      sprintf(command,
				      "[canvasFromSheet $activeSheet] delete graph_%d
                                      doDrawGraph [GetGraphParams $activeSheet %d] %d ",
				      grp->number,grp->number,grp->number);
			
			    sheet->RunScript(command);
			  }
			if(delgraph)
			  {
			    evt->RegisterGraphEvent(grp_evt_del);
			    delete grp_evt;
			  }
			else
			  {		    
			    evt->RegisterGraphEvent(grp_evt);
			    delete grp_evt_del;
			  }
		      }
		    
		  }
		
	      }
	    }
	};
      
      /*********************/
      break;
    case 'r':

      for(int count=0; count<=sheet->graphs_cnt; count++)
	if (sheet->graphs[count])
	  {
	    Graph *grp = sheet->graphs[count];
	    if (grp->range.end.row >= cr_num)
	      sheet->remove_graph_deps(grp);
	  };

      for(r=cr_num;r<=sheet->max_row_used;r++)
	for(c=0;c<=sheet->max_col_used;c++)
	  {
	    if((cell=sheet->cellExists(c,r))!=NULL)
	      {
		if(cell->type==CODE_FORMULA)
		  sheet->manipulateReferences(cell,REMOVE_REFERENCES,NULL);
		sheet->cellSet(c,r,"");
		to_display+=cell;
	      }
	    if((cell=sheet->cellExists(c,r+cr_tot))!=NULL)
	      {
		if(cell->type==CODE_FORMULA)
		  sheet->manipulateReferences(cell,REMOVE_REFERENCES,NULL);
		aux.loadvalue(*cell);
		sheet->cellSet(c,r,"");
		to_display+=cell;
		cell=sheet->cellFind(c,r);
		cell->loadvalue(aux);
		if(cell->type==CODE_FORMULA)
		  sheet->manipulateReferences(cell,INSERT_REFERENCES,NULL);
		to_display+=cell;
	      }
	  }
      sheet->max_row_used-=cr_tot;
      /*******************************/
      if(atoi(argv[5])==1)
	{

	  for(int count=0; count<=sheet->graphs_cnt; count++)
	    {
	      
	      if (sheet->graphs[count]) {
		
		Graph *grp = sheet->graphs[count];
		int delgraph=0;
		
		int startrow  = grp->range.start.row; 
		int endrow    = grp->range.end.row; 

		if (endrow>=cr_num) 
		  {
		    grp_evt=new ChangeGraphEvent(sheet,*grp);
		    grp_evt_del=new DelGraphEvent(sheet,*grp);
		  }

		
		for(int i=cr_tot-1;i>=0 && !delgraph;i--)
		  {
		    if (startrow>i+cr_num)
		      {
			startrow-=1;
			endrow-=1;
			
			grp->range.start.row=startrow;
			grp->range.end.row=endrow;
			
			sheet->add_graph_deps(grp);
			
			if(startrow>endrow)
			  sprintf(command,
				  "[canvasFromSheet $activeSheet] delete graph_%d",
				  grp->number);
			else
			  sprintf(command,
				  "[canvasFromSheet $activeSheet] delete graph_%d
                                  doDrawGraph [GetGraphParams $activeSheet %d] %d ",
				  grp->number,grp->number,grp->number);
			
			sheet->RunScript(command);

			if(delgraph)
			  {
			    evt->RegisterGraphEvent(grp_evt_del);
			    delete grp_evt;
			  }
			else
			  {		    
			    evt->RegisterGraphEvent(grp_evt);
			    delete grp_evt_del;
			  }

		      }

		    else if ((startrow<=i+cr_num)&&(endrow>=i+cr_num))
		      {
			endrow-=1;

			if(endrow<startrow)
			  {
			    delgraph=1;

			    sprintf(command,
				    "[canvasFromSheet $activeSheet] delete graph_%d",
				    grp->number);
			    sheet->RunScript(command);

			    sheet->remove_graph_deps(sheet->graphs[count]);
			    delete sheet->graphs[count];
			    sheet->graphs[count] = NULL;
			  }
			else
			  {			
			    grp->range.end.row=endrow;
			    
			    sheet->add_graph_deps(grp);
			    
			    if(startrow>endrow)
			      sprintf(command,
				      "[canvasFromSheet $activeSheet] delete graph_%d",
				      grp->number);
			    else
			      sprintf(command,
				      "[canvasFromSheet $activeSheet] delete graph_%d
                                      doDrawGraph [GetGraphParams $activeSheet %d] %d ",
				      grp->number,grp->number,grp->number);
			   
			    sheet->RunScript(command);
			  }
			if(delgraph)
			  {
			    evt->RegisterGraphEvent(grp_evt_del);
			    delete grp_evt;
			  }
			else
			  {		    
			    evt->RegisterGraphEvent(grp_evt);
			    delete grp_evt_del;
			  }

		      }
		  }
		
	      }
	    }
	};
      /*******************************/
      break;
    default:
      internal_error();
      break;
    }
  if(sheet->automatic_recalc)
    sheet->global_recalc();
  sheet->display(to_display);
  return(TCL_OK);
}

int Xxl_SetBorderItemId(ClientData clientData, Tcl_Interp *interp,   
			int argc, char *argv[]) 
{
  Sheet *sheet;
  char *err;
  if((err=CheckArgs(argc,argv,F_SET_BORDER_ITEM_ID))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet = SheetFromName(argv[1]);    
  sheet->borderItemId = atoi(argv[2]);
  return(TCL_OK);
}

int Xxl_SetColWidth(ClientData clientData, Tcl_Interp *interp,   
		    int argc, char *argv[]) 
{
  Sheet *sheet;
  char *err;
  if((err=CheckArgs(argc,argv,F_SET_COL_WIDTH))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }

  int diff;
  int i;
  Cell *cell;
  SetColWidthEvent *evt;

  sheet = SheetFromName(argv[1]);    
  short col = atoi(argv[2]);
  int width = atoi(argv[3]);
  int prev_wid = sheet->cell_width(col);

  evt=new SetColWidthEvent(sheet, col);
  sheet->RegisterEvent(evt);

  sheet->set_col_width(col,width);

  for (i=0; i<sheet->cols[col].cells.cnt ; i++) {
    cell = sheet->cols[col].cells[i];
    if (cell->wid+2*INTERNAL_CELL_BORDER > prev_wid 
	|| cell->wid+2*INTERNAL_CELL_BORDER > width) 
      sheet->display(cell);
  }
  return(TCL_OK);
}

int Xxl_SetRowHeight(ClientData clientData, Tcl_Interp *interp,   
		     int argc, char *argv[]) {
  Sheet *sheet;
  char *err;
  if((err=CheckArgs(argc,argv,F_SET_ROW_HEIGHT))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }

  sheet = SheetFromName(argv[1]);    
  int diff;
  int i;
  SetRowHeightEvent *evt;

  short row = atoi(argv[2]);
  int height = atoi(argv[3]);
    
  evt=new SetRowHeightEvent(sheet,row);
  sheet->RegisterEvent(evt);
  sheet->set_row_height(row,height);

  return(TCL_OK);
}

int Xxl_RangeCopy(ClientData clientData, Tcl_Interp *interp,   
		  int argc, char *argv[]) {
  Tcl_HashEntry *entry;
  int newv;
  short row1,col1,row2,col2;
  char *st;
  int CellCoords[2];
  char aux[20];
  Cell *cell1,*cell2;
  Sheet *sheet1, *sheet2;
  Ref rf;
  Ref rfrom;
  short r_width, r_height;
  short c,r;
  array<Cell *> to_calc;
  Range rg;
  int i;
  RangeEvent *evt;
  char *err;

  if((err=CheckArgs(argc,argv,F_RANGE_COPY))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }

  sheet1 = SheetFromName(argv[1]);    
  sheet2 = SheetFromName(argv[6]);    
  rg.start.col = atoi(argv[7]);
  rg.start.row = atoi(argv[8]);
  rg.end.col = atoi(argv[9]);
  rg.end.row = atoi(argv[10]);
  Range_iter rang(rg);
  rfrom.col = atoi(argv[2]);
  rfrom.row = atoi(argv[3]);
  r_width = atoi(argv[4])-atoi(argv[2])+1;
  r_height = atoi(argv[5])-atoi(argv[3])+1;

  evt=new RangeEvent(sheet2,rg.start.col,rg.start.row,
		     rg.end.col,rg.end.row);
  sheet2->RegisterEvent(evt);

  for(rang.first(rf); !rang.last();  rang.next(rf))
    {
      c = ((rf.col-rg.start.col) % r_width) + rfrom.col;
      r = ((rf.row-rg.start.row) % r_height) + rfrom.row;
      cell1 = sheet1->cellFind(c,r);
      cell2 = sheet2->cellFind(rf.col,rf.row);
      *cell2 = *cell1;
      if (cell2->type == CODE_FORMULA) 
	sheet2->manipulateReferences(cell2,INSERT_REFERENCES);
      to_calc += cell2;
    }
  sheet2->recalc(to_calc);
  return(TCL_OK);
}

int Xxl_SortCells(ClientData clientData, Tcl_Interp *interp,   
		  int argc, char *argv[])
{
  Sheet *sheet;
  Range rng;
  Ref aux;
  short end,i,j,k,RowOrCol,Key;
  SortList *list,*cur,*act;
  array<Cell *> to_calc;
  RangeEvent *evt;
  char *err;

  if((err=CheckArgs(argc,argv,F_SORT_CELLS))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }

  sheet=SheetFromName(argv[1]);
  rng.start.col=atoi(argv[2]);
  rng.start.row=atoi(argv[3]);
  rng.end.col=atoi(argv[4]);
  rng.end.row=atoi(argv[5]);
  sort_dir=atoi(argv[6]);
  RowOrCol=atoi(argv[7]);
  Key=atoi(argv[8]);

  evt=new RangeEvent(sheet,
		     rng.start.col,rng.start.row,
		     rng.end.col,rng.end.row);
  sheet->RegisterEvent(evt);
  
  if(RowOrCol==0)
    list=(SortList*) calloc(rng.end.row-rng.start.row+1,sizeof(SortList));
  else
    list=(SortList*) calloc(rng.end.col-rng.start.col+1,sizeof(SortList));

  /* build an auxiliary array with pointers to hash struct to help sorting */
  if(RowOrCol==0)
    list=BuildSortRowVector(sheet,list,rng,&i);
  else
    list=BuildSortColumnVector(sheet,list,rng,&i);

  /* make key row or column become the array column */
  if(Key!=0)
    list=SwapKeyValue(list,i,Key);
    
  /* sort array */
  qsort((void*) list,i,sizeof(SortList),CompareCells);

  /* replace key column at proper position */
  if(Key!=0)
    list=SwapKeyValue(list,i,Key);

  /* rearrange ordered cells and redraw them in their new positions */
  if(RowOrCol==0)
    list=RearrangeSortRow(sheet,list,rng,&i,&to_calc);
  else
    list=RearrangeSortColumn(sheet,list,rng,&i,&to_calc);

  delete list;
  sheet->recalc(to_calc);
  return(TCL_OK);
}

void copy_to_paste_buffer(Sheet *sheet, short c_i, short r_i, 
                          short c_f, short r_f){
  Cell *cell;
  Ref rf;

  paste_buffer.sheet = sheet;
  paste_buffer.rg.start.col = (c_i < sheet->min_col_used ? 
			       sheet->min_col_used : c_i);
  paste_buffer.rg.start.row = (r_i < sheet->min_row_used ?
			       sheet->min_row_used : r_i);
  paste_buffer.rg.end.col = (c_f > sheet->max_col_used ? 
			     sheet->max_col_used : c_f);
  paste_buffer.rg.end.row = (r_f > sheet->max_row_used ?
			     sheet->max_row_used : r_f);

  Range_iter rang(paste_buffer.rg);

  for(rang.first(rf); !rang.last();  rang.next(rf)){
    cell = sheet->cellFind(rf.col,rf.row);
    paste_buffer.cells += *cell;
  }
}

int Xxl_CopyToPasteBuffer(ClientData clientData, Tcl_Interp *interp,   
			  int argc, char *argv[]) {
  Sheet *sheet1;
  char *err;

  if((err=CheckArgs(argc,argv,F_COPY_TO_PASTE_BUFFER))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }

  sheet1 = SheetFromName(argv[1]);    

  paste_buffer.cells.cnt = 0;
  paste_buffer.type = PASTE_CELLS;
    
  if (argc == 6) 
    copy_to_paste_buffer(sheet1,atoi(argv[2]),atoi(argv[3]),
			 atoi(argv[4]),atoi(argv[5])) ;
  else
    copy_to_paste_buffer(sheet1,atoi(argv[2]),atoi(argv[3]),
			 atoi(argv[2]),atoi(argv[3])) ;

  return(TCL_OK);
}

void paste(Sheet *sheet, short c_i, short r_i, 
	   short c_f, short r_f, int what, int how)
{
  Tcl_HashEntry *entry;
  int newv;
  short row1,col1,row2,col2;
  char *st;
  int CellCoords[2];
  char aux[20];
  Cell *cell1,*cell2;
  Ref rf;
  Ref rfrom;
  short r_width, r_height;
  short c,r;
  array<Cell *> to_calc;

  int i;

  r_width = paste_buffer.rg.end.col-paste_buffer.rg.start.col+1;
  r_height = paste_buffer.rg.end.row-paste_buffer.rg.start.row+1;

  Range rg;
  rg.start.col = c_i;
  rg.start.row = r_i;
  if (c_f != -1) 
    {
      rg.end.col = c_f;
      rg.end.row = r_f;
    } 
  else 
    {
      rg.end.col = rg.start.col+r_width-1;
      rg.end.row = rg.start.row+r_height-1;
    }
  Range_iter rang(rg);
  rfrom.col = paste_buffer.rg.start.col;
  rfrom.row = paste_buffer.rg.start.row;

  short cc,rr;

  for(rang.first(rf); !rang.last();  rang.next(rf))
    {
      cc = ((rf.col-rg.start.col) % r_width);
      rr = ((rf.row-rg.start.row) % r_height);
      c = cc + rfrom.col;
      r = rr + rfrom.row;
      cell1 = &(paste_buffer.cells[cc*r_height+rr]);
      cell2 = sheet->cellFind(rf.col,rf.row);
      //*cell2 = *cell1;
      paste_cell(sheet,cell1,cell2,what,how);
      if (cell2->type == CODE_FORMULA)
	sheet->manipulateReferences(cell2,INSERT_REFERENCES);
      to_calc += cell2;
    }
  sheet->recalc(to_calc);
}

int Xxl_Paste(ClientData clientData,Tcl_Interp *interp,
	      int argc,char *argv[])
{
  Sheet *sheet;
  RangeEvent *evt;
  char *err;
  int what, how;

  if((err=CheckArgs(argc,argv,F_PASTE))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }
  sheet = SheetFromName(argv[1]);    
  if (argc == 6) 
    evt=new RangeEvent(sheet,
		       atoi(argv[2]),atoi(argv[3]),
		       paste_buffer.rg.end.col-paste_buffer.rg.start.col+
		       atoi(argv[2]),
		       paste_buffer.rg.end.row-paste_buffer.rg.start.row+
		       atoi(argv[3]));
  if (argc == 8) 
    evt=new RangeEvent(sheet, 
		       atoi(argv[2]),atoi(argv[3]),
		       atoi(argv[4]),atoi(argv[5]));
  sheet->RegisterEvent(evt);
  if (argc == 6)
    {
      what=atoi(argv[4]);
      how=atoi(argv[5]);
      paste(sheet, atoi(argv[2]),atoi(argv[3]),-1,-1,what,how);
    }
  else if (argc == 8)
    {
      what=atoi(argv[6]);
      how=atoi(argv[7]);
      paste(sheet, atoi(argv[2]),atoi(argv[3]),
	    atoi(argv[4]),atoi(argv[5]),what,how);
    }
  return(TCL_OK);
}

int Xxl_Find(ClientData clientData, Tcl_Interp *interp,   
	     int argc, char *argv[]) 
{
  static int lastx=0,lasty=0;
  Sheet *sheet;
  int form,vals,exact,again,whole,i,j,res;
  char *err,*search,*replace=NULL;

  if((err=CheckArgs(argc,argv,F_FIND))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }

  sheet = SheetFromName(argv[1]);    
  form = atoi(argv[3]);  // search formulas ?
  vals = atoi(argv[4]);  // search values ?
  exact = atoi(argv[5]); // case sensitive ?
  whole = atoi(argv[6]); // match whole string ?
  again = atoi(argv[7]); // >0 - again up ; 0 - not again ; <0 - again down

  search=(char *) malloc(strlen(argv[2])+1);
  search=strcpy(search,argv[2]);
  if(argc==9)
    {
      replace=(char *) malloc(strlen(argv[8])+1);
      replace=strcpy(replace,argv[8]);
    }
  if(again==0)
    {
      for(lastx=0;lastx<=sheet->max_col_used;lastx++)
	for(lasty=0;lasty<=sheet->max_row_used;lasty++)
	  {
	    if((res=SearchCell(sheet,interp,search,
			       (argc==9 ? replace : (char *) NULL),
			       lastx,lasty,form,vals,exact,whole))!=-1)
	      {
		free(search);
		if(replace)
		  free(replace);
		return(res);
	      }
	  }
      lastx=0;
      lasty=0;
    }
  else if(again>0)
    {
      if(lasty<sheet->max_row_used)
	++lasty;
      else if(lastx<sheet->max_col_used)
	{
	  lasty=0;
	  ++lastx;
	}
      else
	{
	  lastx=0;
	  lasty=0;
	}
      for(;lastx<=sheet->max_col_used;lastx++)
	{
	  for(;lasty<=sheet->max_row_used;lasty++)
	    {
	      if((res=SearchCell(sheet,interp,search,
				 (argc==9 ? replace : (char *) NULL),
				 lastx,lasty,form,vals,exact,whole))!=-1)
		{
		  free(search);
		  if(replace)
		    free(replace);
		  return(res);
		}
	    }
	  lasty=0;
	}
    }
  else if(again<0)
    {
      if(lasty>0)
	--lasty;
      else if(lastx>0)
	{
	  lasty=sheet->max_row_used;
	  --lastx;
	}
      else
	{
	  lastx=sheet->max_col_used;
	  lasty=sheet->max_row_used;
	}
      for(;lastx>=0;lastx--)
	{
	  for(;lasty>=0;lasty--)
	    {
	      if((res=SearchCell(sheet,interp,search,
				 (argc==9 ? replace : (char *) NULL),
				 lastx,lasty,form,vals,exact,whole))!=-1)
		{
		  free(search);
		  if(replace)
		    free(replace);
		  return(res);
		}
	    }
	  lasty=sheet->max_row_used;
	}
    }
  sprintf(interp->result,"");
  return(TCL_OK);
}

void range_kill(Sheet *sheet, short c_i, short r_i, short c_f, short r_f)
{
  Range rg;

  rg.start.col = c_i;
  rg.start.row = r_i;
  rg.end.col = c_f;
  rg.end.row = r_f;
  Range_iter rang(rg);
  short c,r;
  Ref rf;
  Cell *cell;
  for(rang.first(rf); !rang.last();  rang.next(rf))
    {
      c=rf.col;
      r=rf.row;
      cell=sheet->cellSet(c,r,"");
      sheet->recalc(cell);
    }
}

int Xxl_RangeKill(ClientData clientData, Tcl_Interp *interp,   
		  int argc, char *argv[]) {
  Sheet *sheet1;
  RangeEvent *evt;
  int i;
  char *err;

  if((err=CheckArgs(argc,argv,F_RANGE_KILL))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }

  sheet1 = SheetFromName(argv[1]);    

  if (argc == 6) 
    evt=new RangeEvent(sheet1,atoi(argv[2]),atoi(argv[3]),
		       atoi(argv[4]),atoi(argv[5]));
  else if (argc == 4) 
    evt=new RangeEvent(sheet1,atoi(argv[2]),atoi(argv[3]),
		       atoi(argv[2]),atoi(argv[3]));
  
  sheet1->RegisterEvent(evt);
  
  if (argc == 6) 
    range_kill(sheet1, atoi(argv[2]),atoi(argv[3]),
	       atoi(argv[4]),atoi(argv[5]));
  else if (argc == 4) 
    range_kill(sheet1, atoi(argv[2]),atoi(argv[3]),
	       atoi(argv[2]),atoi(argv[3]));
    
  return(TCL_OK);
}

int Xxl_RangeMove(ClientData clientData, Tcl_Interp *interp,   
		  int argc, char *argv[]) {
  Sheet *sheet1;
  RangeEvent *evt_kill,*evt_paste;
  RangeMoveEvent *evt_move;
  char *err;

  if((err=CheckArgs(argc,argv,F_RANGE_MOVE))!=NULL)
    {
      sprintf(interp->result,err);
      return(TCL_ERROR);
    }

  sheet1 = SheetFromName(argv[1]);    

  evt_kill=
    new RangeEvent(sheet1,
		   paste_buffer.rg.start.col,paste_buffer.rg.start.row,
		   paste_buffer.rg.end.col,paste_buffer.rg.end.row);
  evt_paste=
    new RangeEvent(sheet1,atoi(argv[2]),atoi(argv[3]),
		   paste_buffer.rg.end.col-paste_buffer.rg.start.col+
		   atoi(argv[2]),
		   paste_buffer.rg.end.row-paste_buffer.rg.start.row+
		   atoi(argv[3]));
  evt_move=new RangeMoveEvent(evt_kill, evt_paste);
  sheet1->RegisterEvent(evt_move);

  range_kill(sheet1, paste_buffer.rg.start.col,paste_buffer.rg.start.row,
	     paste_buffer.rg.end.col,paste_buffer.rg.end.row);
  paste(sheet1, atoi(argv[2]),atoi(argv[3]),-1,-1,0,0);

  return(TCL_OK);
    
}

int Xxl_GetMaxColUsed (ClientData clientData, Tcl_Interp *interp,   
		  int argc, char *argv[]) 
{
  Sheet *sheet;

  sheet=SheetFromName(argv[1]);    
  if (sheet == NULL) 
    return(TCL_OK);

  sprintf(interp->result,"%d",sheet->max_col_used);
  return(TCL_OK);
}

int Xxl_GetMaxRowUsed (ClientData clientData, Tcl_Interp *interp,   
		  int argc, char *argv[]) 
{
  Sheet *sheet;

  sheet=SheetFromName(argv[1]);    
  if (sheet == NULL) 
    return(TCL_OK);

  sprintf(interp->result,"%d",sheet->max_row_used);
  return(TCL_OK);
}

int Xxl_GetMinColUsed (ClientData clientData, Tcl_Interp *interp,   
		  int argc, char *argv[]) 
{
  Sheet *sheet;

  sheet=SheetFromName(argv[1]);    
  if (sheet == NULL) 
    return(TCL_OK);

  sprintf(interp->result,"%d",sheet->min_col_used);
  return(TCL_OK);
}

int Xxl_GetMinRowUsed (ClientData clientData, Tcl_Interp *interp,   
		  int argc, char *argv[]) 
{
  Sheet *sheet;

  sheet=SheetFromName(argv[1]);    
  if (sheet == NULL) 
    return(TCL_OK);

  sprintf(interp->result,"%d",sheet->min_row_used);
  return(TCL_OK);
}

#define tmp1_width 6
#define tmp1_height 6
static char tmp1_bits[] = {
  0x01, 0x08, 0x00, 0x02, 0x10, 0x00, };

void 
create_commands(Tcl_Interp *interp) {
    
  Pixmap bitmap;
  Tk_DefineBitmap(interp, Tk_GetUid("gray11"), tmp1_bits,
		  tmp1_width, tmp1_height);

  Tcl_InitHashTable(&sheet_hash,TCL_STRING_KEYS);
  Tcl_InitHashTable(&font_hash,TCL_ONE_WORD_KEYS);

  /* Scripting     */
  Tcl_CreateCommand(interp,"RunScript",Xxl_RunScript,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);

  /* Undo and Redo */
  Tcl_CreateCommand(interp,"UndoRedo",Xxl_UndoRedo,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);

  /* Sort command */
  Tcl_CreateCommand(interp,"SortCells",Xxl_SortCells,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);

  /* Test command */
  Tcl_CreateCommand(interp,"XxlTest",Xxl_Test,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);

  /* Set and get cell values */
  Tcl_CreateCommand(interp,"CellSet",Xxl_CellSet,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"CellValue",Xxl_CellValue,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"CellGetNumber",Xxl_CellGetNumber,
		    (ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"CellGet",Xxl_CellGet,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"CellCopy",Xxl_CellCopy,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);

  /* Format operations */
  Tcl_CreateCommand(interp,"SetFont",Xxl_SetFont,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"SetFill",Xxl_SetFill,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"SetAlignment",Xxl_SetAlignment,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"SetFormat",Xxl_SetFormat,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"GetFormat",Xxl_GetFormat,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"SetBorders",Xxl_SetBorders,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"CellBorders",Xxl_CellBorders,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"PageBorders",Xxl_PageBorders,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"ToggleDisplayBorders",Xxl_ToggleDisplayBorders,
		    (ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"TogglePageBorders",Xxl_TogglePageBorders,
		    (ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);

  /* Range operations */
  Tcl_CreateCommand(interp,"RangeCopy",Xxl_RangeCopy,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"RangeKill",Xxl_RangeKill,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"RangeMove",Xxl_RangeMove,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);

  /* Edit operations */
  Tcl_CreateCommand(interp,"CopyToPasteBuffer",Xxl_CopyToPasteBuffer,
		    (ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"Paste",Xxl_Paste,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"Find",Xxl_Find,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"ColName",Xxl_ColName,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"CellName",Xxl_CellName,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"ParseRange",Xxl_ParseRange,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);

  /* Save and load sheets */
  Tcl_CreateCommand(interp,"SaveSheet",Xxl_SaveSheet,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"LoadSheet",Xxl_LoadSheet,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);

  /* Sheet manipulation functions */
  Tcl_CreateCommand(interp,"SetActiveSheet",Xxl_SetActiveSheet,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"NewSheet",Xxl_NewSheet,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"SheetRecalc",Xxl_SheetRecalc,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"RecalculateBeforeSaving",
		    Xxl_RecalculateBeforeSaving,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"SetAutomaticRecalc",Xxl_SetAutomaticRecalc,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"GetAutomaticRecalc",Xxl_GetAutomaticRecalc,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"ChangeSheetName",Xxl_ChangeSheetName,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"SheetExists",Xxl_SheetExists,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"SheetEmpty",Xxl_SheetEmpty,(ClientData *)NULL,
		    (Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"SheetModified",Xxl_SheetModified,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"RedrawSheet",Xxl_RedrawSheet,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"DeleteSheet",Xxl_DeleteSheet,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"SetPrintRange",Xxl_SetPrintRange,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"GetPrintRange",Xxl_GetPrintRange,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"SetPrintParams",Xxl_SetPrintParams,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"GetPrintParams",Xxl_GetPrintParams,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);

  /* Graph operations */
  Tcl_CreateCommand(interp,"SetGraphParams",Xxl_SetGraphParams,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"ChangeGraphParams",Xxl_ChangeGraphParams,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"GetGraphParams",Xxl_GetGraphParams,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"UsedGraphs",Xxl_UsedGraphs,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"FindGraph",Xxl_FindGraph,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"DeleteGraph",Xxl_DeleteGraph,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);

  /* Column and Row manipulation */
  Tcl_CreateCommand(interp,"CellCol",Xxl_CellCol,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"CellRow",Xxl_CellRow,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"SetColWidth",Xxl_SetColWidth,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"SetRowHeight",Xxl_SetRowHeight,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"InsertRowOrColumn",Xxl_InsertRowOrColumn,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"DeleteRowOrColumn",Xxl_DeleteRowOrColumn,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"GetColWidth",Xxl_GetColWidth,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"GetRowHeight",Xxl_GetRowHeight,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"ColOrigin",Xxl_ColOrigin,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"RowOrigin",Xxl_RowOrigin,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
    
  /* Get sheet information */

  Tcl_CreateCommand(interp,"GetMaxColUsed",Xxl_GetMaxColUsed,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"GetMaxRowUsed",Xxl_GetMaxRowUsed,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"GetMinColUsed",Xxl_GetMinColUsed,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"GetMinRowUsed",Xxl_GetMinRowUsed,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);

  /* Geometry manipulation routines */
  Tcl_CreateCommand(interp,"SheetXOrigin",Xxl_SheetXOrigin,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"SheetYOrigin",Xxl_SheetYOrigin,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"DefWidthInCells",Xxl_DefWidthInCells,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"DefHeightInCells",Xxl_DefHeightInCells,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"DefSheetWidth",Xxl_DefSheetWidth,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"DefSheetHeight",Xxl_DefSheetHeight,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"DefCellHeight",Xxl_DefCellHeight,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"DefCellWidth",Xxl_DefCellWidth,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"SetNewCanvasSize",Xxl_SetNewCanvasSize,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"NeedToScroll",Xxl_NeedToScroll,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"Scroll",Xxl_Scroll,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"ScrollFraction",Xxl_ScrollFraction,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"ResetScroll",Xxl_ResetScroll,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"MaxTextWidth",Xxl_GetMaxTextWidth,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
  Tcl_CreateCommand(interp,"SetBorderItemId",Xxl_SetBorderItemId,
		    (ClientData *)NULL,(Tcl_CmdDeleteProc *) NULL);
}




Sheet *CreateSheet(char *name,Tcl_Interp *interp) {
  Tcl_HashEntry *entry;
  Sheet *sheet; 
  int newv;

  entry = Tcl_CreateHashEntry(&sheet_hash,name,&newv);
  sheet = new Sheet(name,interp);

  Tcl_SetHashValue(entry,sheet);
  return(sheet);
}

Sheet *SheetFromName(char *name) {
  Tcl_HashEntry *entry;
  Sheet *sheet;

  entry = Tcl_FindHashEntry(&sheet_hash,name);
  if (entry == NULL) {
    return(NULL);
  }
    
  sheet = (Sheet*) Tcl_GetHashValue(entry);
  return(sheet);
}


int DefCellWidth() {
  return(CharWidth()*NCHARSPERCELL);
}

int CharWidth() {
  return(8);
}

// $Log: tcl_interf.cc,v $
// Revision 1.51  1998/10/25 23:38:39  cthulhu
// Moved auxiliary functions to interf_aux.
//
// Revision 1.50  1998/10/19 23:27:18  cthulhu
// Fixed previous bugix...
//
// Revision 1.49  1998/10/18 20:24:04  cthulhu
// Fixed some problems while deleting some row and columns that caused some segmentation  faults.
//
// Revision 1.48  1998/10/13 21:28:16  cthulhu
// Fixed column and row deletion when starting col or row was beyond max_col_used
// or max_row_used and when selection ran over max_col_used or max_row_used.
//
// Revision 1.47  1998/10/11 22:45:05  cthulhu
// Column and row deletion now do global sheet recalc to update all cell r
// 	eferences.
//
// Revision 1.46  1998/10/07 21:55:43  cthulhu
// PasteSpecial now works and was merged with old Paste command.
//
// Revision 1.45  1998/10/01 22:20:42  cthulhu
// Fixed paste scanning for columns and rows - should not scan full column/row
// 	anymore.
//
// Revision 1.44  1998/09/30 20:59:49  cthulhu
// SetGraphParams optimizes coordinate ranges by considering
// only those that fit bettewn MaxColUsed, MaxRowUsed,
// MinColUsed and MinRowUsed.
// Added some new commands to retrieve some sheet informations.
//
// Revision 1.43  1998/09/30 00:47:14  cthulhu
// First cut at paste special.
//
// Revision 1.42  1998/09/29 00:00:37  cthulhu
// Find and replace should now work.
// Added interface to checkargs.hh so parameter checking occurs in a standard
// 	way - should check for all sorts of errors when people call macros
// 	direcly.
//
// Revision 1.41  1998/08/25 20:58:54  cthulhu
// New command: RunScripts receives sheetname and filename, runs script using
// interpreter from sheet.
//
// Revision 1.40  1998/08/21 18:58:29  cthulhu
// SetFormat, Alignment, Borders, etc now accept column and row groups instead
// of being called one column/row at a time - undo/redo is now possible of groups
// of column/rows instead of just one by one.
//
// Revision 1.39  1998/08/06 21:08:53  aml
// Released alpha version of Abacus.
//
// Revision 1.38  1997/03/27  10:00:52  aml
// Started implementing graphs.
// Fixed bug in tkCanvasPs.c
// Created bindings for composite characters in Portuguese.
//
// Revision 1.37  1997/02/10 15:11:10  aml
// Print settings now are saved to file.
// Fixed buggy error message when loading sheets.
//
// Revision 1.36  1997/01/07  01:07:52  aml
// Error propagation for formulas fixed.
// Edit operations in place.
//
// Revision 1.35  1996/12/31  17:38:51  aml
// On-demand calculation improved. Loops are detected.
// Automatic recalculation can now be disabled.
// Printing was improved.
//
// Revision 1.34  1996/12/11 21:40:11  aml
// Sumif implemented.
// Diverse time functions implemented.
// Fixed needtoscroll2 to avoid out of control scroll.
//
// Revision 1.33  1996/11/22 16:29:27  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.32  1996/10/10 22:55:30  aml
// Print preview. Improvements on print command.
//
// Revision 1.31  1996/10/09 13:55:43  aml
// First cut of full print.
//
// Revision 1.30  1996/09/19 12:17:27  aml
// Created row and column insert.
// Fixed small problem with display of vergrown cells.
//
// Revision 1.29  1996/09/17 15:17:01  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.28  1996/09/16 18:43:26  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.27  1996/09/15  19:25:40  aml
// Rulling and shading.
// Optionally hide cell borders.
// Works well, but is very slow for large spreadsheets.
//
// Revision 1.26  1996/09/14  23:56:36  aml
// Created cell shading.
//
// Revision 1.25  1996/09/04  14:30:14  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.24  1996/09/02 10:52:20  aml
// Cell fonts created, loaded and saved.
// Row height created.
//
// Revision 1.23  1996/08/29  12:05:57  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.22  1996/08/26 12:09:03  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.21  1996/08/24  10:16:18  aml
// Scroll almost fixed. We are missing scroll steps.
// Several sheets work again, but need to be fixed right.
//
// Revision 1.20  1996/08/23  16:13:51  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.19  1996/07/23 14:01:22  aml
// Changed canvas widget to handle special items like cell borders.
// Seems to work well. Spreadsheet size is no more limited now.
//
// Revision 1.18  1996/04/23 09:43:11  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.17  1996/04/21 13:28:33  aml
// Sped up scroll functions, caching keys presses.
// First cut at handling overflowing cells.
// Overflow into ajoining filled cells not solved.
//
// Revision 1.16  1996/04/19 10:46:47  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.15  1996/03/07  20:33:21  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.14  1996/03/06  20:25:38  aml
// Improved user waiting times during redraw and loads by calling update.
//
// Revision 1.13  1996/03/01 13:08:54  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.12  1996/02/19 15:47:45  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.11  1996/02/13  21:55:25  aml
// Fixed problems with change to elf.
// RangeCopy created. Works !
// Pressed mouse leaving canvas will cause scroll. Works, but needs to
// keep moving.
//
// Revision 1.10  1996/02/13  12:04:02  aml
// Fixed bug with range definition via mouse.
// Fixed bug in range iterators.
//
// Revision 1.9  1996/01/27 23:10:36  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.8  1996/01/09  18:35:00  aml
// Load, save, open, close and exit now work properly (hopefuly).
// Sheet utility functions also work : SheetExists, SheetEmpty, SheetModified
//
// Revision 1.7  1996/01/07  09:07:59  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.6  1996/01/05  23:06:09  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.5  1995/12/28  19:20:42  aml
// Created skeleton to merge calculation engine
//
// Revision 1.4  1995/12/27  23:13:10  aml
// First draft of Sheet::display
//
// Revision 1.3  1995/12/14  12:13:09  aml
// Version 0.4.2
//
// Revision 1.2  1995/12/13  14:34:58  aml
// NewSheet, CellSet and CellGet created
//
// Revision 1.1  1995/12/06  14:20:35  aml
// Initial revision
//


