/* 
 * $Id: x11commn.c,v 1.6 2003/02/16 12:43:37 isizaka Exp isizaka $
 * 
 * This file is part of "Ngraph for X11".
 * 
 * Copyright (C) 2002, Satoshi ISHIZAKA. isizaka@msa.biglobe.ne.jp
 * 
 * "Ngraph for X11" is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * "Ngraph for X11" is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 * 
 */

/**
 *
 * $Log: x11commn.c,v $
 * Revision 1.6  2003/02/16 12:43:37  isizaka
 * for release 6.13.18
 *
 * Revision 1.5  2002/07/06 08:57:25  isizaka
 * change to GPL.
 *
 * Revision 1.4  1999/04/15 12:14:26  isizaka
 * for release 6.03.01
 *
 * Revision 1.3  1999/04/11 06:09:20  isizaka
 * *** empty log message ***
 *
 * Revision 1.2  1999/03/20 12:32:54  isizaka
 * minor change
 *
 * Revision 1.1  1999/03/17 13:28:47  isizaka
 * Initial revision
 *
 *
 **/

#include <Xm/XmAll.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <fcntl.h>

#include "ngraph.h"
#include "object.h"
#include "ioutil.h"
#include "shell.h"
#include "nstring.h"
#include "jstring.h"
#include "odraw.h"
#include "config.h"

#include "x11gui.h"
#include "x11dialg.h"
#include "ox11menu.h"
#include "x11menu.h"
#include "x11commn.h"

#define FALSE 0
#define TRUE 1

void OpenGRA()
{
  int i,id,otherGC;
  char *device,*name;
  struct narray *drawrable;

  if ((menulocal.GRAinst=chkobjinstoid(menulocal.GRAobj,menulocal.GRAoid))!=NULL) return;
  /* search closed gra object */
  if (chkobjinstoid(menulocal.GRAobj,menulocal.GRAoid)==NULL) CloseGRA();
  for (i=chkobjlastinst(menulocal.GRAobj);i>=0;i--) {
    getobj(menulocal.GRAobj,"GC",i,0,NULL,&otherGC);
    if (otherGC<0) break;
  }
  if (i==-1) {
    /* closed gra object is not found. generate new gra object */
    id=newobj(menulocal.GRAobj);
    menulocal.GRAinst=chkobjinst(menulocal.GRAobj,id);
    _getobj(menulocal.GRAobj,"oid",menulocal.GRAinst,&(menulocal.GRAoid));
    /* set page settings */
    putobj(menulocal.GRAobj,"paper_width",id,&(menulocal.PaperWidth));
    putobj(menulocal.GRAobj,"paper_height",id,&(menulocal.PaperHeight));
    putobj(menulocal.GRAobj,"left_margin",id,&(menulocal.LeftMargin));
    putobj(menulocal.GRAobj,"top_margin",id,&(menulocal.TopMargin));
    putobj(menulocal.GRAobj,"zoom",id,&(menulocal.PaperZoom));
    CheckPage();
  } else {
    /* find closed gra object */
    id=i;
    menulocal.GRAinst=chkobjinst(menulocal.GRAobj,id);
    _getobj(menulocal.GRAobj,"oid",menulocal.GRAinst,&(menulocal.GRAoid));
    /* get page settings */
    CheckPage();
  }
  if (arraynum(&(menulocal.drawrable))>0) {
    drawrable=arraynew(sizeof(char *));
    for (i=0;i<arraynum(&(menulocal.drawrable));i++) {
      arrayadd2(drawrable,(char **)arraynget(&(menulocal.drawrable),i));
    }
  } else drawrable=NULL;
  putobj(menulocal.GRAobj,"draw_obj",id,drawrable);
  device=(char *)memalloc(10);
  strcpy(device,"menu:0");
  putobj(menulocal.GRAobj,"device",id,device);
  name=(char *)memalloc(7);
  strcpy(name,"viewer");
  putobj(menulocal.GRAobj,"name",id,name);
  getobj(menulocal.GRAobj,"open",id,0,NULL,&(menulocal.GC));
}

void CheckPage()
{
  _getobj(menulocal.GRAobj,"paper_width",menulocal.GRAinst,
          &(menulocal.PaperWidth));
  _getobj(menulocal.GRAobj,"paper_height",menulocal.GRAinst,
          &(menulocal.PaperHeight));
  _getobj(menulocal.GRAobj,"left_margin",menulocal.GRAinst,
          &(menulocal.LeftMargin));
  _getobj(menulocal.GRAobj,"top_margin",menulocal.GRAinst,
          &(menulocal.TopMargin));
  _getobj(menulocal.GRAobj,"zoom",menulocal.GRAinst,
          &(menulocal.PaperZoom));
}

int ValidGRA()
{
  int id;
  struct objlist *graobj;
  char *inst;

  if ((graobj=chkobject("gra"))==NULL) return -1;
  id=-1;
  if ((inst=chkobjinstoid(graobj,menulocal.GRAoid))!=NULL)
    _getobj(graobj,"id",inst,&id);
  if (id==-1) id=chkobjlastinst(graobj);
  return id;
}

void CloseGRA()
{
  int id;

  if ((menulocal.GRAinst=chkobjinstoid(menulocal.GRAobj,menulocal.GRAoid))==NULL) return;
  if (chkobjinstoid(menulocal.GRAobj,menulocal.GRAoid)!=NULL) {
    _getobj(menulocal.GRAobj,"id",menulocal.GRAinst,&id);
    exeobj(menulocal.GRAobj,"close",id,0,NULL);
    delobj(menulocal.GRAobj,id);
    menulocal.GRAinst=NULL;
    menulocal.GRAoid=-1;
  }
}

void ChangeGRA()
{
  int i,otherGC;

  /* search for closed gra object */
  for (i=chkobjlastinst(menulocal.GRAobj);i>=0;i--) {
    getobj(menulocal.GRAobj,"GC",i,0,NULL,&otherGC);
    if (otherGC<0) break;
  }
  if (i==-1) {
    /* closed gra is not find. maintain present gra object */
    if ((menulocal.GRAinst=chkobjinstoid(menulocal.GRAobj,menulocal.GRAoid))
        ==NULL) {
      ChangePage();
    }
  } else {
    /* use closed gra object */
    ChangePage();
  }
  CheckPage();
}

