/*{{{}}}*/
/*{{{  #includes*/
#define _POSIX_SOURCE   1
#define _POSIX_C_SOURCE 2

#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "cat.h"
#include "default.h"
#include "display.h"
#include "eval.h"
#include "main.h"
#include "misc.h"
#include "scanner.h"
#include "parser.h"
#include "sheet.h"
#include "wgetc.h"
/*}}}  */

/*{{{  variables*/
static WINDOW *edit;
/*}}}  */

/*{{{  do_edit*/
static void do_edit(Sheet *cursheet)
{
  /*{{{  variables*/
  char buf[1024];
  const char *s;
  size_t x,offx;
  int curx,cury,curz;
  Token **t;
  /*}}}  */
    
  if (locked(cursheet,cursheet->curx,cursheet->cury,cursheet->curz)) line_msg(edit,EDIT,ISLOCKED);
  else
  {
    curx=cursheet->curx;
    cury=cursheet->cury;
    curz=cursheet->curz;
    offx=0;
    print(buf,sizeof(buf),0,1,getscientific(cursheet,cursheet->curx,cursheet->cury,cursheet->curz),-1,getcont(cursheet,cursheet->curx,cursheet->cury,cursheet->curz));
    s=buf+strlen(buf);
    do
    {
      x=s-buf;
      if (line_edit(cursheet,buf,sizeof(buf),edit,CONTENTS,&x,&offx)==-1) return;
      s=buf;
      t=scan(&s);
    } while (*s!='\0' && t==(Token**)0);
    if (t!=(Token**)0 && *t==(Token*)0) { free(t); t=(Token**)0; }
    cursheet->curx=curx;
    cursheet->cury=cury;
    cursheet->curz=curz;
    putcont(cursheet,cursheet->curx,cursheet->cury,cursheet->curz,t);
  }
}
/*}}}  */
/*{{{  do_label*/
static void do_label(Sheet *sheet)
{
  /*{{{  variables*/
  char buf[1024];
  size_t x,offx,ok;
  Token t;
  /*}}}  */

  if (locked(sheet,sheet->curx,sheet->cury,sheet->curz)) line_msg(edit,DOLABEL,ISLOCKED);
  else
  {
    ok=x=offx=0;
    strcpy(buf,getlabel(sheet,sheet->curx,sheet->cury,sheet->curz));
    do
    {
      if (line_idedit(buf,sizeof(buf),edit,DOLABEL,&x,&offx)==-1) return;
      if (buf[0]=='\0') ok=1;
      else
      {
        ok=((t=findlabel(sheet,buf)).type==EEK || (t.type==LOCATION && t.u.location[0]==sheet->curx && t.u.location[1]==sheet->cury && t.u.location[2]==sheet->curz));
        tfree(&t);
      }
    } while (!ok);
    setlabel(sheet,sheet->curx,sheet->cury,sheet->curz,buf);
  }
}
/*}}}  */
/*{{{  do_columnwidth*/
static void do_columnwidth(Sheet *cursheet)
{
  /*{{{  variables*/
  size_t x,offx;
  int n;
  /*}}}  */
            
  offx=0;
  x=0;
  n=columnwidth(cursheet,cursheet->curx,cursheet->curz);
  do if (line_numedit(&n,edit,COLWIDTH,&x,&offx)==-1) return; while (n<=0);
  setwidth(cursheet,cursheet->curx,cursheet->curz,n);
}
/*}}}  */
/*{{{  do_attribute*/
static void do_attribute(Sheet *cursheet)
{
  /*{{{  variables*/
  MenuChoice menu[10];
  int c;
  int n;
  int x,y,z;
  int x1,y1,z1;
  int x2,y2,z2;
  /*}}}  */
      
  /*{{{  create menu*/
  menu[0].str=strmalloc(ADLEFT);     menu[0].c='\0';
  menu[1].str=strmalloc(ADRIGHT);    menu[1].c='\0';
  menu[2].str=strmalloc(ADCENTERED); menu[2].c='\0';
  menu[3].str=strmalloc(DOSCI);      menu[3].c='\0';
  menu[4].str=strmalloc(NOSCI);      menu[4].c='\0';
  menu[5].str=strmalloc(PRECISION);  menu[5].c='\0';
  menu[6].str=strmalloc(SHADOW);     menu[6].c='\0';
  menu[7].str=strmalloc(LABEL);      menu[7].c='\0';
  menu[8].str=strmalloc(DOLOCK);     menu[8].c='\0';
  menu[9].str=(char*)0;
  /*}}}  */
  if (cursheet->mark1x==-1)
  /*{{{  range is the current cell*/
  {
    x1=x2=cursheet->curx;
    y1=y2=cursheet->cury;
    z1=z2=cursheet->curz;
  }
  /*}}}  */
  else
  /*{{{  range is the marked cube*/
  {
    x1=cursheet->mark1x; x2=cursheet->mark2x; order(&x1,&x2);
    y1=cursheet->mark1y; y2=cursheet->mark2y; order(&y1,&y2);
    z1=cursheet->mark1z; z2=cursheet->mark2z; order(&z1,&z2);
  }
  /*}}}  */
  if ((c=line_menu(edit,cursheet->mark1x==-1 ? CELLATTR : BLOCKATTR,menu,(int)getadjust(cursheet,x1,y1,z1)))!=-1)
  {
    if (cursheet->mark1x==-1 && c<=7 && locked(cursheet,cursheet->curx,cursheet->cury,cursheet->curz)) line_msg(edit,CELLATTR,ISLOCKED);
    else
    {
      if (c==5)
      {
        size_t x,offx;

        offx=0;
        x=0;
        n=getprecision(cursheet,x1,y1,z1);
        do if (line_numedit(&n,edit,cursheet->mark1x==-1 ? PRECCELL : PRECBLOCK,&x,&offx)==-1) return; while (n<=0 || n>20);
      }
      else if (c==6)
      {
        if ((n=line_ok(edit,cursheet->mark1x==-1 ? SHADOWCELL : SHADOWCUBE,shadowed(cursheet,x1,y1,z1)))==-1) return;
      }
      else if (c==7)
      {
        do_label(cursheet);
      }
      else if (c==8)
      {
        if ((n=line_ok(edit,cursheet->mark1x==-1 ? LOCKCELL : LOCKBLOCK,locked(cursheet,x1,y1,z1)))==-1) return;
      }    
      if (c!=7) for (x=x1; x<=x2; ++x) for (y=y1; y<=y2; ++y) for (z=z1; z<=z2; ++z) switch (c)
      {
        /*{{{  0       -- adjust left*/
        case 0: setadjust(cursheet,x,y,z,LEFT); break;
        /*}}}  */
        /*{{{  1       -- adjust right*/
        case 1: setadjust(cursheet,x,y,z,RIGHT); break;
        /*}}}  */
        /*{{{  2       -- adjust centered*/
        case 2: setadjust(cursheet,x,y,z,CENTER); break;
        /*}}}  */
        /*{{{  3       -- set scientific notation flag*/
        case 3: setscientific(cursheet,x,y,z,1); break;
        /*}}}  */
        /*{{{  4       -- clear scientific notation flag*/
        case 4: setscientific(cursheet,x,y,z,0); break;
        /*}}}  */
        /*{{{  5       -- set precision*/
        case 5: setprecision(cursheet,x,y,z,n); break;
        /*}}}  */
        /*{{{  6       -- shadow*/
        case 6:
        {
          int rx;

          if (n==0) for (rx=x+1; shadowed(cursheet,rx,y,z); ++rx) shadow(cursheet,rx,y,z,0);
          else if (x>0) shadow(cursheet,x,y,z,1);
          break;
        }
        /*}}}  */
        /*{{{  8       -- lock*/
        case 8: lockcell(cursheet,x,y,z,n); break;
        /*}}}  */
        /*{{{  default -- should not happen*/
        default: assert(0);
        /*}}}  */
      }
      if (c==6 && x1==0 && n==1) line_msg(edit,SHADOWCELL,SHADOWBOO);
    }
  }
  /*{{{  free menu*/
  free((char*)menu[0].str);
  free((char*)menu[1].str);
  free((char*)menu[2].str);
  free((char*)menu[3].str);
  free((char*)menu[4].str);
  free((char*)menu[5].str);
  free((char*)menu[6].str);
  free((char*)menu[7].str);
  free((char*)menu[8].str);
  /*}}}  */
  cursheet->marking=0;
  cursheet->mark1x=-1;
  forceupdate(cursheet);
}
/*}}}  */
/*{{{  do_savexdr*/
static void do_savexdr(Sheet *cursheet)
{
  char buf[_POSIX_PATH_MAX];
  const char *msg;
      
  if (cursheet->name==(char*)0)
  {
    /*{{{  variables*/
    size_t x,offx;
    /*}}}  */
        
    x=0;
    offx=0;
    buf[0]='\0';
    if (line_edit((Sheet*)0,buf,sizeof(buf),edit,SAVEXSHEET,&x,&offx)==-1) return;
    cursheet->name=strcpy(malloc(strlen(buf)+1),buf);
  }
  if ((msg=savexdr(cursheet,cursheet->name))!=(char*)0) line_msg(edit,SAVEXSHEET,msg);
}
/*}}}  */
/*{{{  do_saveport*/
static void do_saveport(Sheet *cursheet)
{
  char buf[_POSIX_PATH_MAX];
  const char *msg;
  size_t x,offx;
        
  if (cursheet->name!=(char*)0) strcpy(buf,cursheet->name); else buf[0]='\0';
  strcat(buf,".txt");
  x=strlen(buf);
  offx=0;
  if (line_edit((Sheet*)0,buf,sizeof(buf),edit,SAVEPSHEET,&x,&offx)==-1) return;
  if ((msg=saveport(cursheet,buf))!=(char*)0) line_msg(edit,SAVEPSHEET,msg);
}
/*}}}  */
/*{{{  do_savetbl*/
static void do_savetbl(Sheet *cursheet)
{
  /*{{{  variables*/
  char buf[_POSIX_PATH_MAX+4];
  char pages[80];
  const char *msg;
  size_t x,offx;
  /*}}}  */
      
  if (cursheet->name==(char*)0) buf[0]='\0';
  else
  {
    strcpy(buf,cursheet->name);
    strcat(buf,".tbl");
  }
  x=0;
  offx=0;
  if (line_edit((Sheet*)0,buf,sizeof(buf),edit,SAVETBLF,&x,&offx)==-1) return;
  if (buf[0]=='\0') return;
  pages[0]='\0';
  x=0;
  offx=0;
  if (line_edit((Sheet*)0,pages,sizeof(pages),edit,PAGES,&x,&offx)==-1) return;  
  if ((msg=savetbl(cursheet,buf,pages))!=(const char*)0) line_msg(edit,SAVETBLF,msg);
}
/*}}}  */
/*{{{  do_savelatex*/
static void do_savelatex(Sheet *cursheet)
{
  /*{{{  variables*/
  char buf[_POSIX_PATH_MAX+4];
  char pages[80];
  const char *msg;
  size_t x,offx;
  /*}}}  */
      
  if (cursheet->name==(char*)0) buf[0]='\0';
  else
  {
    strcpy(buf,cursheet->name);
    strcat(buf,".tex");
  }
  x=0;
  offx=0;
  if (line_edit((Sheet*)0,buf,sizeof(buf),edit,SAVELATEXF,&x,&offx)==-1) return;
  if (buf[0]=='\0') return;
  pages[0]='\0';
  x=0;
  offx=0;
  if (line_edit((Sheet*)0,pages,sizeof(pages),edit,PAGES,&x,&offx)==-1) return;
  if ((msg=savelatex(cursheet,buf,pages))!=(const char*)0) line_msg(edit,SAVELATEXF,msg);
}
/*}}}  */
/*{{{  do_savetext*/
static void do_savetext(Sheet *cursheet)
{
  /*{{{  variables*/
  char buf[_POSIX_PATH_MAX+4];
  char pages[80];
  const char *msg;
  size_t x,offx;
  /*}}}  */
      
  if (cursheet->name==(char*)0) buf[0]='\0';
  else
  {
    strcpy(buf,cursheet->name);
    strcat(buf,".doc");
  }
  x=0;
  offx=0;
  if (line_edit((Sheet*)0,buf,sizeof(buf),edit,SAVETEXTF,&x,&offx)==-1) return;
  if (buf[0]=='\0') return;
  pages[0]='\0';
  x=0;
  offx=0;
  if (line_edit((Sheet*)0,pages,sizeof(pages),edit,PAGES,&x,&offx)==-1) return;  
  if ((msg=savetext(cursheet,buf,pages))!=(char*)0) line_msg(edit,SAVETEXTF,msg);
}
/*}}}  */
/*{{{  do_loadxdr*/
static void do_loadxdr(Sheet *cursheet)
{
  /*{{{  variables*/
  size_t x,offx;
  char buf[_POSIX_PATH_MAX];
  const char *msg;
  /*}}}  */
      
  x=0;
  offx=0;
  buf[0]='\0';
  while (buf[0]=='\0') if (line_edit((Sheet*)0,buf,sizeof(buf),edit,LOADXSHEET,&x,&offx)==-1) return;
  if (cursheet->name!=(char*)0) free(cursheet->name);
  cursheet->name=strcpy(malloc(strlen(buf)+1),buf);
  if ((msg=loadxdr(cursheet,cursheet->name))!=(const char*)0) line_msg(edit,LOADXSHEET,msg);
}
/*}}}  */
/*{{{  do_loadport*/
static void do_loadport(Sheet *cursheet)
{
  /*{{{  variables*/
  size_t x,offx;
  char buf[_POSIX_PATH_MAX];
  const char *msg;
  /*}}}  */
      
  x=0;
  offx=0;
  strcpy(buf,".txt");
  do if (line_edit((Sheet*)0,buf,sizeof(buf),edit,LOADPSHEET,&x,&offx)==-1) return; while (buf[0]=='\0');
  if (cursheet->name!=(char*)0) free(cursheet->name);
  cursheet->name=strcpy(malloc(strlen(buf)+1),buf);
  if ((msg=loadport(cursheet,cursheet->name))!=(const char*)0) line_msg(edit,LOADPSHEET,msg);
  if (strlen(cursheet->name)>(size_t)4 && strcmp(cursheet->name+strlen(cursheet->name)-4,".txt")==0) *(cursheet->name+strlen(cursheet->name)-4)='\0';
}
/*}}}  */
/*{{{  do_mark*/
static void do_mark(Sheet *cursheet)
{
  if (!cursheet->marking && cursheet->mark1x==-1)
  {
    cursheet->mark1x=cursheet->mark2x=cursheet->curx;
    cursheet->mark1y=cursheet->mark2y=cursheet->cury;
    cursheet->mark1z=cursheet->mark2z=cursheet->curz;
    cursheet->marking=1;
  }
  else if (cursheet->marking) cursheet->marking=0;
  else cursheet->mark1x=-1;
}
/*}}}  */
/*{{{  do_save*/
static void do_save(Sheet *cursheet)
{
  /*{{{  variables*/
  MenuChoice menu[6];
  /*}}}  */
  
  menu[0].str=strmalloc(ASXDR);    menu[0].c='\0';
  menu[1].str=strmalloc(ASASCII);  menu[1].c='\0';  
  menu[2].str=strmalloc(TBL);      menu[2].c='\0';
  menu[3].str=strmalloc(LATEX);    menu[3].c='\0';
  menu[4].str=strmalloc(SAVETEXT); menu[4].c='\0';
  menu[5].str=(char*)0;
  switch (line_menu(edit,SAVEMENU,menu,0))
  {
    /*{{{  -1      -- abort*/
    case -1: break;
    /*}}}  */
    /*{{{   0      -- save in XDR*/
    case 0: do_savexdr(cursheet); break;
    /*}}}  */
    /*{{{   1      -- save in ASCII format*/
    case 1: do_saveport(cursheet); break;
    /*}}}  */
    /*{{{   2      -- save in tbl format    */
    case 2: do_savetbl(cursheet); break;
    /*}}}  */
    /*{{{   3      -- save in LaTeX format*/
    case 3: do_savelatex(cursheet); break;
    /*}}}  */
    /*{{{   4      -- save in formatted text format*/
    case 4: do_savetext(cursheet); break;
    /*}}}  */
    /*{{{  default -- should not happen*/
    default: assert(0);
    /*}}}  */
  }
  free((char*)menu[0].str);
  free((char*)menu[1].str);
  free((char*)menu[2].str);
  free((char*)menu[3].str);
  free((char*)menu[4].str);
}
/*}}}  */
/*{{{  do_load*/
static void do_load(Sheet *cursheet)
{
  /*{{{  variables*/
  MenuChoice menu[3];
  /*}}}  */
  
  menu[0].str=strmalloc(ASXDR); menu[0].c='\0';
  menu[1].str=strmalloc(ASASCII); menu[1].c='\0';
  menu[2].str=(char*)0;
  switch (line_menu(edit,LOADMENU,menu,0))
  {
    /*{{{  -1      -- abort*/
    case -1: break;
    /*}}}  */
    /*{{{   0      -- load in XDR*/
    case 0: do_loadxdr(cursheet); break;
    /*}}}  */
    /*{{{   1      -- load in ASCII*/
    case 1: do_loadport(cursheet); break;
    /*}}}  */
    /*{{{  default -- should not happen*/
    default: assert(0);
    /*}}}  */
  }
  free((char*)menu[0].str);
  free((char*)menu[1].str);
}
/*}}}  */
/*{{{  do_clear*/
static void do_clear(Sheet *sheet)
{
  /*{{{  variables*/
  int x,y,z;
  int x1,y1,z1;
  int x2,y2,z2;
  /*}}}  */

  if (sheet->mark1x==-1 && locked(sheet,sheet->curx,sheet->cury,sheet->curz)) line_msg(edit,CLEARCELL,ISLOCKED);
  else
  {
    if (sheet->mark1x!=-1) if (line_ok(edit,CLEARBLOCK,0)!=1) return;
    if (sheet->mark1x==-1)
    /*{{{  range is the current cell*/
    {
      x1=x2=sheet->curx;
      y1=y2=sheet->cury;
      z1=z2=sheet->curz;
    }
    /*}}}  */
    else
    /*{{{  range is the marked cube*/
    {
      x1=sheet->mark1x; x2=sheet->mark2x; order(&x1,&x2);
      y1=sheet->mark1y; y2=sheet->mark2y; order(&y1,&y2);
      z1=sheet->mark1z; z2=sheet->mark2z; order(&z1,&z2);
    }
    /*}}}  */
    for (x=x1; x<=x2; ++x) for (y=y1; y<=y2; ++y) for (z=z1; z<=z2; ++z) freecell(sheet,x,y,z);
    forceupdate(sheet);
    sheet->marking=0;
    sheet->mark1x=-1;
  }
}
/*}}}  */
/*{{{  do_insert*/
static void do_insert(Sheet *sheet)
{
  /*{{{  variables*/
  int x1,y1,z1,x2,y2,z2;
  MenuChoice menu[4];
  /*}}}  */
  
  menu[0].str=strcpy(malloc(strlen(INX)+1),INX); menu[0].c='\0';
  menu[1].str=strcpy(malloc(strlen(INY)+1),INY); menu[1].c='\0';
  menu[2].str=strcpy(malloc(strlen(INZ)+1),INZ); menu[2].c='\0';
  menu[3].str=(char*)0;
  if (sheet->mark1x==-1)
  /*{{{  range is the current cell*/
  {
    x1=x2=sheet->curx;
    y1=y2=sheet->cury;
    z1=z2=sheet->curz;
  }
  /*}}}  */
  else
  /*{{{  range is the marked cube*/
  {
    x1=sheet->mark1x; x2=sheet->mark2x; order(&x1,&x2);
    y1=sheet->mark1y; y2=sheet->mark2y; order(&y1,&y2);
    z1=sheet->mark1z; z2=sheet->mark2z; order(&z1,&z2);
  }
  /*}}}  */
  switch (line_menu(edit,sheet->mark1x==-1 ? INSCELL : INSCUBE,menu,0))
  {
    /*{{{   0      -- columns*/
    case 0: insertcube(sheet,x1,y1,z1,x2,y2,z2,IN_X); break;
    /*}}}  */
    /*{{{   1      -- rows*/
    case 1: insertcube(sheet,x1,y1,z1,x2,y2,z2,IN_Y); break;
    /*}}}  */
    /*{{{   2      -- depth*/
    case 2: insertcube(sheet,x1,y1,z1,x2,y2,z2,IN_Z); break;
    /*}}}  */
    /*{{{  -1      -- abort*/
    case -1: goto greak;
    /*}}}  */
    /*{{{  default -- should not happen*/
    default: assert(0);
    /*}}}  */
  }
  sheet->marking=0;
  sheet->mark1x=-1;
  greak:
  free((char*)menu[0].str);
  free((char*)menu[1].str);
  free((char*)menu[2].str);
}
/*}}}  */
/*{{{  do_delete*/
static void do_delete(Sheet *sheet)
{
  /*{{{  variables*/
  int x1,y1,z1,x2,y2,z2;
  MenuChoice menu[4];
  /*}}}  */
  
  menu[0].str=strcpy(malloc(strlen(INX)+1),INX); menu[0].c='\0';
  menu[1].str=strcpy(malloc(strlen(INY)+1),INY); menu[1].c='\0';
  menu[2].str=strcpy(malloc(strlen(INZ)+1),INZ); menu[2].c='\0';
  menu[3].str=(char*)0;
  if (sheet->mark1x==-1)
  /*{{{  range is the current cell*/
  {
    x1=x2=sheet->curx;
    y1=y2=sheet->cury;
    z1=z2=sheet->curz;
  }
  /*}}}  */
  else
  /*{{{  range is the marked cube*/
  {
    x1=sheet->mark1x; x2=sheet->mark2x; order(&x1,&x2);
    y1=sheet->mark1y; y2=sheet->mark2y; order(&y1,&y2);
    z1=sheet->mark1z; z2=sheet->mark2z; order(&z1,&z2);
  }
  /*}}}  */
  switch(line_menu(edit,sheet->mark1x==-1 ? DELCELL : DELBLOCK,menu,0))
  {
    /*{{{   0      -- columns*/
    case 0: deletecube(sheet,x1,y1,z1,x2,y2,z2,IN_X); break;
    /*}}}  */
    /*{{{   1      -- rows*/
    case 1: deletecube(sheet,x1,y1,z1,x2,y2,z2,IN_Y); break;
    /*}}}  */
    /*{{{   2      -- depth*/
    case 2: deletecube(sheet,x1,y1,z1,x2,y2,z2,IN_Z); break;
    /*}}}  */
    /*{{{  -1      -- abort*/
    case -1: goto greak;
    /*}}}  */
    /*{{{  default -- should not happen*/
    default: assert(0);
    /*}}}  */
  }
  forceupdate(sheet);
  sheet->marking=0;
  sheet->mark1x=-1;
  greak:
  free((char*)menu[0].str);
  free((char*)menu[1].str);
  free((char*)menu[2].str);
}
/*}}}  */
/*{{{  do_about*/
static void do_about(Sheet *sheet)
{
  line_msg(edit,(const char*)0,"Version 0.3, Copyright (c) 1995 by Michael Haardt");
}
/*}}}  */
/*{{{  do_move*/
static void do_move(Sheet *sheet, int copy)
{
  if (sheet->mark1x==-1) line_msg(edit,copy ? COPYBLOCK : MOVEBLOCK,NOMARK);
  else if (line_ok(edit,copy ? COPYBLOCK : MOVEBLOCK,0)==1)
  {
    int x1,y1,z1;
    int x2,y2,z2;

    x1=sheet->mark1x; x2=sheet->mark2x; order(&x1,&x2);
    y1=sheet->mark1y; y2=sheet->mark2y; order(&y1,&y2);
    z1=sheet->mark1z; z2=sheet->mark2z; order(&z1,&z2);
    moveblock(sheet,x1,y1,z1,x2,y2,z2,sheet->curx,sheet->cury,sheet->curz,copy);
    sheet->marking=0;
    if (!copy) sheet->mark1x=-1;
  }
}
/*}}}  */
/*{{{  do_sort*/
static void do_sort(Sheet *sheet)
{
  /*{{{  variables*/
  MenuChoice menu1[4],menu2[3],menu3[3];
  Sortkey sk[MAX_SORTKEYS];
  unsigned int key;
  size_t x,offx;
  const char *msg;
  Direction in_dir;
  int x1,y1,z1,x2,y2,z2;
  int doit;
  /*}}}  */
  
  /*{{{  cry if no block is selected*/
  if (sheet->mark1x==-1)
  {
    line_msg(edit,SORTBLOCK,NOREGION);
    return;
  }
  /*}}}  */
  /*{{{  note and order block coordinates*/
  x1=sheet->mark1x; x2=sheet->mark2x; order(&x1,&x2);
  y1=sheet->mark1y; y2=sheet->mark2y; order(&y1,&y2);
  z1=sheet->mark1z; z2=sheet->mark2z; order(&z1,&z2);
  /*}}}  */
  /*{{{  build menues*/
  menu1[0].str=strmalloc(INX);     menu1[0].c='\0';
  menu1[1].str=strmalloc(INY);     menu1[1].c='\0';
  menu1[2].str=strmalloc(INZ);     menu1[2].c='\0';
  menu1[3].str=(char*)0;
  menu2[0].str=strmalloc(SORTIT);  menu2[0].c='\0';
  menu2[1].str=strmalloc(ADDKEY);  menu2[1].c='\0';
  menu2[2].str=(char*)0;
  menu3[0].str=strmalloc(ASCEND);  menu3[0].c='\0';
  menu3[1].str=strmalloc(DESCEND); menu3[0].c='\0';
  menu3[2].str=(char*)0;
  /*}}}  */
  
  /*{{{  ask for sort direction*/
  switch (line_menu(edit,SORTBLOCK,menu1,0))
  {
    /*{{{   0      -- in X*/
    case 0: in_dir=IN_X; break;
    /*}}}  */
    /*{{{   1      -- in Y*/
    case 1: in_dir=IN_Y; break;
    /*}}}  */
    /*{{{   2      -- in Z*/
    case 2: in_dir=IN_Z; break;
    /*}}}  */
    /*{{{  -1      -- abort*/
    case -1: goto greak;
    /*}}}  */
    /*{{{  default -- should not happen*/
    default: assert(0);
    /*}}}  */
  }
  /*}}}  */
  key=0;
  do
  {
    /*{{{  ask for positions*/
    if (in_dir==IN_X) sk[key].x=0;
    else
    /*{{{  ask for x position*/
    {
      x=0;
      offx=0;
      sk[key].x=0;
      do if (line_numedit(&(sk[key].x),edit,XPOS,&x,&offx)==-1) goto greak; while (sk[key].x<0);
    }
    /*}}}  */
    if (in_dir==IN_Y) sk[key].y=0;
    else
    /*{{{  ask for y position*/
    {
      x=0;
      offx=0;
      sk[key].y=0;
      do if (line_numedit(&(sk[key].y),edit,YPOS,&x,&offx)==-1) goto greak; while (sk[key].y<0);
    }
    /*}}}  */
    if (in_dir==IN_Z) sk[key].z=0;
    else
    /*{{{  ask for z position*/
    {
      x=0;
      offx=0;
      sk[key].z=0;
      do if (line_numedit(&(sk[key].z),edit,ZPOS,&x,&offx)==-1) goto greak; while (sk[key].z<0);
    }
    /*}}}  */
    /*}}}  */
    /*{{{  ask for sort key*/
    sk[key].sortkey=0;
    switch (line_menu(edit,SORTBLOCK,menu3,0))
    {
      /*{{{   0      -- ascending*/
      case 0: sk[key].sortkey|=ASCENDING; break;
      /*}}}  */
      /*{{{   1      -- descending*/
      case 1: sk[key].sortkey&=~ASCENDING; break;
      /*}}}  */
      /*{{{  -1      -- abort*/
      case -1: goto greak;
      /*}}}  */
      /*{{{  default -- should not happen*/
      default: assert(0);
      /*}}}  */
    }
    /*}}}  */
    ++key;
    if (key==MAX_SORTKEYS)
    /*{{{  ask for sort comfirmation*/
    {
      if (line_ok(edit,SORTBLOCK,0)==0) doit=1; else goto greak;
    }
    /*}}}  */
    else
    /*{{{  ask for sort or adding another key*/
    switch (line_menu(edit,SORTBLOCK,menu2,0))
    {
      /*{{{        0 -- sort it*/
      case 0: doit=1; break;
      /*}}}  */
      /*{{{        1 -- add another key*/
      case 1: doit=0; break;
      /*}}}  */
      /*{{{       -1 -- abort*/
      case -1: goto greak;
      /*}}}  */
      /*{{{  default -- should not happen*/
      default: assert(0);
      /*}}}  */
    }
    /*}}}  */
  } while (!doit);
  
  if ((msg=sortblock(sheet,x1,y1,z1,x2,y2,z2,in_dir,sk,key))!=(const char*)0) line_msg(edit,SORTBLOCK,msg);
  greak:
  /*{{{  free menues*/
  free((char*)menu1[0].str);
  free((char*)menu1[1].str);
  free((char*)menu1[2].str);
  free((char*)menu2[0].str);
  free((char*)menu2[1].str);
  free((char*)menu2[2].str);
  free((char*)menu3[0].str);
  free((char*)menu3[1].str);
  free((char*)menu3[2].str);
  /*}}}  */
}
/*}}}  */