void SetPageSettingsToGRA()
{
  int i,j,num,otherGC,id;
  struct objlist *obj;
  char *inst;
  struct narray *drawrable;

  if ((obj=chkobject("gra"))==NULL) return;
  for (i=chkobjlastinst(obj);i>=0;i--) {
    getobj(obj,"GC",i,0,NULL,&otherGC);
    if (otherGC<0) break;
  }
  if (i>=0) {
    id=i;
    inst=chkobjinst(obj,id);
    putobj(obj,"paper_width",id,&(menulocal.PaperWidth));
    putobj(obj,"paper_height",id,&(menulocal.PaperHeight));
    putobj(obj,"left_margin",id,&(menulocal.LeftMargin));
    putobj(obj,"top_margin",id,&(menulocal.TopMargin));
    putobj(obj,"zoom",id,&(menulocal.PaperZoom));
    _getobj(obj,"draw_obj",inst,&drawrable);
    arrayfree2(drawrable);
    drawrable=arraynew(sizeof(char *));
    num=arraynum(&(menulocal.drawrable));
    for (j=0;j<num;j++) 
      arrayadd2(drawrable,(char **)arraynget(&(menulocal.drawrable),j));
    _putobj(obj,"draw_obj",inst,&drawrable);
  }
}

void GetPageSettingsFromGRA()
{
  int i,j,num,otherGC,id;
  struct objlist *obj;
  char *inst;
  struct narray *drawrable;

  if ((obj=chkobject("gra"))==NULL) return;
  for (i=chkobjlastinst(obj);i>=0;i--) {
    getobj(obj,"GC",i,0,NULL,&otherGC);
    if (otherGC<0) break;
  }
  if (i>=0) {
    id=i;
    inst=chkobjinst(obj,id);
    CheckPage();
    _getobj(obj,"draw_obj",inst,&drawrable);
    arraydel2(&(menulocal.drawrable));
    num=arraynum(drawrable);
    for (j=0;j<num;j++) {
      arrayadd2(&(menulocal.drawrable),(char **)arraynget(drawrable,j));
    }
  }
  ChangeGRA();
}

void AxisDel2(int id)
{
  struct objlist *obj,*aobj;
  int i,spc,aid1=0,aid2=0;
  char *axis,*axis2;
  struct narray iarray;
  int anum;
  char *inst;
  char *axisx,*axisy;
  int findx,findy;

  if ((obj=getobject("axisgrid"))!=NULL) {
    for (i=chkobjlastinst(obj);i>=0;i--) {
      if ((inst=chkobjinst(obj,i))!=NULL) {
        _getobj(obj,"axis_x",inst,&axisx);
        _getobj(obj,"axis_y",inst,&axisy);
        findx=findy=FALSE;
        if (axisx!=NULL) {
          arrayinit(&iarray,sizeof(int));
          if (!getobjilist(axisx,&aobj,&iarray,FALSE,NULL)) {
            anum=arraynum(&iarray);
            if (anum>0) {
              aid1=*(int *)arraylast(&iarray);
              if (aid1==id) findx=TRUE;
            }
           }
          arraydel(&iarray);
        }
        if (axisy!=NULL) {
          arrayinit(&iarray,sizeof(int));
          if (!getobjilist(axisy,&aobj,&iarray,FALSE,NULL)) {
            anum=arraynum(&iarray);
            if (anum>0) {
              aid1=*(int *)arraylast(&iarray);
              if (aid1==id) findy=TRUE;
            }
          }
          arraydel(&iarray);
        }
        if (findx || findy) delobj(obj,i);
      }
    }
  }
  if ((obj=getobject("file"))==NULL) return;
  for (i=0;i<=chkobjlastinst(obj);i++) {
    if ((getobj(obj,"axis_x",i,0,NULL,&axis)>=0) && (axis!=NULL)) {
      arrayinit(&iarray,sizeof(int));
      if (!getobjilist(axis,&aobj,&iarray,FALSE,&spc)) {
        anum=arraynum(&iarray);
        if ((anum>0) && (spc==1)) {
          aid1=*(int *)arraylast(&iarray);
          if (aid1>id) aid1--;
        } else aid1=-1;
        arraydel(&iarray);
      }
    }
    if ((getobj(obj,"axis_y",i,0,NULL,&axis)>=0) && (axis!=NULL)) {
      arrayinit(&iarray,sizeof(int));
      if (!getobjilist(axis,&aobj,&iarray,FALSE,&spc)) {
        anum=arraynum(&iarray);
        if ((anum>0) && (spc==1)) {
          aid2=*(int *)arraylast(&iarray);
          if (aid2>id) aid2--;
        } else aid2=-1;
        arraydel(&iarray);
      }
    }
    if ((aid1>=0) && (aid2>=0)) {
      if (aid1==aid2) aid2=aid1+1;
    }
    if ((aid1>=0) && (aid2>=0)) {
      if ((axis2=(char *)memalloc(strlen(chkobjectname(aobj))+10))!=NULL) {
        sprintf(axis2,"%s:%d",chkobjectname(aobj),aid1);
        putobj(obj,"axis_x",i,axis2);
      }
      if ((axis2=(char *)memalloc(strlen(chkobjectname(aobj))+10))!=NULL) {
        sprintf(axis2,"%s:%d",chkobjectname(aobj),aid2);
        putobj(obj,"axis_y",i,axis2);
      }
    }
  }
}

void AxisDel(int id)
{
  struct objlist *obj;
  int i,lastinst;
  char *group,*group2;
  char type;
  char *inst,*inst2;
  char group3[20];

  if ((obj=chkobject("axis"))==NULL) return;
  if ((inst=chkobjinst(obj,id))==NULL) return;
  _getobj(obj,"group",inst,&group);
  if ((group==NULL) || (group[0]=='a')) {
    AxisDel2(id);
    delobj(obj,id);
    return;
  }
  lastinst=chkobjlastinst(obj);
  type=group[0];
  strcpy(group3,group);
  for (i=lastinst;i>=0;i--) {
    inst2=chkobjinst(obj,i);
    _getobj(obj,"group",inst2,&group2);
    if ((group2!=NULL) && (group2[0]==type)) {
      if (strcmp(group3+2,group2+2)==0) {
        AxisDel2(i);
        delobj(obj,i);
      }
    }
  }
}

void AxisMove(int id1,int id2)
{
  struct objlist *obj,*aobj;
  int i,spc,aid;
  char *axis,*axis2;
  struct narray iarray;
  int anum;

  if ((obj=getobject("file"))==NULL) return;
  for (i=0;i<=chkobjlastinst(obj);i++) {
    if ((getobj(obj,"axis_x",i,0,NULL,&axis)>=0) && (axis!=NULL)) {
      arrayinit(&iarray,sizeof(int));
      if (!getobjilist(axis,&aobj,&iarray,FALSE,&spc)) {
        anum=arraynum(&iarray);
        if ((anum>0) && (spc==1)) {
          aid=*(int *)arraylast(&iarray);
          if (aid==id1) aid=id2;
          else {
            if (aid>id1) aid--;
            if (aid>=id2) aid++;
          }
          if ((axis2=(char *)memalloc(strlen(chkobjectname(aobj))+10))!=NULL) {
            sprintf(axis2,"%s:%d",chkobjectname(aobj),aid);
            putobj(obj,"axis_x",i,axis2);
          }
        }
        arraydel(&iarray);
      }
    }
    if ((getobj(obj,"axis_y",i,0,NULL,&axis)>=0) && (axis!=NULL)) {
      arrayinit(&iarray,sizeof(int));
      if (!getobjilist(axis,&aobj,&iarray,FALSE,&spc)) {
        anum=arraynum(&iarray);
        if ((anum>0) && (spc==1)) {
          aid=*(int *)arraylast(&iarray);
          if (aid==id1) aid=id2;
          else {
            if (aid>id1) aid--;
            if (aid>=id2) aid++;
          }
          if ((axis2=(char *)memalloc(strlen(chkobjectname(aobj))+10))!=NULL) {
            sprintf(axis2,"%s:%d",chkobjectname(aobj),aid);
            putobj(obj,"axis_y",i,axis2);
          }
        }
        arraydel(&iarray);
      }
    }
  }
}

void AxisNameToGroup()
{
  int idx,idy,idu,idr;
  int findX,findY,findU,findR,graphtype;
  int id,id2,num;
  struct objlist *obj;
  char *name,*name2,*gp;
  struct narray group,iarray;
  int a,j,anum;
  char *argv[2];
  int *data;
  char *inst;


  if ((obj=(struct objlist *)chkobject("axis"))==NULL) return;
  num=chkobjlastinst(obj);
  arrayinit(&iarray,sizeof(int));
  for (id=0;id<=num;id++) {
    anum=arraynum(&iarray);
    data=(int *)arraydata(&iarray);
    for (j=0;j<anum;j++) if (data[j]==id) break;
    inst=chkobjinst(obj,id);
    _getobj(obj,"name",inst,&name);
    _getobj(obj,"group",inst,&gp);
    if ((j==anum) && (gp[0]=='a')) {
      findX=findY=findU=findR=FALSE;
      graphtype=-1;
      if (name!=NULL) {
        if (strncmp(name,"sectionX",8)==0) {
          graphtype=0;
          findX=TRUE;
          idx=id;
        } else if (strncmp(name,"sectionY",8)==0) {
          graphtype=0;
          findY=TRUE;
          idy=id;
        } else if (strncmp(name,"sectionU",8)==0) {
          graphtype=0;
          findU=TRUE;
          idu=id;
        } else if (strncmp(name,"sectionR",8)==0) {
          graphtype=0;
          findR=TRUE;
          idr=id;
        } else if (strncmp(name,"frameX",6)==0) {
          graphtype=1;
          findX=TRUE;
          idx=id;
        } else if (strncmp(name,"frameY",6)==0) {
          graphtype=1;
          findY=TRUE;
          idy=id;
        } else if (strncmp(name,"frameU",6)==0) {
          graphtype=1;
          findU=TRUE;
          idu=id;
        } else if (strncmp(name,"frameR",6)==0) {
          graphtype=1;
          findR=TRUE;
          idr=id;
        } else if (strncmp(name,"crossX",6)==0) {
          graphtype=2;
          findX=TRUE;
          idx=id;
        } else if (strncmp(name,"crossY",6)==0) {
          graphtype=2;
          findY=TRUE;
          idy=id;
        }
      }
      for (id2=id+1;id2<=num;id2++) {
        for (j=0;j<anum;j++) if (data[j]==id2) break;
        inst=chkobjinst(obj,id2);
        _getobj(obj,"name",inst,&name2);
        _getobj(obj,"group",inst,&gp);
        if ((j==anum) && (gp[0]=='a')) {
          if (graphtype==0) {
            if (name2!=NULL) {
              if ((strncmp(name2,"sectionX",8)==0)
              && (strcmp(name+8,name2+8)==0)) {
                findX=TRUE;
                idx=id2;
              } else if ((strncmp(name2,"sectionY",8)==0)
              && (strcmp(name+8,name2+8)==0)) {
                findY=TRUE;
                idy=id2;
              } else if ((strncmp(name2,"sectionU",8)==0)
              && (strcmp(name+8,name2+8)==0)) {
                findU=TRUE;
                idu=id2;
              } else if ((strncmp(name2,"sectionR",8)==0)
              && (strcmp(name+8,name2+8)==0)) {
                findR=TRUE;
                idr=id2;
              }
            }
          } else if (graphtype==1) {
            if (name2!=NULL) {
              if ((strncmp(name2,"frameX",6)==0)
              && (strcmp(name+6,name2+6)==0)) {
                findX=TRUE;
                idx=id2;
              } else if ((strncmp(name2,"frameY",6)==0)
              && (strcmp(name+6,name2+6)==0)) {
                findY=TRUE;
                idy=id2;
              } else if ((strncmp(name2,"frameU",6)==0)
              && (strcmp(name+6,name2+6)==0)) {
                findU=TRUE;
                idu=id2;
              } else if ((strncmp(name2,"frameR",6)==0) 
              && (strcmp(name+6,name2+6)==0)) {
                findR=TRUE;
                idr=id2;
              }
            }
          } else if (graphtype==2) {
            if (name2!=NULL) {
              if ((strncmp(name2,"crossX",6)==0)
              && (strcmp(name+6,name2+6)==0)) {
                findX=TRUE;
                idx=id2;
              } else if ((strncmp(name2,"crossY",6)==0)
              && (strcmp(name+6,name2+6)==0)) {
                findY=TRUE;
                idy=id2;
              }
            }
          }
        }
      }
      if ((graphtype==0) && findX && findY && findU && findR) {
        arrayinit(&group,sizeof(int));
        a=2;
        arrayadd(&group,&a);
        arrayadd(&group,&idx);
        arrayadd(&group,&idy);
        arrayadd(&group,&idu);
        arrayadd(&group,&idr);
        argv[0]=(char *)&group;
        argv[1]=NULL;
        exeobj(obj,"grouping",id,1,argv);
        arraydel(&group);
        arrayadd(&iarray,&idx);
        arrayadd(&iarray,&idy);
        arrayadd(&iarray,&idu);
        arrayadd(&iarray,&idr);
      } else if ((graphtype==1) && findX && findY && findU && findR) {
        arrayinit(&group,sizeof(int));
        a=1;
        arrayadd(&group,&a);
        arrayadd(&group,&idx);
        arrayadd(&group,&idy);
        arrayadd(&group,&idu);
        arrayadd(&group,&idr);
        argv[0]=(char *)&group;
        argv[1]=NULL;
        exeobj(obj,"grouping",id,1,argv);
        arraydel(&group);
        arrayadd(&iarray,&idx);
        arrayadd(&iarray,&idy);
        arrayadd(&iarray,&idu);
        arrayadd(&iarray,&idr);
      } else if ((graphtype==2) && findX && findY) {
        arrayinit(&group,sizeof(int));
        a=3;
        arrayadd(&group,&a);
        arrayadd(&group,&idx);
        arrayadd(&group,&idy);
        argv[0]=(char *)&group;
        argv[1]=NULL;
        exeobj(obj,"grouping",id,1,argv);
        arraydel(&group);
        arrayadd(&iarray,&idx);
        arrayadd(&iarray,&idy);
      }
    }
  }
  arraydel(&iarray);
}