/*{{{  leaveanyway*/
static int leaveanyway(Sheet *sheet)
{
  int result;
  
  if (sheet->changed)
  {
    result=line_ok(edit,LEAVE,0);
    if (result==-1) return 0; else return result;
  }
  return 1;
}
/*}}}  */

/*{{{  do_sheetcmd*/
int do_sheetcmd(Sheet *cursheet, chtype c)
{
  switch (c)
  {
    /*{{{  UP*/
    case KEY_UP:
    {
      if (cursheet->cury>0) --cursheet->cury;
      if (cursheet->marking) cursheet->mark2y=cursheet->cury;
      break;
    }
    /*}}}  */
    /*{{{  DOWN*/
    case KEY_DOWN:
    {
      ++cursheet->cury;
      if (cursheet->marking) cursheet->mark2y=cursheet->cury;
      break;
    }
    /*}}}  */
    /*{{{  LEFT*/
    case KEY_LEFT:
    {
      if (cursheet->curx>0) --cursheet->curx;
      if (cursheet->marking) cursheet->mark2x=cursheet->curx;
      break;
    }
    /*}}}  */
    /*{{{  RIGHT*/
    case KEY_RIGHT:
    {
      ++cursheet->curx; while (shadowed(cursheet,cursheet->curx,cursheet->cury,cursheet->curz)) ++cursheet->curx;
      if (cursheet->marking) cursheet->mark2x=cursheet->curx;
      break;
    }
    /*}}}  */
    /*{{{  <*/
    case '<':
    {
      cursheet->cury=0;
      if (cursheet->marking) cursheet->mark2y=cursheet->cury;
      break;
    }
    /*}}}  */
    /*{{{  >*/
    case '>':
    {
      cursheet->cury=(cursheet->dimy ? cursheet->dimy-1 : 0);
      if (cursheet->marking) cursheet->mark2y=cursheet->cury;
      break;
    }
    /*}}}  */
    /*{{{  BEG*/
    case KEY_BEG:
    {
      cursheet->curx=0;
      if (cursheet->marking) cursheet->mark2x=cursheet->curx;
      break;
    }
    /*}}}  */
    /*{{{  END*/
    case KEY_END:
    {
      cursheet->curx=(cursheet->dimx ? cursheet->dimx-1 : 0);
      while (shadowed(cursheet,cursheet->curx,cursheet->cury,cursheet->curz)) ++cursheet->curx;
      if (cursheet->marking) cursheet->mark2x=cursheet->curx;
      break;
    }
    /*}}}  */
    /*{{{  +      */
    case '+':
    {
      ++cursheet->curz; 
      if (cursheet->marking) cursheet->mark2z=cursheet->curz;
      break;
    }
    /*}}}  */
    /*{{{  -    */
    case '-':
    {
      if (cursheet->curz>0) --cursheet->curz; 
      if (cursheet->marking) cursheet->mark2z=cursheet->curz;
      break;
    }
    /*}}}  */
    /*{{{  **/
    case '*':
    {
      cursheet->curz=(cursheet->dimz ? cursheet->dimz-1 : 0);
      if (cursheet->marking) cursheet->mark2z=cursheet->curz;
      break;
    }
    /*}}}  */
    /*{{{  _    */
    case '_':
    {
      cursheet->curz=0;
      if (cursheet->marking) cursheet->mark2z=cursheet->curz;
      break;
    }
    /*}}}  */
    /*{{{  ENTER, TAB -- edit current cell*/
    case '\t':
    case KEY_ENTER: do_edit(cursheet); break;
    /*}}}  */
    /*{{{  .*/
    case KEY_MARK:
    case '.': do_mark(cursheet); break;
    /*}}}  */
    /*{{{  F2         -- save*/
    case KEY_F(2): do_save(cursheet); break;
    /*}}}  */
    /*{{{  F3         -- load*/
    case KEY_F(3): do_load(cursheet); break;
    /*}}}  */
    /*{{{  F10, /     -- main menu*/
    case KEY_F(10):
    case '/':
    {
      /*{{{  variables*/
      MenuChoice menu[13];
      /*}}}  */

      menu[0].str=strmalloc(ATTR);   menu[0].c='\0';
      menu[1].str=strmalloc(CWIDTH); menu[1].c='\0';
      menu[2].str=strmalloc(CLEAR);  menu[2].c='\0';
      menu[3].str=strmalloc(INSERT); menu[3].c='\0';
      menu[4].str=strmalloc(DELETE); menu[4].c='\0';
      menu[5].str=strmalloc(MOVE);   menu[5].c='\0';
      menu[6].str=strmalloc(COPY);   menu[6].c='\0';
      menu[7].str=strmalloc(SORT);   menu[7].c='\0';
      menu[8].str=strmalloc(LOAD);   menu[8].c='\0';
      menu[9].str=strmalloc(SAVE);   menu[9].c='\0';
      menu[10].str=strmalloc(ABOUT); menu[10].c='\0';
      menu[11].str=strmalloc(QUIT);  menu[11].c='\0';
      menu[12].str=(char*)0;
      switch (line_menu(edit,MAINMENU,menu,0))
      {
        case -1: break;
        case 0: do_attribute(cursheet); break;
        case 1: do_columnwidth(cursheet); break;
        case 2: do_clear(cursheet); break;
        case 3: do_insert(cursheet); break;
        case 4: do_delete(cursheet); break;
        case 5: do_move(cursheet,0); break;
        case 6: do_move(cursheet,1); break;
        case 7: do_sort(cursheet); break;
        case 8: do_load(cursheet); break;
        case 9: do_save(cursheet); break;
        case 10: do_about(cursheet); break;
        case 11: return 1; break;
        default: assert(0);
      }
      free((char*)menu[0].str);
      free((char*)menu[1].str);
      free((char*)menu[2].str);
      free((char*)menu[3].str);
      free((char*)menu[4].str);
      free((char*)menu[5].str);
      free((char*)menu[6].str);
      free((char*)menu[7].str);
      free((char*)menu[8].str);
      free((char*)menu[9].str);
      free((char*)menu[10].str);
      free((char*)menu[11].str);
      break;
    }
    /*}}}  */
    /*{{{  !          -- refresh screen*/
    case '!': redraw(); break;
    /*}}}  */
    /*{{{  q*/
    case 'q': return 1;
    /*}}}  */
  }
  return 0;
}
/*}}}  */
                  