void FitDel(struct objlist *obj,int id)
{
  char *fit;
  struct objlist *fitobj;
  int fitid,idnum;
  struct narray iarray;

  if ((getobj(obj,"fit",id,0,NULL,&fit)>=0) && (fit!=NULL)) {
    arrayinit(&iarray,sizeof(int));
    if (getobjilist(fit,&fitobj,&iarray,FALSE,NULL)==0) {
      idnum=arraynum(&iarray);
      if (idnum>=1) {
        fitid=*(int *)arraylast(&iarray);
        delobj(fitobj,fitid);
      }
    }
    arraydel(&iarray);
  }
}

void FitCopy(struct objlist *obj,int did,int sid)
{
  char *fit;
  struct objlist *fitobj;
  int fitid;
  struct narray iarray;
  struct narray iarray2;
  int j,id2,perm,type,idnum,idnum2;
  char *field;
  int fitoid;
  char *inst;

  if ((getobj(obj,"fit",sid,0,NULL,&fit)>=0) && (fit!=NULL)) {
    arrayinit(&iarray,sizeof(int));
    if (getobjilist(fit,&fitobj,&iarray,FALSE,NULL)==0) {
      idnum=arraynum(&iarray);
      if (idnum>=1) {
        fitid=*(int *)arraylast(&iarray);
        if ((getobj(obj,"fit",did,0,NULL,&fit)>=0) && (fit!=NULL)) {
          arrayinit(&iarray2,sizeof(int));
          if (getobjilist(fit,&fitobj,&iarray2,FALSE,NULL)==0) {
            idnum2=arraynum(&iarray2);
            if (idnum2>=1) {
              id2=*(int *)arraylast(&iarray2);
            } else id2=newobj(fitobj);
          } else id2=newobj(fitobj);
          arraydel(&iarray2);
        } else id2=newobj(fitobj);
        if (id2>=0) {
          for (j=0;j<chkobjfieldnum(fitobj);j++) {
            field=chkobjfieldname(fitobj,j);
            perm=chkobjperm(fitobj,field);
            type=chkobjfieldtype(fitobj,field);
            if ((strcmp2(field,"name")!=0) && (strcmp2(field,"equation")!=0)
              && ((perm&NREAD)!=0) && ((perm&NWRITE)!=0) && (type<NVFUNC))
              copyobj(fitobj,field,id2,fitid);
          }
          inst=getobjinst(fitobj,id2);
          _getobj(fitobj,"oid",inst,&fitoid);
          if ((fit=mkobjlist(fitobj,NULL,fitoid,NULL,TRUE))!=NULL) {
            if (putobj(obj,"fit",did,fit)==-1) memfree(fit);
          }
        }
      }
    }
    arraydel(&iarray);
  }
}

void FitClear()
{
  struct objlist *obj,*fitobj;
  int i,anum,id,hidden;
  char *fit;
  struct narray iarray;

  if (menulock || globallock) return;
  if ((obj=chkobject("file"))==NULL) return;
  if ((fitobj=chkobject("fit"))==NULL) return;
  for (i=0;i<=chkobjlastinst(obj);i++) {
    getobj(obj,"fit",i,0,NULL,&fit);
    if (fit!=NULL) {
      arrayinit(&iarray,sizeof(int));
      if (getobjilist(fit,&fitobj,&iarray,FALSE,NULL)==0) {
        anum=arraynum(&iarray);
        if (anum>=1) {
          id=*(int *)arraylast(&iarray);
          getobj(obj,"hidden",i,0,NULL,&hidden);
          if (!hidden) putobj(fitobj,"equation",id,NULL);
        }
      }
      arraydel(&iarray);
    }
  }
}

void DeleteDrawable()
{
  struct objlist *fileobj,*drawobj;
  int i;

  if ((fileobj=chkobject("file"))!=NULL) {
    for (i=0;i<=chkobjlastinst(fileobj);i++) FitDel(fileobj,i);
  }
  if ((drawobj=chkobject("draw"))!=NULL) delchildobj(drawobj);
}

void SaveParent(HANDLE hFile,struct objlist *parent,int storedata,
                int storemerge)
{
  struct objlist *ocur;
  int i,j,instnum;
  char *s;
  char *inst,*fname,*fname2;

  ocur=chkobjroot();
  while (ocur!=NULL) {
    if (chkobjparent(ocur)==parent) {
      if ((instnum=chkobjlastinst(ocur))!=-1) {
        for (i=0;i<=instnum;i++) {
          getobj(ocur,"save",i,0,NULL,&s);
          nwrite(hFile,s,strlen(s));
          nwrite(hFile,"\n",1);
          if (storedata && (ocur==chkobject("file"))) {
            getobj(ocur,"file",i,0,NULL,&fname);
            if (fname!=NULL) {
              for (j=i-1;j>=0;j--) {
                getobj(ocur,"file",j,0,NULL,&fname2);
                if ((fname2!=NULL) && (strcmp0(fname,fname2)==0)) break;
              }
              inst=chkobjinst(ocur,i);
              if (j==-1) {
                while (_exeobj(ocur,"store_data",inst,0,NULL)==0) {
                  _getobj(ocur,"store_data",inst,&s);
                  nwrite(hFile,s,strlen(s));
                  nwrite(hFile,"\n",1);
                }
              } else  {
                while (_exeobj(ocur,"store_dummy",inst,0,NULL)==0) {
                  _getobj(ocur,"store_dummy",inst,&s);
                  nwrite(hFile,s,strlen(s));
                  nwrite(hFile,"\n",1);
                }
              }
            }
          }
          if (storemerge && (ocur==chkobject("merge"))) {
            getobj(ocur,"file",i,0,NULL,&fname);
            if (fname!=NULL) {
              for (j=i-1;j>=0;j--) {
                getobj(ocur,"file",j,0,NULL,&fname2);
                if ((fname2!=NULL) && (strcmp0(fname,fname2)==0)) break;
              }
              inst=chkobjinst(ocur,i);
              if (j==-1) {
                while (_exeobj(ocur,"store_data",inst,0,NULL)==0) {
                  _getobj(ocur,"store_data",inst,&s);
                  nwrite(hFile,s,strlen(s));
                  nwrite(hFile,"\n",1);
                }
              } else {
                while (_exeobj(ocur,"store_dummy",inst,0,NULL)==0) {
                  _getobj(ocur,"store_dummy",inst,&s);
                  nwrite(hFile,s,strlen(s));
                  nwrite(hFile,"\n",1);
                }
              }
            }
          }
        }
      }
      SaveParent(hFile,ocur,storedata,storemerge);
    }
    ocur=ocur->next;
  }
}

int SaveDrawrable(char *name,int storedata,int storemerge)
{
  HANDLE hFile;
  struct objlist *sysobj,*drawobj,*graobj;
  int id,len,error;
  char *arg[2];
  struct narray sarray;
  char *inst,*ver,*sysname,*s,*opt,*comment;

  error=FALSE;
  hFile=nopen(name,O_CREAT|O_TRUNC|O_RDWR,NFMODE);
  sysobj=chkobject("system");
  inst=chkobjinst(sysobj,0);
  _getobj(sysobj,"name",inst,&sysname);
  _getobj(sysobj,"version",inst,&ver);
  if ((comment=(char *)memalloc(256))!=NULL) {
    len=sprintf(comment,"#!ngraph\n#%%creator: %s %s \n#%%version: %s\n",
                sysname,PLATFORM,ver);
    if (nwrite(hFile,comment,len)!=len) error=TRUE;
    memfree(comment);
  }
  if ((drawobj=chkobject("draw"))!=NULL) {
    SaveParent(hFile,drawobj,storedata,storemerge);
    if ((graobj=chkobject("gra"))!=NULL) {
      id=ValidGRA();
      if (id!=-1) {
        arrayinit(&sarray,sizeof(char *));
        opt="device";
        arrayadd(&sarray,&opt);
        arg[0]=(char *)&sarray;
        arg[1]=NULL;
        getobj(graobj,"save",id,1,arg,&s);
        arraydel(&sarray);
        len=strlen(s);
        if (nwrite(hFile,s,strlen(s))!=len) error=TRUE;
      } else error=TRUE;
    }
  }
  nclose(hFile);
  if (error) MessageBox(TopLevel,"I/O error: Write",NULL,MB_OK);
  return !error;
}

int GraphSave(int overwrite)
{
  char mes[256],*buf;
  int i,path,error;
  struct objlist *obj;
  int sdata,smerge;
  int ret;
  char *ext;
  char *initfil;
  char *file;

  error=FALSE;
  if (NgraphApp.FileName!=NULL) {
    initfil=NgraphApp.FileName;
    if ((ext=getextention(initfil))!=NULL) {
      if ((strcmp0(ext,"PRM")==0) || (strcmp0(ext,"prm")==0))
        strcpy(ext,"ngp");
    }
  } else initfil=NULL;
  if ((initfil==NULL) || (!overwrite || (access(initfil,04)==-1))) {
    ret=nGetSaveFileName(TopLevel,"Save NGP file","ngp",
                   &(menulocal.graphloaddir),initfil,
                   &file,"*.ngp",menulocal.changedirectory);
  } else {
    if ((file=malloc(strlen(initfil)+1))==NULL) return IDCANCEL;
    strcpy(file,initfil);
    ret=IDOK;
  }
  if (ret==IDOK) {
    SaveDialog(&DlgSave,&sdata,&smerge);
    if ((ret=DialogExecute(TopLevel,&DlgSave))==IDOK) { 
      path=DlgSave.Path;
      if ((obj=chkobject("file"))!=NULL) {
        for (i=0;i<=chkobjlastinst(obj);i++)
          putobj(obj,"save_path",i,&path);
      }
      if ((obj=chkobject("merge"))!=NULL) {
        for (i=0;i<=chkobjlastinst(obj);i++)
          putobj(obj,"save_path",i,&path);
      }
      if ((!overwrite) && (access(file,04)==0)) {
        buf=(char *)memalloc(strlen(file)+60);
        if (buf!=NULL) {
          sprintf(buf,"`%s'\n\nOverwrite existing file?",file);
          if (MessageBox(TopLevel,buf,"Save",MB_YESNO)!=IDYES) {
            memfree(buf);
            free(file);
            return IDCANCEL;
          }
          memfree(buf);
        } else {
          if (MessageBox(TopLevel,"Overwrite existing file?",
          "Save",MB_YESNO)!=IDYES)
           free(file);
           return IDCANCEL;
        }
      }
      sprintf(mes,"Saving `%.128s'.",file);
      SetStatusBar(mes); 
      error=!SaveDrawrable(file,sdata,smerge);
      changefilename(file);
      AddNgpFileList(file);
      ResetStatusBar();
      SetFileName(file);
      SetCaption(file);
      NgraphApp.Changed=FALSE;
    }
  }
  free(file);
  if (error) ret=IDCANCEL;
  return ret;
}