/*{{{  main*/
int main(int argc, char *argv[], char *env[])
{
  chtype c;
  Sheet sheet,*cursheet;

  setlocale(LC_ALL,"");
  initscr();
  curs_set(0);
  noecho();
  raw();
  keypad(stdscr,TRUE);
  cursheet=&sheet;
  cursheet->curx=cursheet->cury=cursheet->curz=0;
  cursheet->offx=cursheet->offy=0;
  cursheet->dimx=cursheet->dimy=cursheet->dimz=0;
  cursheet->sheet=(Cell**)0;
  cursheet->column=(int*)0;
  cursheet->window=newwin(LINES-1,COLS,0,0);
  cursheet->name=(char*)0;
  cursheet->mark1x=-1;
  cursheet->marking=0;
  cursheet->changed=0;
  edit=newwin(1,COLS,LINES-1,0);
  keypad(edit,1);
  wmove(edit,0,0); wrefresh(edit); /* does not work else?! */
  line_msg(edit,(const char*)0,TOMENU);
  if (argc>=2)
  {
    const char *msg;

    cursheet->name=strcpy(malloc(strlen(argv[1])+1),argv[1]);
    if ((msg=loadxdr(cursheet,cursheet->name))!=(const char*)0) line_msg(edit,LOADXSHEET,msg);
  }
  do
  {
    redraw_sheet(cursheet);
    c=wgetc(edit);
    wmove(edit,0,0); wclrtoeol(edit); wrefresh(edit);
  } while (do_sheetcmd(cursheet,c)==0 || leaveanyway(cursheet)==0);    
  curs_set(1);
  echo();
  noraw();
  endwin();
  return 0;
}
/*}}}  */