void LoadNgpFile(char *File,int ignorepath,int expand,char *exdir,
                 int console,char *option)
{
  struct objlist *sys;
  char *expanddir;
  struct objlist *obj,*aobj;
  char *name;
  int i,newid,allocnow=FALSE;
  char *s;
  int len;
  char *argv[2];
  struct narray sarray;
  char mes[256];
  int sec;
  char *inst;

  if ((sys=chkobject("system"))==NULL) return;
  if ((expanddir=(char *)memalloc(strlen(exdir)+1))==NULL) return;
  strcpy(expanddir,exdir);
  putobj(sys,"expand_dir",0,expanddir);
  putobj(sys,"expand_file",0,&expand);
  putobj(sys,"ignore_path",0,&ignorepath);
  ignorepath=FALSE;
  if ((obj=chkobject("shell"))==NULL) return;
  newid=newobj(obj);
  if (newid>=0) {
    inst=chkobjinst(obj,newid);
    arrayinit(&sarray,sizeof(char *));
    while ((s=getitok2(&option,&len," \t"))!=NULL) {
      if (arrayadd(&sarray,&s)==NULL) {
        memfree(s);
        arraydel2(&sarray);
        putobj(sys,"ignore_path",0,&ignorepath);
        return;
      }
    }
    if ((name=(char *)memalloc(strlen(File)+1))==NULL) {
      arraydel2(&sarray);
      putobj(sys,"ignore_path",0,&ignorepath);
      return;
    }
    SetFileName(File);
    SetCaption(File);
    strcpy(name,File);
    changefilename(name);
    if (arrayadd(&sarray,&name)==NULL) {
      memfree(name);
      arraydel2(&sarray);
      putobj(sys,"ignore_path",0,&ignorepath);
      return;
    }
    DeleteDrawable();
    if (console) allocnow=AllocConsole();
    sec=TRUE;
    argv[0]=(char *)&sec;
    argv[1]=NULL;
    _exeobj(obj,"security",inst,1,argv);
    argv[0]=(char *)&sarray;
    argv[1]=NULL;
    AddNgpFileList(name);
    sprintf(mes,"Loading `%.128s'.",name);
    SetStatusBar(mes);
    _exeobj(obj,"shell",inst,1,argv);
    sec=FALSE;
    argv[0]=(char *)&sec;
    argv[1]=NULL;
    _exeobj(obj,"security",inst,1,argv);
    if ((aobj=getobject("axis"))!=NULL) {
      for (i=0;i<=chkobjlastinst(aobj);i++) exeobj(aobj,"tight",i,0,NULL);
    }
    if ((aobj=getobject("axisgrid"))!=NULL) {
      for (i=0;i<=chkobjlastinst(aobj);i++) exeobj(aobj,"tight",i,0,NULL);
    }
    AxisNameToGroup();
    ResetStatusBar();
    arraydel2(&sarray);
    if (console) FreeConsole(allocnow);
    GetPageSettingsFromGRA(); 
    UpdateAll();
    delobj(obj,newid);
  }
  putobj(sys,"ignore_path",0,&ignorepath);
}

void LoadPrmFile(char *File)
{
  struct objlist *obj;
  char *name;
  int id;
  char mes[256];

  if ((obj=chkobject("prm"))==NULL) return;
  if ((id=newobj(obj))>=0) {
    if ((name=(char *)memalloc(strlen(File)+1))==NULL) {
      delobj(obj,id);
      return;
    }
    strcpy(name,File);
    SetFileName(name);
    changefilename(name);
    putobj(obj,"file",id,name);
    PrmDialog(&DlgPrm,obj,id);
    if (DialogExecute(TopLevel,&DlgPrm)==IDOK) {
      sprintf(mes,"Loading `%.128s'.",name);
      SetStatusBar(mes);
      DeleteDrawable();
      exeobj(obj,"load",id,0,NULL);
      GetPageSettingsFromGRA();
      UpdateAll();
      ResetStatusBar();
      SetCaption(File);
    }
    delobj(obj,id);
  }
}

void FileAutoScale()
{
  int anum,room;
  struct objlist *aobj,*aobj2;
  double min,max,inc;
  char *argv2[3];
  char *buf;
  struct objlist *fobj;
  int lastinst;
  int i,j,a;
  char *ref;
  struct narray iarray;
  int anum2,aid2;
  char *inst,*group,*refgroup;
  int refother;

  if ((fobj=chkobject("file"))==NULL) return;
  lastinst=chkobjlastinst(fobj);
  aobj=chkobject("axis");
  anum=chkobjlastinst(aobj);
  if ((lastinst>=0) && (aobj!=NULL) && (anum!=0)) {
    if ((buf=(char *)memalloc(6*(lastinst+1)+6))!=NULL) {
      j=0;
      j+=sprintf(buf+j,"file:");
      for (i=0;i<=lastinst;i++) {
        getobj(fobj,"hidden",i,0,NULL,&a);
        if (!a) j+=sprintf(buf+j,"%d,",i);
      }
      if (buf[j]==',') buf[j]='\0';
      room=0;
      argv2[0]=(char *)buf;
      argv2[1]=(char *)&room;
      argv2[2]=NULL;
      for (i=0;i<=anum;i++) {
        getobj(aobj,"min",i,0,NULL,&min);
        getobj(aobj,"max",i,0,NULL,&max);
        getobj(aobj,"inc",i,0,NULL,&inc);
        getobj(aobj,"group",i,0,NULL,&group);
        getobj(aobj,"reference",i,0,NULL,&ref);
        refother=FALSE;
        if (ref!=NULL) {
          refother=TRUE;
          arrayinit(&iarray,sizeof(int));
          if (!getobjilist(ref,&aobj2,&iarray,FALSE,NULL)) {
            anum2=arraynum(&iarray);
            if (anum2>0) {
              aid2=*(int *)arraylast(&iarray);
              arraydel(&iarray);
              if ((anum2>0) && ((inst=getobjinst(aobj2,aid2))!=NULL)) {
                _getobj(aobj2,"group",inst,&refgroup);
                if ((refgroup!=NULL) && (group!=NULL)
                && (refgroup[0]==group[0])
                && (strcmp(refgroup+2,group+2)==0)) refother=FALSE;
              }
            }
          }
        }
        if ((!refother) && ((min==max) || (inc==0)))
          exeobj(aobj,"auto_scale",i,2,argv2);
      }
      memfree(buf);
    }
  }
}

void AdjustAxis()
{
  struct objlist *aobj;
  int i,anum;

  if ((aobj=chkobject("axis"))==NULL) return;
  anum=chkobjlastinst(aobj);
  for (i=0;i<=anum;i++) exeobj(aobj,"adjust",i,0,NULL);
}

int CheckSave()
{
  int ret;

  if (NgraphApp.Changed) {
    ret=MessageBox(TopLevel,"This graph is modified.\nSave this graph?",
                   "Modified",MB_YESNOCANCEL);
    if (ret==IDYES) {
      if (GraphSave(TRUE)==IDCANCEL) return FALSE;
    } else if (ret!=IDNO) return FALSE;
  }
  return TRUE;
}

void AddMathList(char *math)
{
  int i,j,num;
  char **data;
  char *s;
  struct narray *mathlist;

  if ((math==NULL) || (math[0]=='\0')) return;
  mathlist=menulocal.mathlist;
  num=arraynum(mathlist);
  data=(char **)arraydata(mathlist);
  for (i=0;i<num;i++)
    if (strcmp0(data[i],math)==0) break;
  if (i==num) {
    if (num>=10) arrayndel2(mathlist,num-1);
    else num++;
    arrayins2(mathlist,&math,0);
  } else {
    s=data[i];
    for (j=i-1;j>=0;j--) data[j+1]=data[j];
    data[0]=s;
  }
}

void AddNgpFileList(char *file)
{
  int i,j,num,num2;
  char **data,**data2;
  char *s,*cwd;
  struct narray *ngpfilelist,*ngpdirlist;
  XmString xs;

  if ((file==NULL) || (file[0]=='\0')) return;
  ngpfilelist=menulocal.ngpfilelist;
  ngpdirlist=menulocal.ngpdirlist;
  num=arraynum(ngpfilelist);
  num2=arraynum(ngpdirlist);
  if (num!=num2) return;
  data=(char **)arraydata(ngpfilelist);
  data2=(char **)arraydata(ngpdirlist);
  for (i=0;i<num;i++)
    if (strcmp0(data[i],file)==0) break;
  if (i==num) {
    if (num>=10) {
      arrayndel2(ngpfilelist,num-1);
      arrayndel2(ngpdirlist,num-1);
    }
    arrayins2(ngpfilelist,&file,0);
    cwd=ngetcwd();
    arrayins(ngpdirlist,&cwd,0);
  } else {
    s=data[i];
    for (j=i-1;j>=0;j--) data[j+1]=data[j];
    data[0]=s;
    s=data2[i];
    for (j=i-1;j>=0;j--) data2[j+1]=data2[j];
    cwd=ngetcwd();
    memfree(s);
    data2[0]=cwd;
  }
  num=arraynum(ngpfilelist);
  data=(char **)arraydata(ngpfilelist);
  for (i=0;i<10;i++) {
    XtUnmanageChild(NgraphApp.ghistory[i]);
    if (i<num) {
      xs=XmStringCreate(data[i],XmFONTLIST_DEFAULT_TAG);
      XtVaSetValues(NgraphApp.ghistory[i],XmNlabelString,xs,NULL);
      XtManageChild(NgraphApp.ghistory[i]);
      XmStringFree(xs);
    }
  }
}

void AddDataFileList(char *file)
{
  int i,j,num;
  char **data;
  char *s;
  struct narray *datafilelist;
  XmString xs;

  if ((file==NULL) || (file[0]=='\0')) return;
  datafilelist=menulocal.datafilelist;
  num=arraynum(datafilelist);
  data=(char **)arraydata(datafilelist);
  for (i=0;i<num;i++)
    if (strcmp0(data[i],file)==0) break;
  if (i==num) {
    if (num>=10) arrayndel2(datafilelist,num-1);
    arrayins2(datafilelist,&file,0);
  } else {
    s=data[i];
    for (j=i-1;j>=0;j--) data[j+1]=data[j];
    data[0]=s;
  }
  num=arraynum(datafilelist);
  data=(char **)arraydata(datafilelist);
  for (i=0;i<10;i++) {
    XtUnmanageChild(NgraphApp.fhistory[i]);
    if (i<num) {
      xs=XmStringCreate(data[i],XmFONTLIST_DEFAULT_TAG);
      XtVaSetValues(NgraphApp.fhistory[i],XmNlabelString,xs,NULL);
      XtManageChild(NgraphApp.fhistory[i]);
      XmStringFree(xs);
    }
  }
}

void SetFileName(char *name)
{
  char *ngp;

  memfree(NgraphApp.FileName);
  if (name==NULL) {
    NgraphApp.FileName=NULL;
    ngp=NULL;
  } else {
    NgraphApp.FileName=getfullpath(name);
    ngp=getfullpath(name);
  }
  putobj(menulocal.obj,"fullpath_ngp",0,ngp);
}

int nallocconsole();
void nfreeconsole();
void nforegroundconsole();

int AllocConsole()
{
  int allocnow;

  loadstdio(&x11iosave);
  allocnow=nallocconsole();
  nforegroundconsole();
  return allocnow;
}

void FreeConsole(int allocnow)
{
  if (allocnow) nfreeconsole();
  putstderr=mx11putstderr;
  printfstderr=mx11printfstderr;
  inputyn=mx11inputyn;
  ndisplaydialog=mx11displaydialog;
  ndisplaystatus=mx11displaystatus;
}

char *FileCB(struct objlist *obj,int id)
{
  char *valstr,*file,*s;

  if ((s=(char *)memalloc(128))==NULL) return NULL;
  getobj(obj,"file",id,0,NULL,&file);
  valstr=getbasename(file);
  if (valstr!=NULL) {
    sprintf(s,"%-5d %.100s",id,valstr);
    memfree(valstr);
  } else sprintf(s,"%-5d %.100s",id,"....................");
  return s;
}

int SetFileHidden()
{
  struct objlist *fobj;
  int lastinst;
  struct narray farray,ifarray;
  int i,a,num,*array;

  if ((fobj=chkobject("file"))==NULL) return 1;
  lastinst=chkobjlastinst(fobj);
  if (lastinst>=0) {
    arrayinit(&ifarray,sizeof(int));
    for (i=0;i<=lastinst;i++) {
      getobj(fobj,"hidden",i,0,NULL,&a);
      if (!a) arrayadd(&ifarray,&i);
    }
    SelectDialog(&DlgSelect,fobj,FileCB,(struct narray *)&farray,
                                        (struct narray *)&ifarray);
    if (DialogExecute(TopLevel,&DlgSelect)==IDOK) {
      a=TRUE;
      for (i=0;i<=lastinst;i++) putobj(fobj,"hidden",i,&a);
      num=arraynum(&farray);
      array=(int *)arraydata(&farray);
      a=FALSE;
      for (i=0;i<num;i++) putobj(fobj,"hidden",array[i],&a);
    } else {
      arraydel(&farray);
      return 0;
    }
    arraydel(&ifarray);
    arraydel(&farray);
  }
  return 1;
}

int CheckIniFile()
{
  int ret;

  ret=writecheckconfig();
  if (ret==0) {
    MessageBox(TopLevel,"Ngraph.ini is not found.",
               "Ngraph.ini",MB_OK);
    return FALSE;
  } else if ((ret==-1) || (ret==-3)) {
    MessageBox(TopLevel,"Ngraph.ini is write protected.",
               "Ngraph.ini",MB_OK);
    return FALSE;
  } else if ((ret==-2) || (ret==3) || (ret==2)) {
    if (MessageBox(TopLevel,
      "Install `Ngraph.ini' to your $NGRAPHHOME or $HOME directory?",
      "Ngraph.ini",MB_YESNO)==IDYES) {
      if (!copyconfig()) {
        MessageBox(TopLevel,"Ngraph.ini could not be copied.",
                   "Ngraph.ini",MB_OK);
        return FALSE;
      }
    } else {
      return FALSE;
    }
  }
  return TRUE;
}

void SaveHistory()
{
  struct narray conf;
  char *buf;
  int i,num,num2;
  char **data;

  if (!menulocal.savehistory) return;
  if (!CheckIniFile()) return;
  arrayinit(&conf,sizeof(char *));
  num=arraynum(menulocal.ngpfilelist);
  data=(char **)arraydata(menulocal.ngpfilelist);
  for (i=0;i<num;i++) {
    if ((data[i]!=NULL)
    && ((buf=(char *)memalloc(13+strlen(data[i])))!=NULL)) {
      sprintf(buf,"ngp_history=%s",data[i]);
      arrayadd(&conf,&buf);
    }
  }
  num2=arraynum(menulocal.ngpdirlist);
  data=(char **)arraydata(menulocal.ngpdirlist);
  for (i=0;i<num;i++) {
    if ((i>=num2) || (data[i]==NULL)) {
      if ((buf=(char *)memalloc(17))!=NULL) {
        sprintf(buf,"ngp_dir_history=");
        arrayadd(&conf,&buf);
      }
    } else if ((buf=(char *)memalloc(17+strlen(data[i])))!=NULL) {
      sprintf(buf,"ngp_dir_history=%s",data[i]);
      arrayadd(&conf,&buf);
    }
  }
  num=arraynum(menulocal.datafilelist);
  data=(char **)arraydata(menulocal.datafilelist);
  for (i=0;i<num;i++) {
    if ((data[i]!=NULL)
    && ((buf=(char *)memalloc(14+strlen(data[i])))!=NULL)) {
      sprintf(buf,"data_history=%s",data[i]);
      arrayadd(&conf,&buf);
    }
  }
  replaceconfig("[x11menu]",&conf);
  arraydel2(&conf);
  arrayinit(&conf,sizeof(char *));
  if (arraynum(menulocal.ngpfilelist)==0) {
    if ((buf=(char *)memalloc(20))!=NULL) {
      sprintf(buf,"ngp_history");
      arrayadd(&conf,&buf);
    }
  }
  if (arraynum(menulocal.datafilelist)==0) {
    if ((buf=(char *)memalloc(20))!=NULL) {
      sprintf(buf,"data_history");
      arrayadd(&conf,&buf);
    }
  }
  removeconfig("[x11menu]",&conf);
  arraydel2(&conf);
}

void ExtTextOut(Display *disp,Window win,GC gc,
                int x,int y,XRectangle *rect,char *buf,int len)
{
  Region region;
  XRectangle rect2;

  region=XCreateRegion();
  XUnionRectWithRegion(rect,region,region);
  XSetRegion(disp,gc,region);
  XmbDrawString(disp,win,FFont,gc,x,y,buf,len);
  XDestroyRegion(region);
  rect2.x=0;
  rect2.y=0;
  rect2.width=SHRT_MAX;
  rect2.height=SHRT_MAX;
  region=XCreateRegion();
  XUnionRectWithRegion(&rect2,region,region);
  XSetRegion(disp,gc,region);
  XDestroyRegion(region);
}
