/* 
 * $Id: x11view.c,v 1.9 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: x11view.c,v $
 * Revision 1.9  2003/02/16 12:43:37  isizaka
 * for release 6.13.18
 *
 * Revision 1.8  2002/07/06 08:57:25  isizaka
 * change to GPL.
 *
 * Revision 1.7  2001/07/14 17:44:50  isizaka
 * for 6.03.14
 *
 * Revision 1.6  2001/03/23 12:17:43  isizaka
 * for 6.3.13
 *
 * Revision 1.5  2000/11/11 07:01:28  isizaka
 * Zooming rate: 2 --> sqrt(2)
 *
 * 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/21 12:51:18  isizaka
 * change axis initialization
 *
 * Revision 1.1  1999/03/17 13:29:01  isizaka
 * Initial revision
 *
 *
 **/

#include <Xm/XmAll.h>
#include <X11/keysym.h>
#include <X11/cursorfont.h>
#include <math.h>

#include "motif12.h"

#include "ngraph.h"
#include "object.h"
#include "gra.h"
#include "mathfn.h"

#include "x11gui.h"
#include "x11dialg.h"
#include "ox11menu.h"
#include "x11menu.h"
#include "x11graph.h"
#include "x11file.h"
#include "x11axis.h"
#include "x11cood.h"
#include "x11lgnd.h"
#include "x11view.h"
#include "x11commn.h"

struct focuslist {
  struct objlist *obj;
  int oid;
  int ofsx,ofsy;
};

struct pointslist {
  int x,y;
};

#define MOUSENONE 0
#define MOUSEPOINT 1
#define MOUSEDRAG  2
#define MOUSEZOOM1 3
#define MOUSEZOOM2 4
#define MOUSEZOOM3 5
#define MOUSEZOOM4 6
#define MOUSECHANGE 7

#define PointB 0
#define LegendB 1
#define LineB 2
#define CurveB 3
#define PolyB 4
#define RectB 5
#define ArcB 6
#define MarkB 7
#define TextB 8
#define GaussB 9
#define AxisB 10
#define TrimB 11
#define FrameB 12
#define SectionB 13
#define CrossB 14
#define SingleB 15
#define DataB 16
#define EvalB 17
#define ZoomB 18

Region region=NULL;
int PaintLock=FALSE;

struct evaltype evallist[100];
struct narray sellist;
char evbuf[256];

#define IDEVMASK        101
#define IDEVMOVE        102

void EvalDialogSetupItem(Widget w,struct EvalDialog *d)
{
  int i,j;
  XmString xs;

  XmListDeleteAllItems(XtNameToWidget(w,"*list"));
  for (i=0;i<d->Num;i++) {
    j=0;
    j+=sprintf(evbuf+j,"%6d ",evallist[i].id);
    j+=sprintf(evbuf+j,"%8d ",evallist[i].line);
    j+=sprintf(evbuf+j,"%+.15e %+.15e",evallist[i].x,evallist[i].y);
    xs=XmStringCreateLocalized(evbuf);
    XmListAddItem(XtNameToWidget(w,"*list"),xs,0);
    XmStringFree(xs);
  }
}

void EvalDialogMask(Widget w,XtPointer client_data,XtPointer call_data)
{
  struct EvalDialog *d;

  d=(struct EvalDialog *)client_data;
  d->ret=IDEVMASK;
  d->CloseWindow(d->widget,client_data);
}

void EvalDialogMove(Widget w,XtPointer client_data,XtPointer call_data)
{
  struct EvalDialog *d;

  d=(struct EvalDialog *)client_data;
  d->ret=IDEVMOVE;
  d->CloseWindow(d->widget,client_data);
}

void EvalDialogSetup(Widget w,void *data,int makewidget)
{
  Arg al[20];
  Cardinal ac;
  Widget rc,button;
  struct EvalDialog *d;
  
  d=(struct EvalDialog *)data;
  if (makewidget) {
    XtUnmanageChild(XmMessageBoxGetChild(w,XmDIALOG_CANCEL_BUTTON));
    ac=0;
    XtManageChild(button=XmCreatePushButton(w,"Mask",al,ac));
    XtAddCallback(button,XmNdisarmCallback,EvalDialogMask,d);
    ac=0;
    XtManageChild(button=XmCreatePushButton(w,"Move",al,ac));
    XtAddCallback(button,XmNdisarmCallback,EvalDialogMove,d);

    ac=0;
    XtSetArg(al[ac],XmNorientation,XmVERTICAL); ac++;
    XtManageChild(rc=XmCreateRowColumn(w,"rc",al,ac));
    ac=0;
    XtManageChild(XmCreateLabel(rc,
                  "--#--Line----------X----------------Y----------",al,ac));
    ac=0;
    XtSetArg(al[ac],XmNlistSizePolicy,XmCONSTANT); ac++;
    XtSetArg(al[ac],XmNscrollBarDisplayPolicy,XmSTATIC); ac++;
    XtSetArg(al[ac],XmNselectionPolicy,XmEXTENDED_SELECT); ac++;
    XtManageChild(XmCreateScrolledList(rc,"list",al,ac));
  }
  EvalDialogSetupItem(w,d);  
}

void EvalDialogClose(Widget w,void *data)
{
  struct EvalDialog *d;
  int i,a,num;
  int *selected;

  d=(struct EvalDialog *)data;
  if ((d->ret==IDEVMASK) || (d->ret==IDEVMOVE)) {
    XmListGetSelectedPos(XtNameToWidget(w,"*list"),&selected,&num);
    if (num!=0) {
      for (i=0;i<num;i++) {
        a=selected[i]-1;
        arrayadd(d->sel,&a);
      }
      XtFree((char *)selected);
    }
  }
}

void EvalDialog(struct EvalDialog *data,
                struct objlist *obj,int num,struct narray *iarray)
{
  data->SetupWindow=EvalDialogSetup;
  data->CloseWindow=EvalDialogClose;
  data->Obj=obj;
  data->Num=num;
  arrayinit(iarray,sizeof(int));
  data->sel=iarray;
}

void ViewerWinSetup()
{
  struct Viewer *d;
  int x,y;
  unsigned int width,height,border,depth;
  Window root;
  XSetWindowAttributes attr;

  d=&(NgraphApp.Viewer);
  d->win=XtWindow(NgraphApp.Viewer.Win);
  menulocal.GRAoid=-1;
  menulocal.GRAinst=NULL;
  d->Mode=PointB;
  d->Capture=FALSE;
  d->MoveData=FALSE;
  d->MouseMode=MOUSENONE;
  d->focusobj=arraynew(sizeof(struct focuslist *));
  d->points=arraynew(sizeof(struct pointslist *));
  d->FrameOfsX=0;
  d->FrameOfsY=0;
  d->LineX=0;
  d->LineY=0;
  d->CrossX=0;
  d->CrossY=0;
  d->ShowFrame=FALSE;
  d->ShowLine=FALSE;
  d->ShowRect=FALSE;
  d->ShowCross=FALSE;
  d->allclear=TRUE;
  d->ignoreredraw=FALSE;
  d->Mode=PointB;
  region=NULL;
  OpenGRA(); 
  OpenGC();
  SetScroller();
  ChangeDPI(TRUE);
  XGetGeometry(XtDisplay(d->Win),XtWindow(d->Win),
               &root,&x,&y,&width,&height,&border,&depth);
  d->width=width;
  d->height=height;
  d->cx=width/2;
  d->cy=height/2;
  XtVaSetValues(d->HScroll,XmNincrement,10,XmNminimum,0,NULL);
  XtVaSetValues(d->VScroll,XmNincrement,10,XmNminimum,0,NULL);
  XtVaGetValues(d->HScroll,XmNvalue,&(d->hscroll),NULL);
  XtVaGetValues(d->VScroll,XmNvalue,&(d->vscroll),NULL);
  XtAddCallback(d->Win,XmNexposeCallback,ViewerEvPaint,NULL);
  XtAddCallback(d->Win,XmNresizeCallback,ViewerEvSize,NULL);
  XtAddCallback(d->HScroll,XmNvalueChangedCallback,ViewerEvHScroll,NULL);
  XtAddCallback(d->VScroll,XmNvalueChangedCallback,ViewerEvVScroll,NULL);
  XtAddEventHandler(d->Win,ButtonPressMask,False,ViewerEvButtonDown,d);
  XtAddEventHandler(d->Win,ButtonReleaseMask,False,ViewerEvButtonUp,d);
  XtAddEventHandler(d->Win,PointerMotionMask,False,ViewerEvMouseMotion,d);
  XtAddEventHandler(d->Win,KeyPressMask,False,ViewerEvKeyDown,d);
  XtAddEventHandler(d->Win,KeyReleaseMask,False,ViewerEvKeyUp,d);
  if (mxlocal->backingstore) attr.backing_store=Always;
  else attr.backing_store=NotUseful;
  XChangeWindowAttributes(XtDisplay(d->Win),XtWindow(d->Win),
                          CWBackingStore,&attr);
  d->popup=XmVaCreateSimplePopupMenu(d->Win,"viewerpopup",ViewerPopupMenu,
    XmVaPUSHBUTTON,NULL,NULL,NULL,NULL,
    XmVaPUSHBUTTON,NULL,NULL,NULL,NULL,
    XmVaPUSHBUTTON,NULL,NULL,NULL,NULL,
    XmVaPUSHBUTTON,NULL,NULL,NULL,NULL,
    XmVaPUSHBUTTON,NULL,NULL,NULL,NULL,
    XmVaPUSHBUTTON,NULL,NULL,NULL,NULL,
    NULL);
}

void ViewerWinClose()
{
  struct Viewer *d;

  d=&(NgraphApp.Viewer);
  CloseGC();
  CloseGRA();
  arrayfree2(d->focusobj);
  arrayfree2(d->points); 
  XUndefineCursor(XtDisplay(d->Win),XtWindow(d->Win));
  if (region!=NULL) XDestroyRegion(region);
}

int ViewerWinFileUpdate(int x1,int y1,int x2,int y2,int err)
{
  struct objlist *fileobj;
  char *argv[7];
  struct narray *sarray;
  char **sdata;
  int snum;
  struct objlist *dobj;
  int did,id,limit;
  char *dfield,*dinst;
  int i,j;
  struct narray *eval;
  int evalnum;
  int minx,miny,maxx,maxy;
  struct savedstdio save;
  char mes[256];
  struct narray dfile;
  int *ddata,dnum;
  int ret,ret2;

  ret=FALSE;
  ignorestdio(&save);
  minx=(x1<x2)?x1:x2;
  miny=(y1<y2)?y1:y2;
  maxx=(x1>x2)?x1:x2;
  maxy=(y1>y2)?y1:y2;
  limit=1;
  argv[0]=(char *)&minx;
  argv[1]=(char *)&miny;
  argv[2]=(char *)&maxx;
  argv[3]=(char *)&maxy;
  argv[4]=(char *)&err;
  argv[5]=(char *)&limit;
  argv[6]=NULL;
  if ((fileobj=chkobject("file"))!=NULL) {
    arrayinit(&dfile,sizeof(int));
    sprintf(mes,"Searching for data.");
    SetStatusBar(mes);
    if (_getobj(menulocal.obj,"_list",menulocal.inst,&sarray)) return ret;
    if ((snum=arraynum(sarray))==0) return ret;
    sdata=(char **)arraydata(sarray);
    for (i=1;i<snum;i++) {
      if (((dobj=getobjlist(sdata[i],&did,&dfield,NULL))!=NULL)
      && chkobjchild(fileobj,dobj)) {
        if ((dinst=chkobjinstoid(dobj,did))!=NULL) {
          _getobj(dobj,"id",dinst,&id);
          _exeobj(dobj,"evaluate",dinst,6,argv);
          _getobj(dobj,"evaluate",dinst,&eval);
          evalnum=arraynum(eval)/3;
          if (evalnum!=0) arrayadd(&dfile,&id);
        }
      }
    }
    ResetStatusBar();
    dnum=arraynum(&dfile);
    ddata=(int *)arraydata(&dfile);
    for (i=0;i<dnum;i++) {
      id=ddata[i];
      FileDialog(&DlgFile,fileobj,id);
      if ((ret2=DialogExecute(TopLevel,&DlgFile))==IDDELETE) {
        FitDel(fileobj,id);
        delobj(fileobj,id);
        for (j=i+1;j<dnum;j++) if (ddata[i]>id) ddata[i]=ddata[i]-1;
      } else if (ret2!=IDCANCEL) NgraphApp.Changed=TRUE;
      ret=TRUE;
    }
    arraydel(&dfile);
  }
  restorestdio(&save);
  return ret;
}

void Evaluate(int x1,int y1,int x2,int y2,int err)
{
  struct objlist *fileobj;
  char *argv[7];
  struct narray *sarray;
  char **sdata;
  int snum;
  struct objlist *dobj;
  int did,id,limit;
  char *dfield,*dinst;
  int i,j;
  struct narray *eval;
  int evalnum,tot;
  int minx,miny,maxx,maxy;
  struct savedstdio save;
  double line,dx,dy;
  char mes[256];
  int ret;
  int selnum,sel;
  int masknum,iline;
  struct narray *mask;
  struct Viewer *d;

  d=&(NgraphApp.Viewer);
  ignorestdio(&save);
  minx=(x1<x2)?x1:x2;
  miny=(y1<y2)?y1:y2;
  maxx=(x1>x2)?x1:x2;
  maxy=(y1>y2)?y1:y2;
  limit=100;
  argv[0]=(char *)&minx;
  argv[1]=(char *)&miny;
  argv[2]=(char *)&maxx;
  argv[3]=(char *)&maxy;
  argv[4]=(char *)&err;
  argv[5]=(char *)&limit;
  argv[6]=NULL;
  if ((fileobj=chkobject("file"))!=NULL) {
    sprintf(mes,"Evaluating.");
    SetStatusBar(mes);
    if (_getobj(menulocal.obj,"_list",menulocal.inst,&sarray)) return;
    if ((snum=arraynum(sarray))==0) return;
    sdata=(char **)arraydata(sarray);
    tot=0;
    for (i=1;i<snum;i++) {
      if (((dobj=getobjlist(sdata[i],&did,&dfield,NULL))!=NULL)
      && (fileobj==dobj)) {
        if ((dinst=chkobjinstoid(dobj,did))!=NULL) {
          _getobj(dobj,"id",dinst,&id);
          _exeobj(dobj,"evaluate",dinst,6,argv);
          _getobj(dobj,"evaluate",dinst,&eval);
          evalnum=arraynum(eval)/3;
          for (j=0;j<evalnum;j++) {
            if (tot>=limit) break;
            tot++;
            line=*(double *)arraynget(eval,j*3+0);
            dx=*(double *)arraynget(eval,j*3+1);
            dy=*(double *)arraynget(eval,j*3+2);
            evallist[tot-1].id=id;
            evallist[tot-1].line=nround(line);
            evallist[tot-1].x=dx;
            evallist[tot-1].y=dy;
          }
          if (tot>=limit) break;
        }
      }
    }
    ResetStatusBar();
    if (tot>0) {
      EvalDialog(&DlgEval,fileobj,tot,&sellist);
      ret=DialogExecute(TopLevel,&DlgEval);
      selnum=arraynum(&sellist);
      if (ret==IDEVMASK) {
        for (i=0;i<selnum;i++) {
          sel=*(int *)arraynget(&sellist,i);
          getobj(fileobj,"mask",evallist[sel].id,0,NULL,&mask);
          if (mask==NULL) {
            mask=arraynew(sizeof(int));
            putobj(fileobj,"mask",evallist[sel].id,mask);
          }
          masknum=arraynum(mask);
          for (j=0;j<masknum;j++) {
            iline=*(int *)arraynget(mask,j);
            if (iline==evallist[sel].line) break;
          }
          if (j==masknum) {
            arrayadd(mask,&(evallist[sel].line));
            NgraphApp.Changed=TRUE;
          }
        }
        arraydel(&sellist);
      } else if ((ret==IDEVMOVE) && (selnum>0)) {
        SetCursor(XC_left_ptr);
        d->Capture=TRUE;
        d->MoveData=TRUE;
      }
    }
  }
  restorestdio(&save);
}

void Trimming(int x1,int y1,int x2,int y2)
{
  struct narray farray;
  struct objlist *obj;
  int i,id;
  int *array,num;
  int vx1,vy1,vx2,vy2;
  int maxx,maxy,minx,miny;
  int dir,rcode1,rcode2,room;
  double ax,ay,ip1,ip2,min,max;
  char *argv[4];
  struct Viewer *d;

  d=&(NgraphApp.Viewer);
  if ((x1==x2) && (y1==y2)) return;
  if ((obj=chkobject("axis"))==NULL) return;
  if (chkobjlastinst(obj)==-1) return;
  SelectDialog(&DlgSelect,obj,AxisCB,(struct narray *)&farray,NULL);
  if (DialogExecute(TopLevel,&DlgSelect)==IDOK) {
    vx1=x1-x2;
    vy1=y1-y2;
    vx2=x2-x1;
    vy2=y1-y2;
    num=arraynum(&farray);
    array=(int *)arraydata(&farray);
    for (i=0;i<num;i++) {
      id=array[i];
      getobj(obj,"direction",id,0,NULL,&dir);
      ax=cos(dir/18000.0*MPI);
      ay=-sin(dir/18000.0*MPI);
      ip1=ax*vx1+ay*vy1;
      ip2=ax*vx2+ay*vy2;
      if (fabs(ip1)>fabs(ip2)) {
        if (ip1>0) {
          maxx=x1;
          maxy=y1;
          minx=x2;
          miny=y2;
        } else if (ip1<0) {
          maxx=x2;
          maxy=y2;
          minx=x1;
          miny=y1;
        } else {
          maxx=minx=0;
          maxy=miny=0;
        }
      } else {
        if (ip2>0) {
          maxx=x2;
          maxy=y1;
          minx=x1;
          miny=y2;
        } else if (ip2<0) {
          maxx=x1;
          maxy=y2;
          minx=x2;
          miny=y1;
        } else {
          maxx=minx=0;
          maxy=miny=0;
        }
      }
      if ((minx!=maxx) && (miny!=maxy)) {
        argv[0]=(char *)&minx;
        argv[1]=(char *)&miny;
        argv[2]=NULL;
        rcode1=getobj(obj,"coordinate",id,2,argv,&min);
        argv[0]=(char *)&maxx;
        argv[1]=(char *)&maxy;
        argv[2]=NULL;
        rcode2=getobj(obj,"coordinate",id,2,argv,&max);
        if ((rcode1!=-1) && (rcode2!=-1)) {
          exeobj(obj,"scale_push",id,0,NULL);
          room=1;
          argv[0]=(char *)&min;
          argv[1]=(char *)&max;
          argv[2]=(char *)&room;
          argv[3]=NULL;
          exeobj(obj,"scale",id,3,argv);
          NgraphApp.Changed=TRUE;
        }
      }
    }
    AdjustAxis();
    d->allclear=TRUE;
    UpdateAll();
  }
  arraydel(&farray);
}


void Match(char *objname,int x1,int y1,int x2,int y2,int err)
{
  struct objlist *fobj;
  char *argv[6];
  struct narray *sarray;
  char **sdata;
  int snum;
  struct objlist *dobj;
  int did;
  char *dfield,*dinst;
  int i,match;
  struct focuslist *focus;
  int minx,miny,maxx,maxy;
  struct savedstdio save;
  struct Viewer *d;

  d=&(NgraphApp.Viewer);
  ignorestdio(&save);
  minx=(x1<x2)?x1:x2;
  miny=(y1<y2)?y1:y2;
  maxx=(x1>x2)?x1:x2;
  maxy=(y1>y2)?y1:y2;
  argv[0]=(char *)&minx;
  argv[1]=(char *)&miny;
  argv[2]=(char *)&maxx;
  argv[3]=(char *)&maxy;
  argv[4]=(char *)&err;
  argv[5]=NULL;
  if ((fobj=chkobject(objname))!=NULL) {
    if (_getobj(menulocal.obj,"_list",menulocal.inst,&sarray)) return;
    if ((snum=arraynum(sarray))==0) return;
    sdata=(char **)arraydata(sarray);
    for (i=1;i<snum;i++) {
      if (((dobj=getobjlist(sdata[i],&did,&dfield,NULL))!=NULL)
       && chkobjchild(fobj,dobj)) {
        if ((dinst=chkobjinstoid(dobj,did))!=NULL) {
          _exeobj(dobj,"match",dinst,5,argv);
          _getobj(dobj,"match",dinst,&match);
          if (match) {
            if ((focus=(struct focuslist *)memalloc(sizeof(struct focuslist)))
            !=NULL) {
              focus->obj=dobj;
              focus->oid=did;
              arrayadd(d->focusobj,&focus);
            }
          }
        }
      }
    }
  }
  restorestdio(&save);
}

void AddList(struct objlist *obj,char *inst)
{
  int addi;
  struct objlist *aobj;
  char *ainst;
  char *afield;
  int i,j,po,num,oid,id,id2;
  struct objlist **objlist;
  struct objlist *obj2;
  char *inst2;
  char *field,**objname;
  struct narray *draw,drawrable;
  struct Viewer *d;

  d=&(NgraphApp.Viewer);
  aobj=obj;
  ainst=inst;
  afield="draw";
  addi=-1;
  _getobj(obj,"id",inst,&id);
  draw=&(menulocal.drawrable);
  num=arraynum(draw);
  if (num==0) {
    arrayinit(&drawrable,sizeof(char *));
    menuadddrawrable(chkobject("draw"),&drawrable);
    draw=&drawrable;
    num=arraynum(draw);
  }
  if ((objlist=(struct objlist **)memalloc(sizeof(struct objlist *)*num))
  ==NULL) return;
  po=0;
  for (i=0;i<num;i++) {
    objname=(char **)arraynget(draw,i);
    objlist[i]=chkobject(*objname);
    if (objlist[i]==obj) po=i;
  }
  i=1;
  j=0;
  while ((obj2=GRAgetlist(menulocal.GC,&oid,&field,i))!=NULL) {
   for (;j<num;j++) if (objlist[j]==obj2) break;
   if (j==po) {
     inst2=chkobjinstoid(obj2,oid);
     _getobj(obj2,"id",inst2,&id2);
     if (id2>id) {
       addi=i;
       memfree(objlist);
       mx_inslist(menulocal.obj,menulocal.inst,aobj,ainst,afield,addi);
       if (draw!=&(menulocal.drawrable)) arraydel2(draw);
       return;
     }
   } else if (j>po) {
     addi=i;
     memfree(objlist);
     mx_inslist(menulocal.obj,menulocal.inst,aobj,ainst,afield,addi);
     if (draw!=&(menulocal.drawrable)) arraydel2(draw);
     return;
   }
   i++;
  }
  addi=i;
  memfree(objlist);
  mx_inslist(menulocal.obj,menulocal.inst,aobj,ainst,afield,addi);
  if (draw!=&(menulocal.drawrable)) arraydel2(draw);
}

void DelList(struct objlist *obj,char *inst)
{
  int i,oid,oid2;
  struct objlist *obj2;
  char *field;
  struct Viewer *d;

  d=&(NgraphApp.Viewer);
  _getobj(obj,"oid",inst,&oid);
  i=0;
  while ((obj2=GRAgetlist(menulocal.GC,&oid2,&field,i))!=NULL) {
    if ((obj2==obj) && (oid==oid2))
      mx_dellist(menulocal.obj,menulocal.inst,i);
    i++;
  }
}

void AddInvalidateRect(struct objlist *obj,char *inst)
{
  struct narray *abbox;
  int bboxnum,*bbox;
  double zoom;
  struct Viewer *d;
  XRectangle rect;

  d=&(NgraphApp.Viewer);
  _exeobj(obj,"bbox",inst,0,NULL);
  _getobj(obj,"bbox",inst,&abbox);
  bboxnum=arraynum(abbox);
  bbox=(int *)arraydata(abbox);
  if (bboxnum>=4) {
    zoom=menulocal.PaperZoom/10000.0;
    rect.x=mxd2p(bbox[0]*zoom+menulocal.LeftMargin)
          -d->hscroll+d->cx-7;
    rect.y=mxd2p(bbox[1]*zoom+menulocal.TopMargin)
          -d->vscroll+d->cy-7;
    rect.width=mxd2p(bbox[2]*zoom+menulocal.LeftMargin)
              -d->hscroll+d->cx+7-rect.x;
    rect.height=mxd2p(bbox[3]*zoom+menulocal.TopMargin)
               -d->vscroll+d->cy+7-rect.y;
    if (region==NULL) region=XCreateRegion();
    XUnionRectWithRegion(&rect,region,region);
  }
}


void GetLargeFrame(int *minx,int *miny,int *maxx,int *maxy)
{
  int i,num;
  struct focuslist **focus;
  struct narray *abbox;
  int bboxnum,*bbox;
  char *inst;
  struct savedstdio save;
  struct Viewer *d;

  d=&(NgraphApp.Viewer);
  ignorestdio(&save);
  *minx=*miny=*maxx=*maxy=0;
  num=arraynum(d->focusobj);
  focus=(struct focuslist **)arraydata(d->focusobj);
  if ((inst=chkobjinstoid(focus[0]->obj,focus[0]->oid))!=NULL) {
    _exeobj(focus[0]->obj,"bbox",inst,0,NULL);
    _getobj(focus[0]->obj,"bbox",inst,&abbox);
    bboxnum=arraynum(abbox);
    bbox=(int *)arraydata(abbox);
    if (bboxnum>=4) {
      *minx=bbox[0];
      *miny=bbox[1];
      *maxx=bbox[2];
      *maxy=bbox[3];
    }
  }
  for (i=1;i<num;i++)
  if ((inst=chkobjinstoid(focus[i]->obj,focus[i]->oid))!=NULL) {
    _exeobj(focus[i]->obj,"bbox",inst,0,NULL);
    _getobj(focus[i]->obj,"bbox",inst,&abbox);
    bboxnum=arraynum(abbox);
    bbox=(int *)arraydata(abbox);
    if (bboxnum>=4) {
      if (bbox[0]<*minx) *minx=bbox[0];
      if (bbox[1]<*miny) *miny=bbox[1];
      if (bbox[2]>*maxx) *maxx=bbox[2];
      if (bbox[3]>*maxy) *maxy=bbox[3];
    }
  }
  restorestdio(&save);
}

void GetFocusFrame(int *minx,int *miny,int *maxx,int *maxy,int ofsx,int ofsy)
{
  int x1,y1,x2,y2;
  double zoom;
  struct Viewer *d;

  d=&(NgraphApp.Viewer);
  GetLargeFrame(&x1,&y1,&x2,&y2);
  zoom=menulocal.PaperZoom/10000.0;
  *minx=mxd2p((x1+ofsx)*zoom+menulocal.LeftMargin)-d->hscroll+d->cx;
  *miny=mxd2p((y1+ofsy)*zoom+menulocal.TopMargin)-d->vscroll+d->cy;
  *maxx=mxd2p((x2+ofsx)*zoom+menulocal.LeftMargin)-d->hscroll+d->cx;
  *maxy=mxd2p((y2+ofsy)*zoom+menulocal.TopMargin)-d->vscroll+d->cy;
}

void ShowFocusFrame(GC gc)
{
  int i,j,num;
  struct focuslist **focus;
  struct narray *abbox;
  int bboxnum;
  int *bbox;
  int x1,y1,x2,y2;
  char *inst;
  struct savedstdio save;
  double zoom;
  struct Viewer *d;
  int minx,miny,height,width;

  d=&(NgraphApp.Viewer);
  ignorestdio(&save);
  XSetForeground(Disp,gc,white ^ black);
  XSetFunction(Disp,gc,GXxor);
  XSetLineAttributes(Disp,gc,0,LineOnOffDash,CapButt,JoinMiter);
  XSetDashes(Disp,gc,0,dashes,2);
  num=arraynum(d->focusobj);
  focus=(struct focuslist **)arraydata(d->focusobj);
  if (num>0) {
    GetFocusFrame(&x1,&y1,&x2,&y2,d->FrameOfsX,d->FrameOfsY);
    x1-=5;
    y1-=5;
    x2+=4;
    y2+=4;
    minx=(x1<x2)?x1:x2;
    miny=(y1<y2)?y1:y2;
    width=abs(x2-x1);
    height=abs(y2-y1);
    XDrawRectangle(Disp,d->win,gc,minx,miny,width,height);
    XFillRectangle(Disp,d->win,gc,x1-6,y1-6,6,6);
    XFillRectangle(Disp,d->win,gc,x1-6,y2,6,6);
    XFillRectangle(Disp,d->win,gc,x2,y1-6,6,6);
    XFillRectangle(Disp,d->win,gc,x2,y2,6,6);
  }
  zoom=menulocal.PaperZoom/10000.0;
  if (num>1) {
    for (i=0;i<num;i++) {
      if ((inst=chkobjinstoid(focus[i]->obj,focus[i]->oid))!=NULL) {
        _exeobj(focus[i]->obj,"bbox",inst,0,NULL);
        _getobj(focus[i]->obj,"bbox",inst,&abbox);
        bboxnum=arraynum(abbox);
        bbox=(int *)arraydata(abbox);
        if (bboxnum>=4) {
          x1=mxd2p((bbox[0]+d->FrameOfsX)*zoom+menulocal.LeftMargin)
            -d->hscroll+d->cx;
          y1=mxd2p((bbox[1]+d->FrameOfsY)*zoom+menulocal.TopMargin)
            -d->vscroll+d->cy;
          x2=mxd2p((bbox[2]+d->FrameOfsX)*zoom+menulocal.LeftMargin)
            -d->hscroll+d->cx;
          y2=mxd2p((bbox[3]+d->FrameOfsY)*zoom+menulocal.TopMargin)
            -d->vscroll+d->cy;
          minx=(x1<x2)?x1:x2;
          miny=(y1<y2)?y1:y2;
          width=abs(x2-x1);
          height=abs(y2-y1);
          XDrawRectangle(Disp,d->win,gc,minx,miny,width,height); 
        }
      }
    }
  } else if (num==1) {
    i=0;
    if ((inst=chkobjinstoid(focus[i]->obj,focus[i]->oid))!=NULL) {
      _exeobj(focus[i]->obj,"bbox",inst,0,NULL);
      _getobj(focus[i]->obj,"bbox",inst,&abbox);
      bboxnum=arraynum(abbox);
      bbox=(int *)arraydata(abbox);
      for (j=4;j<bboxnum;j+=2) {
        x1=mxd2p((bbox[j  ]+d->FrameOfsX)*zoom+menulocal.LeftMargin)
          -d->hscroll+d->cx;
        y1=mxd2p((bbox[j+1]+d->FrameOfsY)*zoom+menulocal.TopMargin)
          -d->vscroll+d->cy;
        XFillRectangle(Disp,d->win,gc,x1-3,y1-3,6,6);
      }
    }
  }
  XSetFunction(Disp,gc,GXcopy);
  restorestdio(&save);
}

void ShowFocusLine(GC gc,int change)
{
  int j,num;
  struct focuslist **focus;
  struct narray *abbox;
  int bboxnum;
  int *bbox;
  int x0=0,y0=0,x1=0,y1=0,x2=0,y2=0,ofsx,ofsy;
  char *inst;
  struct savedstdio save;
  double zoom;
  int frame;
  char *group;
  int minx,miny,height,width;
  struct Viewer *d;

  d=&(NgraphApp.Viewer);
  ignorestdio(&save);
  XSetForeground(Disp,gc,white ^ black);
  XSetFunction(Disp,gc,GXxor);
  XSetLineAttributes(Disp,gc,0,LineOnOffDash,CapButt,JoinMiter);
  XSetDashes(Disp,gc,0,dashes,2);
  num=arraynum(d->focusobj);
  focus=(struct focuslist **)arraydata(d->focusobj);
  zoom=menulocal.PaperZoom/10000.0;
  if (num==1) {
    if ((inst=chkobjinstoid(focus[0]->obj,focus[0]->oid))!=NULL) {
      _exeobj(focus[0]->obj,"bbox",inst,0,NULL);
      _getobj(focus[0]->obj,"bbox",inst,&abbox);
      bboxnum=arraynum(abbox);
      bbox=(int *)arraydata(abbox);
      frame=FALSE;
      if (focus[0]->obj==chkobject("rectangle")) frame=TRUE;
      if (focus[0]->obj==chkobject("axis")) {
        _getobj(focus[0]->obj,"group",inst,&group);
        if ((group!=NULL) && (group[0]!='a')) frame=TRUE;
      }
      if (!frame) {
        for (j=4;j<bboxnum;j+=2) {
          if (change==(j-4)/2) {
            ofsx=d->LineX;
            ofsy=d->LineY;
          } else {
            ofsx=0;
            ofsy=0;
          }
          x1=mxd2p((bbox[j  ]+ofsx)*zoom+menulocal.LeftMargin)
            -d->hscroll+d->cx;
          y1=mxd2p((bbox[j+1]+ofsy)*zoom+menulocal.TopMargin)
            -d->vscroll+d->cy;
          if (j!=4) XDrawLine(Disp,d->win,gc,x0,y0,x1,y1);
          x0=x1;
          y0=y1;
        }
      } else {
        if (change==0) {
          x1=mxd2p((bbox[4]+d->LineX)*zoom+menulocal.LeftMargin)
            -d->hscroll+d->cx;
          y1=mxd2p((bbox[5]+d->LineY)*zoom+menulocal.TopMargin)
            -d->vscroll+d->cy;
          x2=mxd2p((bbox[8])*zoom+menulocal.LeftMargin)
            -d->hscroll+d->cx;
          y2=mxd2p((bbox[9])*zoom+menulocal.TopMargin)
            -d->vscroll+d->cy;
        } else if (change==1) {
          x1=mxd2p((bbox[4])*zoom+menulocal.LeftMargin)
            -d->hscroll+d->cx;
          y1=mxd2p((bbox[5]+d->LineY)*zoom+menulocal.TopMargin)
            -d->vscroll+d->cy;
          x2=mxd2p((bbox[8]+d->LineX)*zoom+menulocal.LeftMargin)
            -d->hscroll+d->cx;
          y2=mxd2p((bbox[9])*zoom+menulocal.TopMargin)
            -d->vscroll+d->cy;
        } else if (change==2) {
          x1=mxd2p((bbox[4])*zoom+menulocal.LeftMargin)
            -d->hscroll+d->cx;
          y1=mxd2p((bbox[5])*zoom+menulocal.TopMargin)
            -d->vscroll+d->cy;
          x2=mxd2p((bbox[8]+d->LineX)*zoom+menulocal.LeftMargin)
            -d->hscroll+d->cx;
          y2=mxd2p((bbox[9]+d->LineY)*zoom+menulocal.TopMargin)
            -d->vscroll+d->cy;
        } else if (change==3) {
          x1=mxd2p((bbox[4]+d->LineX)*zoom+menulocal.LeftMargin)
            -d->hscroll+d->cx;
          y1=mxd2p((bbox[5])*zoom+menulocal.TopMargin)
            -d->vscroll+d->cy;
          x2=mxd2p((bbox[8])*zoom+menulocal.LeftMargin)
            -d->hscroll+d->cx;
          y2=mxd2p((bbox[9]+d->LineY)*zoom+menulocal.TopMargin)
            -d->vscroll+d->cy;
        }
        minx=(x1<x2)?x1:x2;
        miny=(y1<y2)?y1:y2;
        width=abs(x2-x1);
        height=abs(y2-y1);
        XDrawRectangle(Disp,d->win,gc,minx,miny,width,height); 
      }
    }
  }
  XSetFunction(Disp,gc,GXcopy);
  restorestdio(&save);
}

void ShowPoints(GC gc)
{
  int i,num,x0=0,y0=0,x1,y1,x2,y2;
  struct pointslist **po;
  double zoom;
  struct Viewer *d;
  int minx,miny,height,width;

  d=&(NgraphApp.Viewer);
  XSetForeground(Disp,gc,white ^ black);
  XSetFunction(Disp,gc,GXxor);
  num=arraynum(d->points);
  po=(struct pointslist **)arraydata(d->points);
  zoom=menulocal.PaperZoom/10000.0;
  if ((d->Mode==RectB) || (d->Mode==ArcB) || (d->Mode==GaussB)
  || (d->Mode==FrameB) || (d->Mode==SectionB) || (d->Mode==CrossB)) {
    if (num==2) {
      XSetLineAttributes(Disp,gc,0,LineOnOffDash,CapButt,JoinMiter);
      XSetDashes(Disp,gc,0,dashes,2);
      x1=mxd2p(po[0]->x*zoom+menulocal.LeftMargin)-d->hscroll+d->cx;
      y1=mxd2p(po[0]->y*zoom+menulocal.TopMargin)-d->vscroll+d->cy;
      x2=mxd2p(po[1]->x*zoom+menulocal.LeftMargin)-d->hscroll+d->cx;
      y2=mxd2p(po[1]->y*zoom+menulocal.TopMargin)-d->vscroll+d->cy;
      minx=(x1<x2)?x1:x2;
      miny=(y1<y2)?y1:y2;
      width=abs(x2-x1);
      height=abs(y2-y1);
      XDrawRectangle(Disp,d->win,gc,minx,miny,width,height);
    }
  } else {
    XSetLineAttributes(Disp,gc,0,LineSolid,CapButt,JoinMiter);
    for (i=0;i<num;i++) {
      x1=mxd2p(po[i]->x*zoom+menulocal.LeftMargin)-d->hscroll+d->cx;
      y1=mxd2p(po[i]->y*zoom+menulocal.TopMargin)-d->vscroll+d->cy;
      XDrawLine(Disp,d->win,gc,x1-4,y1,x1+5,y1);
      XDrawLine(Disp,d->win,gc,x1,y1-4,x1,y1+5);
    }
    if (num>=1) {
      x1=mxd2p(po[0]->x*zoom+menulocal.LeftMargin)-d->hscroll+d->cx;
      y1=mxd2p(po[0]->y*zoom+menulocal.TopMargin)-d->vscroll+d->cy;
      x0=x1;
      y0=y1;
    }
    for (i=1;i<num;i++) {
      x1=mxd2p(po[i]->x*zoom+menulocal.LeftMargin)-d->hscroll+d->cx;
      y1=mxd2p(po[i]->y*zoom+menulocal.TopMargin)-d->vscroll+d->cy;
      XDrawLine(Disp,d->win,gc,x0,y0,x1,y1);
      x0=x1;
      y0=y1;
    }
  }
  XSetFunction(Disp,gc,GXcopy);
}

void ShowFrameRect(GC gc)
{
  int x1,y1,x2,y2;
  double zoom;
  struct Viewer *d;
  int minx,miny,width,height;

  d=&(NgraphApp.Viewer);
  zoom=menulocal.PaperZoom/10000.0;
  XSetForeground(Disp,gc,white ^ black);
  XSetFunction(Disp,gc,GXxor);
  XSetLineAttributes(Disp,gc,0,LineOnOffDash,CapButt,JoinMiter);
  XSetDashes(Disp,gc,0,dashes,2);
  if ((d->MouseX1!=d->MouseX2) || (d->MouseY1!=d->MouseY2)) {
    x1=mxd2p(d->MouseX1*zoom+menulocal.LeftMargin)-d->hscroll+d->cx;
    y1=mxd2p(d->MouseY1*zoom+menulocal.TopMargin)-d->vscroll+d->cy;
    x2=mxd2p(d->MouseX2*zoom+menulocal.LeftMargin)-d->hscroll+d->cx;
    y2=mxd2p(d->MouseY2*zoom+menulocal.TopMargin)-d->vscroll+d->cy;
    minx=(x1<x2)?x1:x2;
    miny=(y1<y2)?y1:y2;
    width=abs(x2-x1);
    height=abs(y2-y1);
    XDrawRectangle(Disp,d->win,gc,minx,miny,width,height);
  }
  XSetFunction(Disp,gc,GXcopy);
}

void ShowCrossGauge(GC gc)
{
  int x,y;
  double zoom;
  unsigned int width,height,border,depth;
  Window root;
  struct Viewer *d;

  d=&(NgraphApp.Viewer);
  XSetForeground(Disp,gc,white ^ gray);
  XSetFunction(Disp,gc,GXxor);
  XSetLineAttributes(Disp,gc,0,LineSolid,CapButt,JoinMiter);
  XGetGeometry(Disp,d->win,&root,&x,&y,&width,&height,&border,&depth);
  zoom=menulocal.PaperZoom/10000.0;
  x=mxd2p(d->CrossX*zoom+menulocal.LeftMargin)-d->hscroll+d->cx;
  y=mxd2p(d->CrossY*zoom+menulocal.TopMargin)-d->vscroll+d->cy;
  XDrawLine(Disp,d->win,gc,x,0,x,height);
  XDrawLine(Disp,d->win,gc,0,y,width,y);
  XSetFunction(Disp,gc,GXcopy);
}

void CheckGrid(int ofs,unsigned int state,int *x,int *y,double *zoom)
{
  int offset;
  int grid;

  if ((state & ControlMask) && (!ofs)) {
    if ((x!=NULL) && (y!=NULL)) {
      if (abs(*x)>abs(*y)) *y=0;
      else *x=0;
    }
  }
  grid=mxlocal->grid;
  if (!(state & ShiftMask)) {
    if (ofs) offset=grid/2;
    else offset=0;
    if (x!=NULL) *x=((*x+offset)/grid)*grid;
    if (y!=NULL) *y=((*y+offset)/grid)*grid;
    if (zoom!=NULL) *zoom=nround(*zoom*grid)/((double )grid);
  }
}

void ViewerEvLButtonDown(unsigned int state,TPoint point,struct Viewer *d)
{
  GC dc;
  int minx,miny,maxx,maxy;
  int x1,y1,vx1,vx2,vy1,vy2;
  double cc,nn;
  struct pointslist *po;
  double zoom,zoom2;
  int i,j;
  struct narray *abbox;
  int bboxnum;
  int *bbox;
  char *inst;
  struct focuslist *focus;
  int vdpi;
  int selnum,sel,movenum,iline;
  struct narray *move,*movex,*movey;
  struct objlist *fileobj,*aobjx,*aobjy;
  char *argv[3];
  double dx,dy;
  int ax,ay,anum;
  struct narray iarray;
  char *axis;

  if (menulock || globallock) return;
  if (region!=NULL) return;
  zoom=menulocal.PaperZoom/10000.0;
  d->MouseX1=d->MouseX2=(mxp2d(point.x-d->cx+d->hscroll)
                       -menulocal.LeftMargin)/zoom;
  d->MouseY1=d->MouseY2=(mxp2d(point.y-d->cy+d->vscroll)
                       -menulocal.TopMargin)/zoom;
  d->MouseMode=MOUSENONE;
  dc=XCreateGC(Disp,d->win,0,0);
  if (d->MoveData) {
    if ((fileobj=chkobject("file"))!=NULL) {
      selnum=arraynum(&sellist);
      for (i=0;i<selnum;i++) {
        sel=*(int *)arraynget(&sellist,i);
        getobj(fileobj,"axis_x",evallist[sel].id,0,NULL,&axis);
        arrayinit(&iarray,sizeof(int));
        if (getobjilist(axis,&aobjx,&iarray,FALSE,NULL)) ax=-1;
        else {
          anum=arraynum(&iarray);
          if (anum<1) ax=-1;
          else ax=*(int *)arraylast(&iarray);
          arraydel(&iarray);
        }
        getobj(fileobj,"axis_y",evallist[sel].id,0,NULL,&axis);
        arrayinit(&iarray,sizeof(int));
        if (getobjilist(axis,&aobjy,&iarray,FALSE,NULL)) ay=-1;
        else {
          anum=arraynum(&iarray);
          if (anum<1) ay=-1;
          else ay=*(int *)arraylast(&iarray);
          arraydel(&iarray);
        }
        if ((ax!=-1) && (ax!=-1)) {
          argv[0]=(char *)&(d->MouseX1);
          argv[1]=(char *)&(d->MouseY1);
          argv[2]=NULL;
          if ((getobj(aobjx,"coordinate",ax,2,argv,&dx)!=-1)
           && (getobj(aobjy,"coordinate",ay,2,argv,&dy)!=-1)) {
            getobj(fileobj,"move_data",evallist[sel].id,0,NULL,&move);
            getobj(fileobj,"move_data_x",evallist[sel].id,0,NULL,&movex);
            getobj(fileobj,"move_data_y",evallist[sel].id,0,NULL,&movey);
            if (move==NULL) {
              move=arraynew(sizeof(int));
              putobj(fileobj,"move_data",evallist[sel].id,move);
            }
            if (movex==NULL) {
              movex=arraynew(sizeof(double));
              putobj(fileobj,"move_data_x",evallist[sel].id,movex);
            }
            if (movey==NULL) {
              movey=arraynew(sizeof(double));
              putobj(fileobj,"move_data_y",evallist[sel].id,movey);
            }
            movenum=arraynum(move);
            if (arraynum(movex)<movenum) {
              for (j=movenum-1;j>=arraynum(movex);j--) {
                arrayndel(move,j);
              }
              movenum=arraynum(movex);
            }
            if (arraynum(movey)<movenum) {
              for (j=movenum-1;j>=arraynum(movey);j--) {
                arrayndel(move,j);
                arrayndel(movex,j);
              }
              movenum=arraynum(movey);
            }
            for (j=0;j<movenum;j++) {
              iline=*(int *)arraynget(move,j);
              if (iline==evallist[sel].line) break;
            }
            if (j==movenum) {
              arrayadd(move,&(evallist[sel].line));
              arrayadd(movex,&dx);
              arrayadd(movey,&dy);
              NgraphApp.Changed=TRUE;
            } else {
              arrayput(move,&(evallist[sel].line),j);
              arrayput(movex,&dx,j);
              arrayput(movey,&dy,j);
              NgraphApp.Changed=TRUE;
            }
          }
        }
      }
      MessageBox(TopLevel,"Data points are moved.","Confirm",MB_OK);
    }
    arraydel(&sellist);
    d->MoveData=FALSE;
    d->Capture=FALSE;
    SetCursor(XC_left_ptr);
  } else if ((d->Mode==PointB) || (d->Mode==LegendB) || (d->Mode==AxisB)) {
    d->Capture=TRUE;
    if (arraynum(d->focusobj)!=0) {
      GetFocusFrame(&minx,&miny,&maxx,&maxy,0,0);
      if ((minx-10<=point.x) && (point.x<=minx-4)
       && (miny-10<=point.y) && (point.y<=miny-4)) {
        GetLargeFrame(&(d->RefX2),&(d->RefY2),&(d->RefX1),&(d->RefY1));
        d->MouseMode=MOUSEZOOM1;
        SetCursor(XC_top_left_corner);
      } else if ((maxx+4<=point.x) && (point.x<=maxx+10)
       && (miny-10<=point.y) && (point.y<=miny-4)) {
        GetLargeFrame(&(d->RefX1),&(d->RefY2),&(d->RefX2),&(d->RefY1));
        d->MouseMode=MOUSEZOOM2;
        SetCursor(XC_top_right_corner);
      } else if ((maxx+4<=point.x) && (point.x<=maxx+10)
       && (maxy+4<=point.y) && (point.y<=maxy+10)) {
        GetLargeFrame(&(d->RefX1),&(d->RefY1),&(d->RefX2),&(d->RefY2));
        d->MouseMode=MOUSEZOOM3;
        SetCursor(XC_bottom_right_corner);
      } else if ((minx-10<=point.x) && (point.x<=minx-4)
       && (maxy+4<=point.y) && (point.y<=maxy+10)) {
        GetLargeFrame(&(d->RefX2),&(d->RefY1),&(d->RefX1),&(d->RefY2));
        d->MouseMode=MOUSEZOOM4;
        SetCursor(XC_bottom_left_corner);
      } else {
        focus=*(struct focuslist **)arraynget(d->focusobj,0);
        if ((arraynum(d->focusobj)==1)
        && ((inst=chkobjinstoid(focus->obj,focus->oid))!=NULL)) {
          _exeobj(focus->obj,"bbox",inst,0,NULL);
          _getobj(focus->obj,"bbox",inst,&abbox);
          bboxnum=arraynum(abbox);
          bbox=(int *)arraydata(abbox);
          for (j=4;j<bboxnum;j+=2) {
            x1=mxd2p(bbox[j]*zoom+menulocal.LeftMargin)
              -d->hscroll+d->cx;
            y1=mxd2p(bbox[j+1]*zoom+menulocal.TopMargin)
              -d->vscroll+d->cy;
            if ((x1-3<=point.x) && (point.x<=x1+3)
             && (y1-3<=point.y) && (point.y<=y1+3)) break;
          }
          if (j<bboxnum) {
            d->MouseMode=MOUSECHANGE;
            d->ChangePoint=(j-4)/2;
            ShowFocusFrame(dc);
            d->ShowFrame=FALSE;
            SetCursor(XC_crosshair);
            d->ShowLine=TRUE;
            d->LineX=d->LineY=0;
            ShowFocusLine(dc,d->ChangePoint);
          }
        }
      }
      if (d->MouseMode==MOUSENONE) {
        if ((minx<=point.x) && (point.x<=maxx)
         && (miny<=point.y) && (point.y<=maxy)) d->MouseMode=MOUSEDRAG;
        else {
          ShowFocusFrame(dc);
          d->ShowFrame=FALSE;
          arraydel2(d->focusobj);
        }
      } else if ((MOUSEZOOM1<=d->MouseMode) && (d->MouseMode<=MOUSEZOOM4)) {
        ShowFocusFrame(dc);
        d->ShowFrame=FALSE;
        d->MouseDX=d->RefX2-d->MouseX1;
        d->MouseDY=d->RefY2-d->MouseY1;
        vx1=d->MouseX1;
        vy1=d->MouseY1;
        vx1-=d->RefX1-d->MouseDX;
        vy1-=d->RefY1-d->MouseDY;
        vx2=(d->RefX2-d->RefX1);
        vy2=(d->RefY2-d->RefY1);
        cc=vx1*vx2+vy1*vy2;
        nn=vx2*vx2+vy2*vy2;
        if ((nn==0) || (cc<0)) {
          zoom2=0;
        } else {
          zoom2=cc/nn;
        }
        CheckGrid(FALSE,state,NULL,NULL,&zoom2);
        SetZoom(zoom2);
        vx1=d->RefX1+vx2*zoom2;
        vy1=d->RefY1+vy2*zoom2;
        d->MouseX1=d->RefX1;
        d->MouseY1=d->RefY1;
        d->MouseX2=vx1;
        d->MouseY2=vy1;
        ShowFrameRect(dc);
        d->ShowRect=TRUE;
      }
    }
    if (arraynum(d->focusobj)==0) {
      d->MouseMode=MOUSEPOINT;
      d->ShowRect=TRUE;
    }
  } else if ((d->Mode==TrimB) || (d->Mode==DataB) || (d->Mode==EvalB)) {
    d->Capture=TRUE;
    d->MouseMode=MOUSEPOINT;
    d->ShowRect=TRUE;
  } else if ((d->Mode==MarkB) || (d->Mode==TextB)) {
    if (!d->Capture) {
      x1=d->MouseX1;
      y1=d->MouseY1;
      CheckGrid(TRUE,state,&x1,&y1,NULL);
      if ((po=(struct pointslist *)memalloc(sizeof(struct pointslist)))
          !=NULL) {
         po->x=x1;
         po->y=y1;
         arrayadd(d->points,&po);
      }
      ShowPoints(dc);
      d->Capture=TRUE;
    }
  } else if (d->Mode==ZoomB) {
    if (state & ShiftMask) {
      d->hscroll-=(d->cx-point.x);
      d->vscroll-=(d->cy-point.y);
      ChangeDPI(TRUE);
    } else if (getobj(menulocal.obj,"dpi",0,0,NULL,&vdpi)!=-1) {
      if (state & ControlMask) {
        if ((int )(vdpi/sqrt(2))>=20) {
          vdpi=(int )(vdpi/sqrt(2));
          if (putobj(menulocal.obj,"dpi",0,&vdpi)!=-1) {
            d->hscroll-=(d->cx-point.x);
            d->vscroll-=(d->cy-point.y);
            ChangeDPI(FALSE);
          }
        } else MessageBeep(TopLevel);
      } else {
        if ((int )(vdpi*sqrt(2))<=620) {
          vdpi=(int )(vdpi*sqrt(2));
          if (putobj(menulocal.obj,"dpi",0,&vdpi)!=-1) {
            d->hscroll-=(d->cx-point.x);
            d->vscroll-=(d->cy-point.y);
            ChangeDPI(FALSE);
          }
        } else MessageBeep(TopLevel);
      }
    }
  } else {
    if (!(d->Capture)) {
      x1=d->MouseX1;
      y1=d->MouseY1;
      CheckGrid(TRUE,state,&x1,&y1,NULL);
      if ((po=(struct pointslist *)memalloc(sizeof(struct pointslist)))
          !=NULL) {
         po->x=x1;
         po->y=y1;
         arrayadd(d->points,&po);
      }
      if ((po=(struct pointslist *)memalloc(sizeof(struct pointslist)))
          !=NULL) {
         po->x=x1;
         po->y=y1;
         arrayadd(d->points,&po);
      }
      ShowPoints(dc);
      d->Capture=TRUE;
    }
  }
  XFreeGC(Disp,dc);
}

void ViewerEvLButtonUp(unsigned int state,TPoint point,struct Viewer *d)
{
  int x1,y1,x2,y2,err;
  GC dc;
  int i,num,dx,dy;
  struct focuslist *focus;
  struct objlist *obj;
  char *inst;
  char *argv[4];
  struct pointslist *po;
  int vx1,vx2,vy1,vy2;
  double cc,nn,zoom,zoom2;
  int zm;
  int axis;

  if (menulock || globallock) return;
  dc=XCreateGC(Disp,d->win,0,0);
  zoom=menulocal.PaperZoom/10000.0;
  axis=FALSE;
  if (d->Capture) {
    if ((d->Mode==PointB) || (d->Mode==LegendB) || (d->Mode==AxisB)
     || (d->Mode==TrimB) || (d->Mode==DataB) || (d->Mode==EvalB)) {
      if (d->MouseMode==MOUSEDRAG) {
        d->Capture=FALSE;
        if ((d->MouseX1!=d->MouseX2) || (d->MouseY1!=d->MouseY2)) {
          ShowFocusFrame(dc);
          d->ShowFrame=FALSE;
          d->MouseX2=(mxp2d(point.x+d->hscroll-d->cx)
                    -menulocal.LeftMargin)/zoom;
          d->MouseY2=(mxp2d(point.y+d->vscroll-d->cy)
                    -menulocal.TopMargin)/zoom;
          dx=d->MouseX2-d->MouseX1;
          dy=d->MouseY2-d->MouseY1;
          CheckGrid(FALSE,state,&dx,&dy,NULL);
          argv[0]=(char *)&dx;
          argv[1]=(char *)&dy;
          argv[2]=NULL;
          num=arraynum(d->focusobj);
          PaintLock=TRUE;
          for (i=num-1;i>=0;i--) {
            focus=*(struct focuslist **)arraynget(d->focusobj,i);
            obj=focus->obj;
            if (obj==chkobject("axis")) axis=TRUE;
            if ((inst=chkobjinstoid(focus->obj,focus->oid))!=NULL) {
              AddInvalidateRect(obj,inst);
              _exeobj(obj,"move",inst,2,argv);
              NgraphApp.Changed=TRUE;
              AddInvalidateRect(obj,inst);
            }
          }
          PaintLock=FALSE;
          d->FrameOfsX=d->FrameOfsY=0;
          ShowFocusFrame(dc);
          d->ShowFrame=TRUE;
          if ((d->Mode==LegendB) || ((d->Mode==PointB) && (!axis)))
            d->allclear=FALSE;
          UpdateAll();
        }
        d->MouseMode=MOUSENONE;
      } else if ((MOUSEZOOM1<=d->MouseMode) && (d->MouseMode<=MOUSEZOOM4)) {
        d->Capture=FALSE;
        ShowFrameRect(dc);
        d->ShowRect=FALSE;
        vx1=(mxp2d(point.x-d->cx+d->hscroll)
           -menulocal.LeftMargin)/zoom;
        vy1=(mxp2d(point.y-d->cy+d->vscroll)
           -menulocal.TopMargin)/zoom;
        vx1-=d->RefX1-d->MouseDX;
        vy1-=d->RefY1-d->MouseDY;
        vx2=(d->RefX2-d->RefX1);
        vy2=(d->RefY2-d->RefY1);
        cc=vx1*vx2+vy1*vy2;
        nn=vx2*vx2+vy2*vy2;
        if ((nn==0) || (cc<0)) {
          zoom2=0;
        } else {
          zoom2=cc/nn;
        }
        if ((d->Mode!=DataB) && (d->Mode!=EvalB))
          CheckGrid(FALSE,state,NULL,NULL,&zoom2);
        zm=nround(zoom2*10000);
        ResetZoom();
        argv[0]=(char *)&zm;
        argv[1]=(char *)&(d->RefX1);
        argv[2]=(char *)&(d->RefY1);
        argv[3]=NULL;
        num=arraynum(d->focusobj);
        PaintLock=TRUE;
        for (i=num-1;i>=0;i--) {
          focus=*(struct focuslist **)arraynget(d->focusobj,i);
          obj=focus->obj;
          if (obj==chkobject("axis")) axis=TRUE;
          if ((inst=chkobjinstoid(focus->obj,focus->oid))!=NULL) {
            AddInvalidateRect(obj,inst);
            _exeobj(obj,"zooming",inst,3,argv);
            NgraphApp.Changed=TRUE;
            AddInvalidateRect(obj,inst);
          }
        }
        PaintLock=FALSE;
        d->FrameOfsX=d->FrameOfsY=0;
        d->ShowFrame=TRUE;
        ShowFocusFrame(dc);
        if ((d->Mode==LegendB) || ((d->Mode==PointB) && (!axis)))
          d->allclear=FALSE;
        UpdateAll();
        SetCursor(XC_left_ptr);
        d->MouseMode=MOUSENONE;
      } else if (d->MouseMode==MOUSECHANGE) {
        d->Capture=FALSE;
        ShowFocusLine(dc,d->ChangePoint);
        d->ShowLine=FALSE;
        if ((d->MouseX1!=d->MouseX2) || (d->MouseY1!=d->MouseY2)) {
          d->MouseX2=(mxp2d(point.x+d->hscroll-d->cx)
                    -menulocal.LeftMargin)/zoom;
          d->MouseY2=(mxp2d(point.y+d->vscroll-d->cy)
                    -menulocal.TopMargin)/zoom;
          dx=d->MouseX2-d->MouseX1;
          dy=d->MouseY2-d->MouseY1;
          if ((d->Mode!=DataB) && (d->Mode!=EvalB))
            CheckGrid(FALSE,state,&dx,&dy,NULL);
          argv[0]=(char *)&(d->ChangePoint);
          argv[1]=(char *)&dx;
          argv[2]=(char *)&dy;
          argv[3]=NULL;
          focus=*(struct focuslist **)arraynget(d->focusobj,0);
          obj=focus->obj;
          if (obj==chkobject("axis")) axis=TRUE;
          PaintLock=TRUE;
          if ((inst=chkobjinstoid(focus->obj,focus->oid))!=NULL) {
            AddInvalidateRect(obj,inst);
            _exeobj(obj,"change",inst,3,argv);
            NgraphApp.Changed=TRUE;
            AddInvalidateRect(obj,inst);
          }
          PaintLock=FALSE;
          d->FrameOfsX=d->FrameOfsY=0;
          d->ShowFrame=TRUE;
          ShowFocusFrame(dc);
          if ((d->Mode==LegendB) || ((d->Mode==PointB) && (!axis)))
            d->allclear=FALSE;
          UpdateAll();
        } else {
          d->FrameOfsX=d->FrameOfsY=0;
          d->ShowFrame=TRUE;
          ShowFocusFrame(dc);
        }
        SetCursor(XC_left_ptr);
        d->MouseMode=MOUSENONE;
      } else if (d->MouseMode==MOUSEPOINT) {
        d->Capture=FALSE;
        ShowFrameRect(dc);
        d->ShowRect=FALSE;
        d->MouseX2=(mxp2d(point.x+d->hscroll-d->cx)
                  -menulocal.LeftMargin)/zoom;
        d->MouseY2=(mxp2d(point.y+d->vscroll-d->cy)
                  -menulocal.TopMargin)/zoom;
        x1=d->MouseX1;
        y1=d->MouseY1;
        x2=d->MouseX2;
        y2=d->MouseY2;
        err=mxp2d(2)/zoom;
        if (d->Mode==PointB) {
          Match("legend",x1,y1,x2,y2,err);
          Match("axis",x1,y1,x2,y2,err);
          Match("merge",x1,y1,x2,y2,err);
          d->FrameOfsX=d->FrameOfsY=0;
          d->ShowFrame=TRUE;
          ShowFocusFrame(dc);
        } else if (d->Mode==LegendB) {
          Match("legend",x1,y1,x2,y2,err);
          Match("merge",x1,y1,x2,y2,err);
          d->FrameOfsX=d->FrameOfsY=0;
          d->ShowFrame=TRUE;
          ShowFocusFrame(dc);
        } else if (d->Mode==AxisB) {
          Match("axis",x1,y1,x2,y2,err);
          d->FrameOfsX=d->FrameOfsY=0;
          d->ShowFrame=TRUE;
          ShowFocusFrame(dc);
        } else if (d->Mode==TrimB) {
          Trimming(x1,y1,x2,y2);
        } else if (d->Mode==DataB) {
          if (ViewerWinFileUpdate(x1,y1,x2,y2,err)) UpdateAll();
        } else {
          Evaluate(x1,y1,x2,y2,err);
        }
      }
      d->MouseMode=MOUSENONE;
    } else if ((d->Mode==MarkB) || (d->Mode==TextB)) {
      d->Capture=FALSE;
      ShowPoints(dc);
      d->MouseX1=(mxp2d(point.x+d->hscroll-d->cx)
                -menulocal.LeftMargin)/zoom;
      d->MouseY1=(mxp2d(point.y+d->vscroll-d->cy)
                -menulocal.TopMargin)/zoom;
      x1=d->MouseX1;
      y1=d->MouseY1;
      CheckGrid(TRUE,state,&x1,&y1,NULL);
      num=arraynum(d->points);
      if (num>=1) {
        po=*(struct pointslist **)arraynget(d->points,0);
        po->x=x1;
        po->y=y1;
      }
      ShowPoints(dc);
      if (arraynum(d->points)==1) {
        ViewerEvLButtonDblClk(state,point,d);
      }
    } else if ((d->Mode!=MarkB) && (d->Mode!=TextB)) {
      ShowPoints(dc);
      d->MouseX1=(mxp2d(point.x+d->hscroll-d->cx)
                -menulocal.LeftMargin)/zoom;
      d->MouseY1=(mxp2d(point.y+d->vscroll-d->cy)
                -menulocal.TopMargin)/zoom;
      x1=d->MouseX1;
      y1=d->MouseY1;
      CheckGrid(TRUE,state,&x1,&y1,NULL);
      num=arraynum(d->points);
      if (num>=2) po=*(struct pointslist **)arraynget(d->points,num-2);
      if ((num<2) || (po->x!=x1) || (po->y!=y1)) {
        if ((po=(struct pointslist *)memalloc(sizeof(struct pointslist)))
           !=NULL) {
          po->x=x1;
          po->y=y1;
          arrayadd(d->points,&po);
        }
      }
      ShowPoints(dc);
      if ((d->Mode==RectB) || (d->Mode==ArcB) || (d->Mode==GaussB)
      || (d->Mode==FrameB) || (d->Mode==SectionB) || (d->Mode==CrossB)
      || (d->Mode==SingleB)) {
        if (arraynum(d->points)==3) {
          d->Capture=FALSE;
          ViewerEvLButtonDblClk(state,point,d);
        }
      }
    }
  }
  XFreeGC(Disp,dc);
}

void ViewerEvLButtonDblClk(unsigned int state,TPoint point,struct Viewer *d)
{
  int i,id,num;
  struct objlist *obj=NULL,*obj2;
  char *inst;
  int ret=IDCANCEL;
  GC dc;
  int x1,y1,x2,y2,x,y,rx,ry;
  struct pointslist *po,**pdata;
  struct narray *parray;
  int idx,idy,idu,idr,idg,lenx,leny,oidx,oidy;
  double fx1,fy1;
  int dir;
  struct narray group;
  int type;
  char *argv[2];
  char *ref;

  if (menulock || globallock) return;
  if ((d->Mode==PointB) || (d->Mode==LegendB) || (d->Mode==AxisB)) {
    d->Capture=FALSE;
    ViewUpdate();
  } else if ((d->Mode!=TrimB) && (d->Mode!=DataB) && (d->Mode!=EvalB)) {
    dc=XCreateGC(Disp,d->win,0,0);
    if ((d->Mode==MarkB) || (d->Mode==TextB)) {
      d->Capture=FALSE;
      num=arraynum(d->points);
      if (d->Mode==MarkB) obj=chkobject("mark");
      else obj=chkobject("text");
      if (obj!=NULL) {
        if ((id=newobj(obj))>=0) {
          if (num>=1) {
            po=*(struct pointslist **)arraynget(d->points,0);
            x1=po->x;
            y1=po->y;
          }
          inst=chkobjinst(obj,id);
          _putobj(obj,"x",inst,&x1);
          _putobj(obj,"y",inst,&y1);
          PaintLock=TRUE;
          if (d->Mode==MarkB) {
            LegendMarkDialog(&DlgLegendMark,obj,id);
            ret=DialogExecute(TopLevel,&DlgLegendMark);
          } else {
            LegendTextDialog(&DlgLegendText,obj,id);
            ret=DialogExecute(TopLevel,&DlgLegendText);
          }
          if ((ret==IDDELETE) || (ret==IDCANCEL)) delobj(obj,id);
          else {
            AddList(obj,inst);
            AddInvalidateRect(obj,inst);
            NgraphApp.Changed=TRUE;
          }
          PaintLock=FALSE;
        }
      }
      ShowPoints(dc);
      arraydel2(d->points);
      d->allclear=FALSE;
    } else if ((d->Mode==LineB) || (d->Mode==CurveB) || (d->Mode==PolyB)) {
      d->Capture=FALSE;
      num=arraynum(d->points);
      if (num>=3) {
        if (d->Mode==LineB) obj=chkobject("line");
        else if (d->Mode==CurveB) obj=chkobject("curve");
        else if (d->Mode==PolyB) obj=chkobject("polygon");
        if (obj!=NULL) {
          if ((id=newobj(obj))>=0) {
            inst=chkobjinst(obj,id);
            parray=arraynew(sizeof(int));
            for (i=0;i<num-1;i++) {
              po=*(struct pointslist **)arraynget(d->points,i);
              arrayadd(parray,&(po->x));
              arrayadd(parray,&(po->y));
            }
            _putobj(obj,"points",inst,parray);
            PaintLock=TRUE;
            if (d->Mode==LineB) {
              LegendArrowDialog(&DlgLegendArrow,obj,id);
              ret=DialogExecute(TopLevel,&DlgLegendArrow);
            } else if (d->Mode==CurveB) {
              LegendCurveDialog(&DlgLegendCurve,obj,id);
              ret=DialogExecute(TopLevel,&DlgLegendCurve);
            } else if (d->Mode==PolyB) {
              LegendPolyDialog(&DlgLegendPoly,obj,id);
              ret=DialogExecute(TopLevel,&DlgLegendPoly);
            }
            if ((ret==IDDELETE) || (ret==IDCANCEL)) delobj(obj,id);
            else {
              AddList(obj,inst);
              AddInvalidateRect(obj,inst);
              NgraphApp.Changed=TRUE;
            }
            PaintLock=FALSE;
          }
        }
      }
      ShowPoints(dc);
      arraydel2(d->points);
      d->allclear=FALSE;
    } else if ((d->Mode==RectB) || (d->Mode==ArcB)) {
      d->Capture=FALSE;
      num=arraynum(d->points);
      pdata=(struct pointslist **)arraydata(d->points);
      if (num>=3) {
        if (d->Mode==RectB) obj=chkobject("rectangle");
        else if (d->Mode==ArcB) obj=chkobject("arc");
        if (obj!=NULL) {
          if ((id=newobj(obj))>=0) {
            inst=chkobjinst(obj,id);
            x1=pdata[0]->x;
            y1=pdata[0]->y;
            x2=pdata[1]->x;
            y2=pdata[1]->y;
            PaintLock=TRUE;
            if (d->Mode==RectB) {
              _putobj(obj,"x1",inst,&x1);
              _putobj(obj,"y1",inst,&y1);
              _putobj(obj,"x2",inst,&x2);
              _putobj(obj,"y2",inst,&y2);
              LegendRectDialog(&DlgLegendRect,obj,id);
              ret=DialogExecute(TopLevel,&DlgLegendRect);
            } else if (d->Mode==ArcB) {
              x=(x1+x2)/2;
              y=(y1+y2)/2;
              rx=abs(x1-x);
              ry=abs(y1-y);
              _putobj(obj,"x",inst,&x);
              _putobj(obj,"y",inst,&y);
              _putobj(obj,"rx",inst,&rx);
              _putobj(obj,"ry",inst,&ry);
              LegendArcDialog(&DlgLegendArc,obj,id);
              ret=DialogExecute(TopLevel,&DlgLegendArc);
            }
            if ((ret==IDDELETE) || (ret==IDCANCEL)) delobj(obj,id);
            else {
              AddList(obj,inst);
              AddInvalidateRect(obj,inst);
              NgraphApp.Changed=TRUE;
            }
            PaintLock=FALSE;
          }
        }
      }
      ShowPoints(dc);
      arraydel2(d->points);
      d->allclear=FALSE;
    } else if (d->Mode==GaussB) {
      d->Capture=FALSE;
      num=arraynum(d->points);
      pdata=(struct pointslist **)arraydata(d->points);
      if (num>=3) {
        obj=chkobject("curve");
        if (obj!=NULL) {
          if ((id=newobj(obj))>=0) {
            inst=chkobjinst(obj,id);
            x1=pdata[0]->x;
            y1=pdata[0]->y;
            x2=pdata[1]->x;
            y2=pdata[1]->y;
            if (x1>x2) {
              x=x1; x1=x2; x2=x;
            }
            if (y1>y2) {
              y=y1; y1=y2; y2=y;
            }
            PaintLock=TRUE;
            if ((x1!=x2) && (y1!=y2)) {
              LegendGaussDialog(&DlgLegendGauss,obj,id,x1,y1,x2-x1,y2-y1);
              ret=DialogExecute(TopLevel,&DlgLegendGauss);
              if (ret!=IDOK) delobj(obj,id);
              else {
                AddList(obj,inst);
                AddInvalidateRect(obj,inst);
                NgraphApp.Changed=TRUE;
              }
            }
            PaintLock=FALSE;
          }
        }
      }
      ShowPoints(dc);
      arraydel2(d->points);
      d->allclear=FALSE;
    } else if (d->Mode==SingleB) {
      d->Capture=FALSE;
      num=arraynum(d->points);
      pdata=(struct pointslist **)arraydata(d->points);
      if (num>=3) {
        obj=chkobject("axis");
        if (obj!=NULL) {
          if ((id=newobj(obj))>=0) {
            inst=chkobjinst(obj,id);
            x1=pdata[0]->x;
            y1=pdata[0]->y;
            x2=pdata[1]->x;
            y2=pdata[1]->y;
            fx1=x2-x1;
            fy1=y2-y1;
            lenx=nround(sqrt(fx1*fx1+fy1*fy1));
            if (fx1==0) {
              if (fy1>=0) dir=27000;
              else dir=9000;
            } else {
              dir=nround(atan(-fy1/fx1)/MPI*18000);
              if (fx1<0) dir+=18000;
              if (dir<0) dir+=36000;
              if (dir>=36000) dir-=36000;
            }
            inst=chkobjinst(obj,id);
            _putobj(obj,"x",inst,&x1);
            _putobj(obj,"y",inst,&y1);
            _putobj(obj,"length",inst,&lenx);
            _putobj(obj,"direction",inst,&dir);
            AxisDialog(&DlgAxis,obj,id,TRUE);
            ret=DialogExecute(TopLevel,&DlgAxis);
            if ((ret==IDDELETE) || (ret==IDCANCEL)) {
              delobj(obj,id);
            } else NgraphApp.Changed=TRUE;
            AddList(obj,inst);
          }
        }
      }
      ShowPoints(dc);
      arraydel2(d->points);
      d->allclear=TRUE;
    } else if ((d->Mode==FrameB) || (d->Mode==SectionB) || (d->Mode==CrossB)) {
      d->Capture=FALSE;
      num=arraynum(d->points);
      pdata=(struct pointslist **)arraydata(d->points);
      if (num>=3) {
        obj=chkobject("axis");
        obj2=chkobject("axisgrid");
        if (obj!=NULL) {
          x1=pdata[0]->x;
          y1=pdata[0]->y;
          x2=pdata[1]->x;
          y2=pdata[1]->y;
          lenx=abs(x1-x2);
          leny=abs(y1-y2);
          x1=(x1<x2)?x1:x2;
          y1=(y1>y2)?y1:y2;
          idx=newobj(obj);
          idy=newobj(obj);
          if (d->Mode!=CrossB) {
            idu=newobj(obj);
            idr=newobj(obj);
            arrayinit(&group,sizeof(int));
            if (d->Mode==FrameB) type=1;
            else type=2;
            arrayadd(&group,&type);
            arrayadd(&group,&idx);
            arrayadd(&group,&idy);
            arrayadd(&group,&idu);
            arrayadd(&group,&idr);
            arrayadd(&group,&x1);
            arrayadd(&group,&y1);
            arrayadd(&group,&lenx);
            arrayadd(&group,&leny);
            argv[0]=(char *)&group;
            argv[1]=NULL;
            exeobj(obj,"default_grouping",idr,1,argv);
            arraydel(&group);
          } else {
            arrayinit(&group,sizeof(int));
            type=3;
            arrayadd(&group,&type);
            arrayadd(&group,&idx);
            arrayadd(&group,&idy);
            arrayadd(&group,&x1);
            arrayadd(&group,&y1);
            arrayadd(&group,&lenx);
            arrayadd(&group,&leny);
            argv[0]=(char *)&group;
            argv[1]=NULL;
            exeobj(obj,"default_grouping",idx,1,argv);
            arraydel(&group);
          }
          if ((d->Mode==SectionB) && (obj2!=NULL)) {
            idg=newobj(obj2);
            if (idg>=0) {
              getobj(obj,"oid",idx,0,NULL,&oidx);
              if ((ref=(char *)memalloc(15))!=NULL) {
                sprintf(ref,"axis:^%d",oidx);
                putobj(obj2,"axis_x",idg,ref);
              }
              getobj(obj,"oid",idy,0,NULL,&oidy);
              if ((ref=(char *)memalloc(15))!=NULL) {
                sprintf(ref,"axis:^%d",oidy);
                putobj(obj2,"axis_y",idg,ref);
              }
            }
          } else idg=-1;
          if (d->Mode==FrameB) {
            SectionDialog(&DlgSection,x1,y1,lenx,leny,obj,idx,
                          idy,idu,idr,obj2,&idg,FALSE);
            ret=DialogExecute(TopLevel,&DlgSection);
          } else if (d->Mode==SectionB) {
            SectionDialog(&DlgSection,x1,y1,lenx,leny,obj,idx,
                          idy,idu,idr,obj2,&idg,TRUE);
            ret=DialogExecute(TopLevel,&DlgSection);
          } else if (d->Mode==CrossB) {
            CrossDialog(&DlgCross,x1,y1,lenx,leny,obj,idx,idy);
            ret=DialogExecute(TopLevel,&DlgCross);
          }
          if ((ret==IDDELETE) || (ret==IDCANCEL)) {
            if (d->Mode!=CrossB) {
              delobj(obj,idr);
              delobj(obj,idu);
            }
            delobj(obj,idy);
            delobj(obj,idx);
            if ((idg!=-1) && (obj2!=NULL)) delobj(obj2,idg);
          } else {
            if ((inst=chkobjinst(obj,idx))!=NULL) AddList(obj,inst);
            if ((inst=chkobjinst(obj,idy))!=NULL) AddList(obj,inst);
            if (d->Mode!=CrossB) {
              if ((inst=chkobjinst(obj,idu))!=NULL) AddList(obj,inst);
              if ((inst=chkobjinst(obj,idr))!=NULL) AddList(obj,inst);
            }
            if ((idg!=-1) && (obj2!=NULL)) {
              if ((inst=chkobjinst(obj2,idg))!=NULL) AddList(obj2,inst);
            }
            NgraphApp.Changed=TRUE;
          }
        }
      }
      ShowPoints(dc);
      arraydel2(d->points);
      d->allclear=TRUE;
    }
    XFreeGC(Disp,dc);
    UpdateAll();
  }
}

void ViewerEvRButtonDown(unsigned int state,TPoint point,struct Viewer *d,
                         XButtonPressedEvent *event)
{
  GC dc;
  int num;
  struct pointslist *po;
  double zoom;
  int vdpi;
  struct focuslist *focus;
  struct objlist *obj;

  if (menulock || globallock) return;
  if (d->MoveData) {
    arraydel(&sellist);
    d->MoveData=FALSE;
    d->Capture=FALSE;
    SetCursor(XC_left_ptr);
    MessageBox(TopLevel,"Moving data points is canceled.","Confirm",MB_OK);
  } else if (d->Capture
  && ((d->Mode==LineB) || (d->Mode==CurveB) || (d->Mode==PolyB))) {
    zoom=menulocal.PaperZoom/10000.0;
    dc=XCreateGC(Disp,d->win,0,0);
    num=arraynum(d->points);
    if (num>0) {
      ShowPoints(dc);
      arrayndel2(d->points,num-1);
      if (num<=2) {
        arraydel2(d->points);
        d->Capture=FALSE;
      } else {
        po=*(struct pointslist **)arraylast(d->points);
        if (po!=NULL) {
          d->MouseX1=(mxp2d(d->hscroll+point.x-d->cx)
                    -menulocal.LeftMargin)/zoom;
          d->MouseY1=(mxp2d(d->vscroll+point.y-d->cy)
                    -menulocal.TopMargin)/zoom;
          po->x=d->MouseX1;
          po->y=d->MouseY1;
          CheckGrid(TRUE,state,&(po->x),&(po->y),NULL);
        }
        ShowPoints(dc);
      }
    }
    XFreeGC(Disp,dc);
  } else if (d->Mode==ZoomB) {
    if (state & ShiftMask) {
      d->hscroll-=(d->cx-point.x);
      d->vscroll-=(d->cy-point.y);
      ChangeDPI(TRUE);
    } else if (getobj(menulocal.obj,"dpi",0,0,NULL,&vdpi)!=-1) {
      if (state & ControlMask) {
        if ((int )(vdpi*sqrt(2))<=620) {
          vdpi=(int )(vdpi*sqrt(2));
          if (putobj(menulocal.obj,"dpi",0,&vdpi)!=-1) {
            d->hscroll-=(d->cx-point.x);
            d->vscroll-=(d->cy-point.y);
            ChangeDPI(FALSE);
          }
        } else MessageBeep(TopLevel);
      } else {
        if ((int )(vdpi/sqrt(2))>=20) {
          vdpi=(int )(vdpi/sqrt(2));
          if (putobj(menulocal.obj,"dpi",0,&vdpi)!=-1) {
            d->hscroll-=(d->cx-point.x);
            d->vscroll-=(d->cy-point.y);
            ChangeDPI(FALSE);
          }
        } else MessageBeep(TopLevel);
      }
    }
  } else if (!(d->MoveData) && !(d->Capture) && (d->MouseMode==MOUSENONE)) {
    XtSetSensitive(XtNameToWidget(d->popup,"button_0"),False);
    XtSetSensitive(XtNameToWidget(d->popup,"button_1"),False);
    XtSetSensitive(XtNameToWidget(d->popup,"button_2"),False);
    XtSetSensitive(XtNameToWidget(d->popup,"button_3"),False);
    XtSetSensitive(XtNameToWidget(d->popup,"button_4"),False);
    if ((d->Mode==PointB) || (d->Mode==LegendB) || (d->Mode==AxisB)) {
      num=arraynum(d->focusobj);
      if (num>0) {
        XtSetSensitive(XtNameToWidget(d->popup,"button_0"),True);
        XtSetSensitive(XtNameToWidget(d->popup,"button_1"),True);
        XtSetSensitive(XtNameToWidget(d->popup,"button_2"),True);
      }
      if (num==1) {
        focus=*(struct focuslist **)arraynget(d->focusobj,0);
        obj=focus->obj;
        if (chkobjchild(chkobject("legend"),obj)) {
          XtSetSensitive(XtNameToWidget(d->popup,"button_3"),True);
          XtSetSensitive(XtNameToWidget(d->popup,"button_4"),True);
        }
      }  
    }
    XmMenuPosition(d->popup,event);
    XtManageChild(d->popup);
  }
}

void ViewerEvMButtonDown(unsigned int state,TPoint point,struct Viewer *d)
{
  if (menulock || globallock) return;
  if (d->Mode==ZoomB) {
    d->hscroll-=(d->cx-point.x);
    d->vscroll-=(d->cy-point.y);
    ChangeDPI(TRUE);
  } else {
    ViewerEvLButtonDown(state,point,d);
    ViewerEvLButtonUp(state,point,d);
    ViewerEvLButtonDblClk(state,point,d);
  }
}

void ViewerEvMouseMove(unsigned int state,TPoint point,struct Viewer *d)
{
  GC dc;
  struct pointslist *po;
  int x,y,dx,dy,vx1,vx2,vy1,vy2;
  double cc,nn;
  double zoom,zoom2;

  if (menulock || globallock) return;
  zoom=menulocal.PaperZoom/10000.0;
  dx=(mxp2d(point.x+d->hscroll-d->cx)-menulocal.LeftMargin)/zoom;
  dy=(mxp2d(point.y+d->vscroll-d->cy)-menulocal.TopMargin)/zoom;
  if ((d->Mode!=DataB) && (d->Mode!=EvalB) && (d->Mode!=ZoomB) &&
      (d->MouseMode!=MOUSEPOINT)
  && (((d->Mode!=PointB) && (d->Mode!=LegendB) && (d->Mode!=AxisB))
  || (d->MouseMode!=MOUSENONE)))
    CheckGrid(TRUE,state,&dx,&dy,NULL);
  CoordWinSetCoord(dx,dy);
  SetPoint(dx,dy);
  dc=XCreateGC(Disp,d->win,0,0);
  if (region==NULL) {
    if (d->ShowCross) ShowCrossGauge(dc);
    d->CrossX=dx;
    d->CrossY=dy;
    if (d->ShowCross) ShowCrossGauge(dc);
  }
  if (d->Capture) {
    if ((d->Mode==PointB) || (d->Mode==LegendB) || (d->Mode==AxisB)
     || (d->Mode==TrimB) || (d->Mode==DataB) || (d->Mode==EvalB)) {
      if (d->MouseMode==MOUSEDRAG) {
        ShowFocusFrame(dc);
        d->MouseX2=(mxp2d(point.x+d->hscroll-d->cx)
                  -menulocal.LeftMargin)/zoom;
        d->MouseY2=(mxp2d(point.y+d->vscroll-d->cy)
                  -menulocal.TopMargin)/zoom;
        x=d->MouseX2-d->MouseX1;
        y=d->MouseY2-d->MouseY1;
        CheckGrid(FALSE,state,&x,&y,NULL);
        d->FrameOfsX=x;
        d->FrameOfsY=y;
        ShowFocusFrame(dc);
      } else if ((MOUSEZOOM1<=d->MouseMode) && (d->MouseMode<=MOUSEZOOM4)) {
        ShowFrameRect(dc);
        vx1=(mxp2d(point.x-d->cx+d->hscroll)
            -menulocal.LeftMargin)/zoom;
        vy1=(mxp2d(point.y-d->cy+d->vscroll)
            -menulocal.TopMargin)/zoom;
        vx1-=d->RefX1-d->MouseDX;
        vy1-=d->RefY1-d->MouseDY; 
        vx2=(d->RefX2-d->RefX1);
        vy2=(d->RefY2-d->RefY1);
        cc=vx1*vx2+vy1*vy2;
        nn=vx2*vx2+vy2*vy2;
        if ((nn==0) || (cc<0)) {
          zoom2=0;
        } else {
          zoom2=cc/nn;
        }
        if ((d->Mode!=DataB) && (d->Mode!=EvalB))
          CheckGrid(FALSE,state,NULL,NULL,&zoom2);
        SetZoom(zoom2);
        vx1=d->RefX1+vx2*zoom2;
        vy1=d->RefY1+vy2*zoom2;
        d->MouseX1=d->RefX1;
        d->MouseY1=d->RefY1;
        d->MouseX2=vx1;
        d->MouseY2=vy1;
        ShowFrameRect(dc);
      } else if (d->MouseMode==MOUSECHANGE) {
        ShowFocusLine(dc,d->ChangePoint);
        d->MouseX2=(mxp2d(point.x+d->hscroll-d->cx)
                  -menulocal.LeftMargin)/zoom;
        d->MouseY2=(mxp2d(point.y+d->vscroll-d->cy)
                  -menulocal.TopMargin)/zoom;
        x=d->MouseX2-d->MouseX1;
        y=d->MouseY2-d->MouseY1;
        if ((d->Mode!=DataB) && (d->Mode!=EvalB))
          CheckGrid(FALSE,state,&x,&y,NULL);
        d->LineX=x;
        d->LineY=y;
        ShowFocusLine(dc,d->ChangePoint);
      } else if (d->MouseMode==MOUSEPOINT) {
        ShowFrameRect(dc);
        d->MouseX2=(mxp2d(point.x+d->hscroll-d->cx)
                  -menulocal.LeftMargin)/zoom;
        d->MouseY2=(mxp2d(point.y+d->vscroll-d->cy)
                  -menulocal.TopMargin)/zoom;
        ShowFrameRect(dc);
      }
    } else {
      ShowPoints(dc);
      if (arraynum(d->points)!=0) {
        po=*(struct pointslist **)arraylast(d->points);
        if (po!=NULL) {
          po->x=dx;
          po->y=dy;
        }
      }
      ShowPoints(dc);
    }
  }
  XFreeGC(Disp,dc);
}

void ViewerEvMouseMotion(Widget w,XtPointer client_data,XEvent *event,
                         Boolean *dispatch)
{
  struct Viewer *d;
  XMotionEvent *e;
  TPoint point;

  d=(struct Viewer *)client_data;
  e=(XMotionEvent *)event;
  point.x=e->x;
  point.y=e->y;
  ViewerEvMouseMove(e->state,point,d);
}

Time ViewerTime=0;
TPoint ViewerPoint;

void ViewerEvButtonDown(Widget w,XtPointer client_data,XEvent *event,
                        Boolean *dispatch)
{
  struct Viewer *d;
  XButtonEvent *e;
  TPoint point;
  int dbl;

  d=(struct Viewer *)client_data;
  e=(XButtonEvent *)event;
  point.x=e->x;
  point.y=e->y;
  if (((e->time-ViewerTime)<menulocal.mouseclick)
  && (ViewerPoint.x==point.x) && (ViewerPoint.y==point.y)) dbl=TRUE;
  else dbl=FALSE;
  ViewerTime=e->time;
  ViewerPoint.x=point.x;
  ViewerPoint.y=point.y;
  if (e->button==Button1) {
    if (dbl) ViewerEvLButtonDblClk(e->state,point,d);
    else ViewerEvLButtonDown(e->state,point,d);
  } else if (e->button==Button2) {
    ViewerEvMButtonDown(e->state,point,d);
  } else if (e->button==Button3) ViewerEvRButtonDown(e->state,point,d,e);
}

void ViewerEvButtonUp(Widget w,XtPointer client_data,XEvent *event,
                      Boolean *dispatch)
{
  struct Viewer *d;
  XButtonEvent *e;
  TPoint point;

  d=(struct Viewer *)client_data;
  e=(XButtonEvent *)event;
  point.x=e->x;
  point.y=e->y;
  if (e->button==Button1) ViewerEvLButtonUp(e->state,point,d);
}

void ViewerEvKeyDown(Widget w,XtPointer client_data,XEvent *event,
                     Boolean *dispatch)
{
  struct Viewer *d;
  XKeyEvent *e;
  KeySym sym;
  GC dc;
  int dx=0,dy=0,mv;
  double zoom;
  struct objlist *obj;
  int num;
  struct focuslist *focus;

  if (menulock || globallock) return;
  d=(struct Viewer *)client_data;
  e=(XKeyEvent *)event;
  sym=XKeycodeToKeysym(Disp,e->keycode,0);
  switch (sym) {
  case XK_space:
    CmViewerDrawB(NULL,NULL,NULL);
    break;
  case XK_Delete:
    ViewDelete();
    break;
  case XK_Return:
    ViewUpdate();
    break;
  case XK_Insert:
    ViewCopy();
    break;
  case XK_Home:
    if (e->state & ShiftMask) ViewTop();
    break;
  case XK_End:
    if (e->state & ShiftMask) ViewLast();
    break;
  case XK_Down: case XK_Up: case XK_Left: case XK_Right:
    if (((d->MouseMode==MOUSENONE) || (d->MouseMode==MOUSEDRAG))
     && ((d->Mode==PointB) || (d->Mode==LegendB) || (d->Mode==AxisB))) {
      dc=XCreateGC(Disp,d->win,0,0);
      zoom=menulocal.PaperZoom/10000.0;
      ShowFocusFrame(dc);
      if (e->state & ShiftMask) mv=mxlocal->grid/10;
      else mv=mxlocal->grid;
      if (sym==XK_Down) {
        dx=0;
        dy=mv;
      } else if (sym==XK_Up) {
        dx=0;
        dy=-mv;
      } else if (sym==XK_Right) {
        dx=mv;
        dy=0;
      } else if (sym==XK_Left) {
        dx=-mv;
        dy=0;
      }
      d->FrameOfsX+=dx/zoom;
      d->FrameOfsY+=dy/zoom;
      ShowFocusFrame(dc);
      XFreeGC(Disp,dc);
      d->MouseMode=MOUSEDRAG;
    }
    break;
  case XK_Shift_L: case XK_Shift_R:
    if (d->Mode==ZoomB) SetCursor(XC_centering);
    break;
  case XK_Control_L: case XK_Control_R:
    if (d->Mode==ZoomB) SetCursor(XC_zoomout);
    break;
  case XK_BackSpace:
    ViewCross();
    break;
  case XK_P: case XK_p:
    if (e->state & ControlMask) {
      if (!(d->MoveData) && !(d->Capture) && (d->MouseMode==MOUSENONE)) {
        XmMenuPosition(d->popup,(XButtonPressedEvent *)event);
        XtManageChild(d->popup);
        XtSetSensitive(XtNameToWidget(d->popup,"button_0"),False);
        XtSetSensitive(XtNameToWidget(d->popup,"button_1"),False);
        XtSetSensitive(XtNameToWidget(d->popup,"button_2"),False);
        XtSetSensitive(XtNameToWidget(d->popup,"button_3"),False);
        XtSetSensitive(XtNameToWidget(d->popup,"button_4"),False);
        if ((d->Mode==PointB) || (d->Mode==LegendB) || (d->Mode==AxisB)) {
          num=arraynum(d->focusobj);
          if (num>0) {
            XtSetSensitive(XtNameToWidget(d->popup,"button_0"),True);
            XtSetSensitive(XtNameToWidget(d->popup,"button_1"),True);
            XtSetSensitive(XtNameToWidget(d->popup,"button_2"),True);
          }
          if (num==1) {
            focus=*(struct focuslist **)arraynget(d->focusobj,0);
            obj=focus->obj;
            if (chkobjchild(chkobject("legend"),obj)) {
              XtSetSensitive(XtNameToWidget(d->popup,"button_3"),True);
              XtSetSensitive(XtNameToWidget(d->popup,"button_4"),True);
            }
          }  
        }
      }
    }
    break; 
  default:
    break;
  }
}

void ViewerEvKeyUp(Widget w,XtPointer client_data,XEvent *event,
                     Boolean *dispatch)
{
  struct Viewer *d;
  XKeyEvent *e;
  KeySym sym;
  GC dc;
  int num,i,dx,dy;
  struct focuslist *focus;
  char *inst;
  struct objlist *obj;
  char *argv[4];
  int axis;

  if (menulock || globallock) return;
  d=(struct Viewer *)client_data;
  e=(XKeyEvent *)event;
  sym=XKeycodeToKeysym(Disp,e->keycode,0);
  switch (sym) {
  case XK_Shift_L: case XK_Shift_R:
  case XK_Control_L: case XK_Control_R:
    if (d->Mode==ZoomB) SetCursor(XC_zoomin);
    break;
  case XK_Down: case XK_Up: case XK_Left: case XK_Right:
    if (d->MouseMode==MOUSEDRAG) {
      dc=XCreateGC(Disp,d->win,0,0);
      ShowFocusFrame(dc);
      dx=d->FrameOfsX;
      dy=d->FrameOfsY;
      argv[0]=(char *)&dx;
      argv[1]=(char *)&dy;
      argv[2]=NULL;
      num=arraynum(d->focusobj);
      axis=FALSE;
      PaintLock=TRUE;
      for (i=num-1;i>=0;i--) {
        focus=*(struct focuslist **)arraynget(d->focusobj,i);
        obj=focus->obj;
        if (obj==chkobject("axis")) axis=TRUE;
        if ((inst=chkobjinstoid(focus->obj,focus->oid))!=NULL) {
          AddInvalidateRect(obj,inst);
          _exeobj(obj,"move",inst,2,argv);
          NgraphApp.Changed=TRUE;
          AddInvalidateRect(obj,inst);
        }
      }
      PaintLock=FALSE;
      d->FrameOfsX=d->FrameOfsY=0;
      d->ShowFrame=TRUE;
      ShowFocusFrame(dc);
      XFreeGC(Disp,dc);
      if ((d->Mode==LegendB) || ((d->Mode==PointB) && (!axis))) 
        d->allclear=FALSE;
      UpdateAll();
      d->MouseMode=MOUSENONE;
    }
    break;
  default:
    break;
  }
}

void ViewerEvSize(Widget w,XtPointer client_data,XtPointer call_data)
{
  struct Viewer *d;
  int x,y;
  unsigned int width,height,border,depth;
  Window root;

  d=&(NgraphApp.Viewer);
  XGetGeometry(XtDisplay(w),XtWindow(w),
               &root,&x,&y,&width,&height,&border,&depth);
  d->cx=width/2;
  d->cy=height/2;
  ChangeDPI(TRUE);
  d->width=width;
  d->height=height;
}

void ViewerEvPaint(Widget w,XtPointer client_data,XtPointer call_data)
{
  GC gc;
  struct mxlocal mxsave;
  struct savedstdio save;
  XmAnyCallbackStruct *dd;
  XExposeEvent *e;
  struct Viewer *d;
  XRectangle rect;
  Arg al[20];
  Cardinal ac;
  unsigned pixel;

  d=&(NgraphApp.Viewer);
  dd=(XmAnyCallbackStruct *)call_data;
  e=(XExposeEvent *)(dd->event);
  if (region==NULL) region=XCreateRegion();
  XtAddExposureToRegion(dd->event,region);
  if ((e->count!=0) || globallock) return;
  gc=XCreateGC(e->display,e->window,0,0);
  XSetRegion(e->display,gc,region);
  XClipBox(region,&rect);
  if (!PaintLock) {
    ac=0;
    XtSetArg(al[ac],XmNbackground,&pixel); ac++;
    XtGetValues(d->Win,al,ac);
    XSetForeground(e->display,gc,pixel); 
    XFillRectangle(e->display,e->window,gc,
                   rect.x,rect.y,rect.width,rect.height); 
  }
  if (chkobjinstoid(menulocal.GRAobj,menulocal.GRAoid)!=NULL) {
    mxsaveGC(gc,e->window,-d->cx+d->hscroll,-d->cy+d->vscroll,
             &mxsave,-1,region);
    MakeRuler(gc);
    if (mxlocal->autoredraw && !d->ignoreredraw) {
      ignorestdio(&save);
      mx_redraw(menulocal.obj,menulocal.inst);
      restorestdio(&save);
    }
    mxrestoreGC(&mxsave);
    XSetRegion(e->display,gc,region);
    if (d->ShowFrame) ShowFocusFrame(gc);
    ShowPoints(gc);
    if (d->ShowLine) ShowFocusLine(gc,d->ChangePoint);
    if (d->ShowRect) ShowFrameRect(gc);
    if (d->ShowCross) ShowCrossGauge(gc); 
    XFreeGC(e->display,gc);
    mxlocal->scrollx=-d->cx+d->hscroll;
    mxlocal->scrolly=-d->cy+d->vscroll;
  }
  if (!PaintLock) {
    XDestroyRegion(region);
    region=NULL;
  }
}

void ViewerEvVScroll(Widget w,XtPointer client_data,XtPointer call_data)
{
  XmScrollBarCallbackStruct *dd;
  struct Viewer *d;
  int dy;
  int x,y;
  unsigned int width,height,border,depth;
  Window root;
  GC gc;

  d=&(NgraphApp.Viewer);
  dd=(XmScrollBarCallbackStruct *)call_data;
  dy=dd->value-d->vscroll;
  if (dy!=0) {
    gc=XCreateGC(Disp,d->win,0,0);
    XGetGeometry(Disp,d->win,&root,&x,&y,&width,&height,&border,&depth);
    XCopyArea(Disp,d->win,d->win,gc,0,dy,width,height,0,0);
    XFreeGC(Disp,gc);
    if (dy>0) XClearArea(Disp,d->win,0,height-dy,width,dy,TRUE);
    else XClearArea(Disp,d->win,0,0,width,-dy,TRUE);
  }             
  d->vscroll=dd->value;
}

void ViewerEvHScroll(Widget w,XtPointer client_data,XtPointer call_data)
{
  XmScrollBarCallbackStruct *dd;
  struct Viewer *d;
  int dx;
  int x,y;
  unsigned int width,height,border,depth;
  Window root;
  GC gc;

  d=&(NgraphApp.Viewer);
  dd=(XmScrollBarCallbackStruct *)call_data;
  dx=dd->value-d->hscroll;
  if (dx!=0) {
    gc=XCreateGC(Disp,d->win,0,0);
    XGetGeometry(Disp,d->win,&root,&x,&y,&width,&height,&border,&depth);
    XCopyArea(Disp,d->win,d->win,gc,dx,0,width,height,0,0);
    XFreeGC(Disp,gc);
    if (dx>0) XClearArea(Disp,d->win,width-dx,0,dx,height,TRUE);
    else XClearArea(Disp,d->win,0,0,-dx,height,TRUE);
  }             
  d->hscroll=dd->value;
}

void ViewerWinUpdate(int clear)
{
  int i,num;
  struct focuslist **focus;
  struct Viewer *d;

  d=&(NgraphApp.Viewer);
  if (chkobjinstoid(menulocal.GRAobj,menulocal.GRAoid)==NULL) {
    CloseGC();
    CloseGRA();
    OpenGRA();
    OpenGC(); 
    SetScroller();
    ChangeDPI(TRUE);
  }
  CheckPage();
  num=arraynum(d->focusobj);
  focus=(struct focuslist **)arraydata(d->focusobj);
  for (i=num-1;i>=0;i--) {
    if (chkobjoid(focus[i]->obj,focus[i]->oid)==-1) arrayndel2(d->focusobj,i);
  }
  if (d->allclear) XClearArea(XtDisplay(d->Win),XtWindow(d->Win),0,0,0,0,TRUE);
  else if (region!=NULL) 
    XClearArea(XtDisplay(d->Win),XtWindow(d->Win),1,1,1,1,TRUE);
  d->allclear=TRUE;
}

void MakeRuler(GC gc)
{
  int Width,Height;
  int x,y,len;
  struct Viewer *d;
  int x1,y1,x2,y2;
  int minx,miny,width,height;

  d=&(NgraphApp.Viewer);
  Width=menulocal.PaperWidth;
  Height=menulocal.PaperHeight;
  XSetFunction(Disp,gc,GXcopy);
  XSetLineAttributes(Disp,gc,0,LineSolid,CapButt,JoinMiter);
  XSetForeground(Disp,gc,gray);
  x1=mxd2px(0);
  y1=mxd2py(0);
  x2=mxd2px(Width)-1;
  y2=mxd2py(Height)-1;
  minx=(x1<x2)?x1:x2;
  miny=(y1<y2)?y1:y2;
  width=abs(x2-x1);
  height=abs(y2-y1);
  XDrawRectangle(Disp,d->win,gc,minx,miny,width,height);
  if (mxlocal->ruler) {
    XSetForeground(Disp,gc,gray);
    for (x=500;x<Width;x+=500) {
      if (!(x%10000)) {
        XSetForeground(Disp,gc,red);
      }
      if (!(x%10000)) len=225;
      else if (!(x%1000)) len=150;
      else len=75;
      XDrawLine(Disp,d->win,gc,
                mxd2px(x),mxd2py(0),
                mxd2px(x),mxd2py(len));
      XDrawLine(Disp,d->win,gc,
                mxd2px(x),mxd2py(Height)-1,
                mxd2px(x),mxd2py(Height-len)-1);
      if (!(x%10000)) {
        XSetForeground(Disp,gc,gray);
      }
    }
    for (y=500;y<Height;y+=500) {
      if (!(y%10000)) {
        XSetForeground(Disp,gc,red);
      }
      if (!(y%10000)) len=225;
      else if (!(y%1000)) len=150;
      else len=75;
      XDrawLine(Disp,d->win,gc,
                mxd2px(0),mxd2py(y),
                mxd2px(len),mxd2py(y));
      XDrawLine(Disp,d->win,gc,
                mxd2px(Width)-1,mxd2py(y),
                mxd2px(Width-len)-1,mxd2py(y));
      if (!(y%10000)) {
        XSetForeground(Disp,gc,gray);
      }
    }
  }
}

void Focus(struct objlist *fobj,int id)
{
  int oid;
  char *inst;
  struct narray *sarray;
  char **sdata;
  int snum;
  struct objlist *dobj;
  int did;
  char *dfield;
  int i;
  struct focuslist *focus;
  struct savedstdio save;
  GC dc;
  int man;
  struct Viewer *d;

  d=&(NgraphApp.Viewer);
  if (chkobjchild(chkobject("legend"),fobj)) {
    CmViewerButtonArm(NgraphApp.viewb[1],NULL,NULL);
  } else if (chkobjchild(chkobject("axis"),fobj)) {
    CmViewerButtonArm(NgraphApp.viewb[10],NULL,NULL);
  } else return;
  UnFocus();
  dc=XCreateGC(Disp,d->win,0,0);
  inst=chkobjinst(fobj,id);
  _getobj(fobj,"oid",inst,&oid);
  ignorestdio(&save);
  if (fobj!=NULL) {
    if (_getobj(menulocal.obj,"_list",menulocal.inst,&sarray)) return;
    if ((snum=arraynum(sarray))==0) return;
    sdata=(char **)arraydata(sarray);
    for (i=1;i<snum;i++) {
      if (((dobj=getobjlist(sdata[i],&did,&dfield,NULL))!=NULL)
      && (fobj==dobj) && (did==oid)) {
        if ((focus=(struct focuslist *)memalloc(sizeof(struct focuslist)))
        !=NULL) {
          if (chkobjchild(chkobject("axis"),dobj)) {
            getobj(fobj,"group_manager",id,0,NULL,&man);
            getobj(fobj,"oid",man,0,NULL,&did);
          }
          focus->obj=dobj;
          focus->oid=did;
          arrayadd(d->focusobj,&focus);
          d->ShowFrame=TRUE;
          ShowFocusFrame(dc);
        }
        d->MouseMode=MOUSENONE;
        break;
      }
    }
  }
  XFreeGC(Disp,dc);
  restorestdio(&save);
}


void UnFocus()
{
  GC gc;
  struct Viewer *d;

  d=&(NgraphApp.Viewer);
  if (arraynum(d->focusobj)!=0) {
    gc=XCreateGC(Disp,d->win,0,0);
    ShowFocusFrame(gc);
    XFreeGC(Disp,gc);
    arraydel2(d->focusobj);
  }
  d->ShowFrame=FALSE;
  if (arraynum(d->points)!=0) {
    gc=XCreateGC(Disp,d->win,0,0);
    ShowPoints(gc);
    XFreeGC(Disp,gc);
    arraydel2(d->points);
  }
}

void OpenGC()
{
  int i;

  if (mxlocal->gc!=0) return;
  Disp=XtDisplay(NgraphApp.Viewer.Win);
  mxlocal->win=XtWindow(NgraphApp.Viewer.Win);
  mxlocal->gc=XCreateGC(Disp,mxlocal->win,0,0);
  mxlocal->offsetx=0;
  mxlocal->offsety=0;
  mxlocal->cpx=0;
  mxlocal->cpy=0;
  mxlocal->pixel_dot=mxlocal->windpi/25.4/100;
  mxlocal->fontalias=NULL;
  for (i=0;i<X11FONTCASH;i++) (mxlocal->font[i]).fontalias=NULL;
  mxlocal->loadfont=0;
  mxlocal->loadfontf=-1;
  mxlocal->linetonum=0; 
  mxlocal->scrollx=0;
  mxlocal->scrolly=0;
  mxlocal->region=NULL;
}

void SetScroller()
{
  int width,height,x,y;
  struct Viewer *d;

  d=&(NgraphApp.Viewer);
  width=mxd2p(menulocal.PaperWidth);
  height=mxd2p(menulocal.PaperHeight);
  x=width/2;
  y=height/2;
  XtVaSetValues(d->HScroll,XmNmaximum,width,XmNvalue,x,NULL);
  XtVaSetValues(d->VScroll,XmNmaximum,height,XmNvalue,y,NULL);
  d->hscroll=x;
  d->vscroll=y;
}

void ChangeDPI(int redraw)
{
  int width,height,i;
  double ratex,ratey;
  struct objlist *obj;
  int num;
  struct narray *array;
  char *inst;
  struct Viewer *d;
  int XPos,YPos,XRange,YRange;

  d=&(NgraphApp.Viewer);
  XtVaGetValues(d->HScroll,XmNmaximum,&XRange,NULL);
  XtVaGetValues(d->VScroll,XmNmaximum,&YRange,NULL);
  XPos=d->hscroll;
  YPos=d->vscroll;
  if (XPos<0) XPos=0;
  if (YPos<0) YPos=0;
  if (XPos>XRange) XPos=XRange;
  if (YPos>YRange) YPos=YRange;
  if (XRange==0) ratex=0;
  else ratex=XPos/(double )XRange;
  if (YRange==0) ratey=0;
  else ratey=YPos/(double )YRange;
  width=mxd2p(menulocal.PaperWidth);
  height=mxd2p(menulocal.PaperHeight);
  XPos=width*ratex;
  YPos=height*ratey;
  XtVaSetValues(d->HScroll,XmNmaximum,width,XmNvalue,XPos,NULL);
  XtVaSetValues(d->VScroll,XmNmaximum,height,XmNvalue,YPos,NULL);
  d->hscroll=XPos;
  d->vscroll=YPos;
  mxlocal->scrollx=-d->cx+XPos;
  mxlocal->scrolly=-d->cy+YPos;
  if ((obj=chkobject("text"))!=NULL) {
    num=chkobjlastinst(obj);
    for (i=0;i<=num;i++) {
      inst=chkobjinst(obj,i);
      _getobj(obj,"bbox",inst,&array);
      arrayfree(array);
      _putobj(obj,"bbox",inst,NULL);
    }
  }
  XClearArea(Disp,d->win,0,0,0,0,redraw);
}

void CloseGC()
{
  int i;

  if (mxlocal->gc==0) return;
  if (mxlocal->linetonum!=0) {
    XDrawLines(Disp,mxlocal->win,mxlocal->gc,
               mxlocal->points,mxlocal->linetonum,CoordModeOrigin);
    mxlocal->linetonum=0;
  }
  XFlush(Disp);
  XFreeGC(Disp,mxlocal->gc);
  mxlocal->gc=0;
  memfree(mxlocal->fontalias);
  for (i=0;i<X11FONTCASH;i++) memfree((mxlocal->font[i]).fontalias);
  if (mxlocal->region!=NULL) XDestroyRegion(mxlocal->region);
  mxlocal->region=NULL;
  UnFocus(); 
}

void ReopenGC()
{
  if (mxlocal->gc!=0) {
    if (mxlocal->linetonum!=0) {
      XDrawLines(Disp,mxlocal->win,mxlocal->gc,
                 mxlocal->points,mxlocal->linetonum,CoordModeOrigin);
      mxlocal->linetonum=0;
    }
    XFlush(Disp);
    XFreeGC(Disp,mxlocal->gc);
  }
  mxlocal->gc=XCreateGC(Disp,mxlocal->win,0,0);
  mxlocal->offsetx=0;
  mxlocal->offsety=0;
  mxlocal->cpx=0;
  mxlocal->cpy=0;
  mxlocal->pixel_dot=mxlocal->windpi/25.4/100;
  mxlocal->loadfontf=-1;
  mxlocal->linetonum=0; 
  if (mxlocal->region!=NULL) XDestroyRegion(mxlocal->region);
  mxlocal->region=NULL;
}

void Draw(int SelectFile)
{
  int SShowFrame,SShowLine,SShowRect,SShowCross;
  GC gc;
  struct Viewer *d;

  d=&(NgraphApp.Viewer);
  if (SelectFile && !SetFileHidden()) return;
  FitClear();
  FileAutoScale();
  AdjustAxis();
  SetStatusBar("Drawing.");
  gc=XCreateGC(Disp,d->win,0,0);
  if (d->ShowFrame) ShowFocusFrame(gc);
  if (d->ShowLine) ShowFocusLine(gc,d->ChangePoint);
  if (d->ShowRect) ShowFrameRect(gc);
  if (d->ShowCross) ShowCrossGauge(gc); 
  XFreeGC(Disp,gc);
  SShowFrame=d->ShowFrame;
  SShowLine=d->ShowLine;
  SShowRect=d->ShowRect;
  SShowCross=d->ShowCross;
  d->ShowFrame=d->ShowLine=d->ShowRect=d->ShowCross=FALSE;
  ReopenGC(); 
  if (chkobjinstoid(menulocal.GRAobj,menulocal.GRAoid)!=NULL) {
    _exeobj(menulocal.GRAobj,"clear",menulocal.GRAinst,0,NULL); 
    d->ignoreredraw=TRUE;
    XmUpdateDisplay(d->Win);
    d->ignoreredraw=FALSE;
    _exeobj(menulocal.GRAobj,"draw",menulocal.GRAinst,0,NULL);
    _exeobj(menulocal.GRAobj,"flush",menulocal.GRAinst,0,NULL); 
  }
  d->ShowFrame=SShowFrame;
  d->ShowLine=SShowLine;
  d->ShowRect=SShowRect;
  d->ShowCross=SShowCross;
  gc=XCreateGC(Disp,d->win,0,0);
  if (d->ShowFrame) ShowFocusFrame(gc);
  if (d->ShowLine) ShowFocusLine(gc,d->ChangePoint);
  if (d->ShowRect) ShowFrameRect(gc);
  if (d->ShowCross) ShowCrossGauge(gc);
  XFreeGC(Disp,gc);
  ResetStatusBar(); 
}

void Clear()
{
  if (chkobjinstoid(menulocal.GRAobj,menulocal.GRAoid)!=NULL) {
    UnFocus();
    _exeobj(menulocal.GRAobj,"clear",menulocal.GRAinst,0,NULL);
    ReopenGC();
  }
}

void CmViewerDraw()
{
  if (menulock || globallock) return;
  Draw(TRUE);
  FileWinUpdate(TRUE);
  AxisWinUpdate(TRUE);
}

void CmViewerDrawB(Widget w,XtPointer client_data,XtPointer call_data)
{

  if (menulock || globallock) return;
  Draw(FALSE);
  FileWinUpdate(TRUE);
  AxisWinUpdate(TRUE);
}

void CmViewerClear()
{
  if (menulock || globallock) return;
  Clear();
  FileWinUpdate(TRUE);
}

void CmViewerClearB(Widget w,XtPointer client_data,XtPointer call_data)
{
  CmViewerClear();
}

void ViewUpdate()
{
  int i,id,id2,did,num;
  struct focuslist *focus;
  struct objlist *obj,*dobj=NULL,*aobj;
  char *inst,*inst2,*dinst,*dfield;
  int ret,section;
  int x1,y1;
  int aid=0,idx=0,idy=0,idu=0,idr=0,idg,j,lenx,leny;
  int findX,findY,findU,findR,findG;
  char *axisx,*axisy;
  int matchx,matchy;
  struct narray iarray,*sarray;
  int snum;
  char **sdata;
  GC dc;
  char type;
  char *group,*group2;
  int  axis;
  struct Viewer *d;

  if (menulock || globallock) return;
  d=&(NgraphApp.Viewer);
  dc=XCreateGC(Disp,d->win,0,0);
  ShowFocusFrame(dc);
  d->ShowFrame=FALSE;
  num=arraynum(d->focusobj);
  axis=FALSE;
  PaintLock=TRUE;
  for (i=num-1;i>=0;i--) {
    focus=*(struct focuslist **)arraynget(d->focusobj,i);
    if ((focus!=NULL) && ((inst=chkobjinstoid(focus->obj,focus->oid))!=NULL)) {
      obj=focus->obj;
      _getobj(obj,"id",inst,&id);
      ret=IDCANCEL;
      if (obj==chkobject("axis")) {
        axis=TRUE;
        _getobj(obj,"group",inst,&group);
        if ((group!=NULL) && (group[0]!='a')) {
          findX=findY=findU=findR=findG=FALSE;
          type=group[0];
          for (j=0;j<=id;j++) {
            inst2=chkobjinst(obj,j);
            _getobj(obj,"group",inst2,&group2);
            _getobj(obj,"id",inst2,&id2);
            if ((group2!=NULL) && (group2[0]==type)) {
              if (strcmp(group+2,group2+2)==0) {
                if (group2[1]=='X') {
                  findX=TRUE;
                  idx=id2;
                } else if (group2[1]=='Y') {
                  findY=TRUE;
                  idy=id2;
                } else if (group2[1]=='U') {
                  findU=TRUE;
                  idu=id2;
                } else if (group2[1]=='R') {
                  findR=TRUE;
                  idr=id2;
                }
              }
            }
          }
          if (((type=='s') || (type=='f')) && findX && findY
          && !_getobj(menulocal.obj,"_list",menulocal.inst,&sarray)
          && ((snum=arraynum(sarray))>=0)) {
            sdata=(char **)arraydata(sarray);
            for (j=1;j<snum;j++) {
              if (((dobj=getobjlist(sdata[j],&did,&dfield,NULL))!=NULL)
              && (dobj==chkobject("axisgrid"))) {
                if ((dinst=chkobjinstoid(dobj,did))!=NULL) {
                  _getobj(dobj,"axis_x",dinst,&axisx);
                  _getobj(dobj,"axis_y",dinst,&axisy);
                  matchx=matchy=FALSE;
                  if (axisx!=NULL) {
                    arrayinit(&iarray,sizeof(int));
                    if (!getobjilist(axisx,&aobj,&iarray,FALSE,NULL)) {
                      if ((arraynum(&iarray)>=1) && (obj==aobj)
                      && (*(int *)arraylast(&iarray)==idx)) matchx=TRUE;
                    }
                    arraydel(&iarray);
                  }
                  if (axisy!=NULL) {
                    arrayinit(&iarray,sizeof(int));
                    if (!getobjilist(axisy,&aobj,&iarray,FALSE,NULL)) {
                      if ((arraynum(&iarray)>=1) && (obj==aobj)
                      && (*(int *)arraylast(&iarray)==idy)) matchy=TRUE;
                    }
                    arraydel(&iarray);
                  }
                  if (matchx && matchy) {
                    findG=TRUE;
                    _getobj(dobj,"id",dinst,&idg);
                    break;
                  }
                }
              }
            }
          }
          if (((type=='s') || (type=='f')) 
          && findX && findY && findU && findR) {
            if (!findG) {
              dobj=chkobject("axisgrid");
              idg=-1;
            }
            if (type=='s') section=TRUE;
            else section=FALSE;
            getobj(obj,"y",idx,0,NULL,&y1);
            getobj(obj,"x",idy,0,NULL,&x1);
            getobj(obj,"y",idu,0,NULL,&leny);
            getobj(obj,"x",idr,0,NULL,&lenx);
            leny=y1-leny;
            lenx=lenx-x1;
            SectionDialog(&DlgSection,x1,y1,lenx,leny,obj,idx,
                          idy,idu,idr,dobj,&idg,section);
            ret=DialogExecute(TopLevel,&DlgSection);
            if (ret==IDDELETE) {
              AxisDel(id);
              arrayndel2(d->focusobj,i);
            }
            if (!findG) {
              if (idg!=-1) {
                if ((dinst=chkobjinst(dobj,idg))!=NULL) AddList(dobj,dinst);
              }
            }
            if (ret!=IDCANCEL) NgraphApp.Changed=TRUE;
          } else if ((type=='c') && findX && findY) {
            getobj(obj,"x",idx,0,NULL,&x1);
            getobj(obj,"y",idy,0,NULL,&y1);
            getobj(obj,"length",idx,0,NULL,&lenx);
            getobj(obj,"length",idy,0,NULL,&leny);
            CrossDialog(&DlgCross,x1,y1,lenx,leny,obj,idx,idy);
            ret=DialogExecute(TopLevel,&DlgCross);
            if (ret==IDDELETE) {
              AxisDel(aid);
              arrayndel2(d->focusobj,i);
            }
            if (ret!=IDCANCEL) NgraphApp.Changed=TRUE;
          }
        } else {
          AxisDialog(&DlgAxis,obj,id,TRUE);
          ret=DialogExecute(TopLevel,&DlgAxis);
          if (ret==IDDELETE) {
            AxisDel(id);
            arrayndel2(d->focusobj,i);
          }
          if (ret!=IDCANCEL) NgraphApp.Changed=TRUE;
        }
      } else {
        AddInvalidateRect(obj,inst);
        if (obj==chkobject("line")) {
          LegendArrowDialog(&DlgLegendArrow,obj,id);
          ret=DialogExecute(TopLevel,&DlgLegendArrow);
        } else if (obj==chkobject("curve")) {
          LegendCurveDialog(&DlgLegendCurve,obj,id);
          ret=DialogExecute(TopLevel,&DlgLegendCurve);
        } else if (obj==chkobject("polygon")) {
          LegendPolyDialog(&DlgLegendPoly,obj,id);
          ret=DialogExecute(TopLevel,&DlgLegendPoly);
        } else if (obj==chkobject("rectangle")) {
          LegendRectDialog(&DlgLegendRect,obj,id);
          ret=DialogExecute(TopLevel,&DlgLegendRect);
        } else if (obj==chkobject("arc")) {
          LegendArcDialog(&DlgLegendArc,obj,id);
          ret=DialogExecute(TopLevel,&DlgLegendArc);
        } else if (obj==chkobject("mark")) {
          LegendMarkDialog(&DlgLegendMark,obj,id);
          ret=DialogExecute(TopLevel,&DlgLegendMark);
        } else if (obj==chkobject("text")) {
          LegendTextDialog(&DlgLegendText,obj,id);
          ret=DialogExecute(TopLevel,&DlgLegendText);
        } else if (obj==chkobject("merge")) {
          MergeDialog(&DlgMerge,obj,id);
          ret=DialogExecute(TopLevel,&DlgMerge);
        }
        if (ret==IDDELETE) {
          delobj(obj,id);
        }
        if (ret==IDOK) AddInvalidateRect(obj,inst);
        if (ret!=IDCANCEL) NgraphApp.Changed=TRUE;
      }
    }
  }
  PaintLock=FALSE;
  if ((d->Mode==LegendB) || ((d->Mode==PointB) && (!axis))) d->allclear=FALSE;
  UpdateAll();
  ShowFocusFrame(dc);
  d->ShowFrame=TRUE;
  XFreeGC(Disp,dc);
}

void ViewDelete()
{
  int i,id,num;
  struct focuslist *focus;
  struct objlist *obj;
  char *inst;
  GC dc;
  int axis;
  struct Viewer *d;

  d=&(NgraphApp.Viewer);
  if ((d->MouseMode==MOUSENONE)
  && ((d->Mode==PointB) || (d->Mode==LegendB) || (d->Mode==AxisB))) {
    dc=XCreateGC(Disp,d->win,0,0);
    ShowFocusFrame(dc);
    d->ShowFrame=FALSE;
    axis=FALSE;
    PaintLock=TRUE;
    num=arraynum(d->focusobj);
    for (i=num-1;i>=0;i--) {
      focus=*(struct focuslist **)arraynget(d->focusobj,i);
      obj=focus->obj;
      if ((inst=chkobjinstoid(obj,focus->oid))!=NULL) {
        AddInvalidateRect(obj,inst);
        DelList(obj,inst);
        _getobj(obj,"id",inst,&id);
        if (obj==chkobject("axis")) {
          AxisDel(id);
          axis=TRUE;
        } else delobj(obj,id);
        NgraphApp.Changed=TRUE;
      }
    }
    PaintLock=FALSE;
    if ((d->Mode==LegendB) || ((d->Mode==PointB) && (!axis)))
      d->allclear=FALSE;
    if (num!=0) UpdateAll();
  }
}

void ViewTop()
{
  int id,num;
  struct focuslist *focus;
  struct objlist *obj;
  char *inst;
  struct Viewer *d;

  d=&(NgraphApp.Viewer);
  if ((d->MouseMode==MOUSENONE)
  && ((d->Mode==PointB) || (d->Mode==LegendB) || (d->Mode==AxisB))) {
    num=arraynum(d->focusobj);
    if (num==1) {
      focus=*(struct focuslist **)arraynget(d->focusobj,0);
      obj=focus->obj;
      if (chkobjchild(chkobject("legend"),obj)) {
        if ((inst=chkobjinstoid(obj,focus->oid))!=NULL) {
          DelList(obj,inst);
          _getobj(obj,"id",inst,&id);
          movetopobj(obj,id);
          AddList(obj,inst);
          NgraphApp.Changed=TRUE;
          d->allclear=TRUE;
          UpdateAll();
        }
      }
    }
  }
}

void ViewLast()
{
  int id,num;
  struct focuslist *focus;
  struct objlist *obj;
  char *inst;
  struct Viewer *d;

  d=&(NgraphApp.Viewer);
  if ((d->MouseMode==MOUSENONE)
  && ((d->Mode==PointB) || (d->Mode==LegendB) || (d->Mode==AxisB))) {
    num=arraynum(d->focusobj);
    if (num==1) {
      focus=*(struct focuslist **)arraynget(d->focusobj,0);
      obj=focus->obj;
      if (chkobjchild(chkobject("legend"),obj)) {
        if ((inst=chkobjinstoid(obj,focus->oid))!=NULL) {
          DelList(obj,inst);
          _getobj(obj,"id",inst,&id);
          movelastobj(obj,id);
          AddList(obj,inst);
          NgraphApp.Changed=TRUE;
          d->allclear=TRUE;
          UpdateAll();
         }
      }
    }
  }
}

void ncopyobj(struct objlist *obj,int id1,int id2)
{
  int j;
  char *field;
  int perm,type;

  for (j=0;j<chkobjfieldnum(obj);j++) {
    field=chkobjfieldname(obj,j);
    perm=chkobjperm(obj,field);
    type=chkobjfieldtype(obj,field);
    if (((perm&NREAD)!=0) && ((perm&NWRITE)!=0) && (type<NVFUNC))
      copyobj(obj,field,id1,id2);
  }
}

void ViewCopy()
{
  int i,j,id,id2,did,num;
  struct focuslist *focus;
  struct objlist *obj,*dobj,*aobj;
  char *inst,*inst2,*dinst,*dfield;
  GC dc;
  int findX,findY,findU,findR,findG;
  char *axisx,*axisy;
  int matchx,matchy;
  struct narray iarray,*sarray;
  int snum;
  char **sdata;
  int oidx,oidy;
  int idx=0,idy=0,idu=0,idr=0,idg;
  int idx2,idy2,idu2,idr2,idg2;
  char type;
  char *group,*group2;
  int tp;
  struct narray agroup;
  char *argv[2];
  int axis;
  struct Viewer *d;

  d=&(NgraphApp.Viewer);
  if ((d->MouseMode==MOUSENONE)
  && ((d->Mode==PointB) || (d->Mode==LegendB) || (d->Mode==AxisB))) {
    dc=XCreateGC(Disp,d->win,0,0);
    ShowFocusFrame(dc);
    d->ShowFrame=FALSE;
    axis=FALSE;
    PaintLock=TRUE;
    num=arraynum(d->focusobj);
    for (i=0;i<num;i++) {
      focus=*(struct focuslist **)arraynget(d->focusobj,i);
      if ((focus!=NULL)
      && ((inst=chkobjinstoid(focus->obj,focus->oid))!=NULL)) {
        obj=focus->obj;
        _getobj(obj,"id",inst,&id);
        if (obj==chkobject("axis")) {
          axis=TRUE;
          _getobj(obj,"group",inst,&group);
          if ((group!=NULL) && (group[0]!='a')) {
            findX=findY=findU=findR=findG=FALSE;
            type=group[0];
            for (j=0;j<=id;j++) {
              inst2=chkobjinst(obj,j);
              _getobj(obj,"group",inst2,&group2);
              _getobj(obj,"id",inst2,&id2);
              if ((group2!=NULL) && (group2[0]==type)) {
                if (strcmp(group+2,group2+2)==0) {
                  if (group2[1]=='X') {
                    findX=TRUE;
                    idx=id2;
                  } else if (group2[1]=='Y') {
                    findY=TRUE;
                    idy=id2;
                  } else if (group2[1]=='U') {
                    findU=TRUE;
                    idu=id2;
                  } else if (group2[1]=='R') {
                    findR=TRUE;
                    idr=id2;
                  }
                }
              }
            }
            if (((type=='s') || (type=='f')) && findX && findY
            && !_getobj(menulocal.obj,"_list",menulocal.inst,&sarray)
            && ((snum=arraynum(sarray))>=0)) {
              sdata=(char **)arraydata(sarray);
              for (j=1;j<snum;j++) {
                if (((dobj=getobjlist(sdata[j],&did,&dfield,NULL))!=NULL)
                && (dobj==chkobject("axisgrid"))) {
                  if ((dinst=chkobjinstoid(dobj,did))!=NULL) {
                    _getobj(dobj,"axis_x",dinst,&axisx);
                    _getobj(dobj,"axis_y",dinst,&axisy);
                    matchx=matchy=FALSE;
                    if (axisx!=NULL) {
                      arrayinit(&iarray,sizeof(int));
                      if (!getobjilist(axisx,&aobj,&iarray,FALSE,NULL)) {
                        if ((arraynum(&iarray)>=1) && (obj==aobj)
                        && (*(int *)arraylast(&iarray)==idx)) matchx=TRUE;
                      }
                      arraydel(&iarray);
                    }
                    if (axisy!=NULL) {
                      arrayinit(&iarray,sizeof(int));
                      if (!getobjilist(axisy,&aobj,&iarray,FALSE,NULL)) {
                        if ((arraynum(&iarray)>=1) && (obj==aobj)
                        && (*(int *)arraylast(&iarray)==idy)) matchy=TRUE;
                      }
                      arraydel(&iarray);
                    }
                    if (matchx && matchy) {
                      findG=TRUE;
                      _getobj(dobj,"id",dinst,&idg);
                      break;
                    }
                  }
                }
              }
            }
            if (((type=='s') || (type=='f')) 
            && findX && findY && findU && findR) {
              if ((idx2=newobj(obj))>=0) {
                ncopyobj(obj,idx2,idx);
                inst2=chkobjinst(obj,idx2);
                _getobj(obj,"oid",inst2,&oidx);
                AddList(obj,inst2);
                AddInvalidateRect(obj,inst2);
                NgraphApp.Changed=TRUE;
              } else {
                AddInvalidateRect(obj,inst);
              }
              if ((idy2=newobj(obj))>=0) {
                ncopyobj(obj,idy2,idy);
                inst2=chkobjinst(obj,idy2);
                _getobj(obj,"oid",inst2,&oidy);
                AddList(obj,inst2);
                AddInvalidateRect(obj,inst2);
                NgraphApp.Changed=TRUE;
              } else {
                AddInvalidateRect(obj,inst);
              }
              if ((idu2=newobj(obj))>=0) {
                ncopyobj(obj,idu2,idu);
                inst2=chkobjinst(obj,idu2);
                if ((idx2>=0) && ((axisx=(char *)memalloc(13))!=NULL)) {
                  sprintf(axisx,"axis:^%d",oidx);
                  putobj(obj,"reference",idu2,axisx);
                }
                AddList(obj,inst2);
                AddInvalidateRect(obj,inst2);
                NgraphApp.Changed=TRUE;
              } else {
                AddInvalidateRect(obj,inst);
              }
              if ((idr2=newobj(obj))>=0) {
                ncopyobj(obj,idr2,idr);
                inst2=chkobjinst(obj,idr2);
                if ((idy2>=0) && ((axisy=(char *)memalloc(13))!=NULL)) {
                  sprintf(axisy,"axis:^%d",oidy);
                  putobj(obj,"reference",idr2,axisy);
                }
                arrayinit(&agroup,sizeof(int));
                if (type=='f') tp=1;
                else tp=2;
                arrayadd(&agroup,&tp);
                arrayadd(&agroup,&idx2);
                arrayadd(&agroup,&idy2);
                arrayadd(&agroup,&idu2);
                arrayadd(&agroup,&idr2);
                argv[0]=(char *)&agroup;
                argv[1]=NULL;
                exeobj(obj,"grouping",idr2,1,argv);
                arraydel(&agroup);
                _getobj(obj,"oid",inst2,&(focus->oid));
                AddList(obj,inst2);
                AddInvalidateRect(obj,inst2);
                NgraphApp.Changed=TRUE;
              } else {
                AddInvalidateRect(obj,inst);
              }
              if (findG) {
                dobj=chkobject("axisgrid");
                if ((idg2=newobj(dobj))>=0) {
                  ncopyobj(dobj,idg2,idg);
                  inst2=chkobjinst(dobj,idg2);
                  if ((idx2>=0) && (idu2>=0) 
                  && ((axisx=(char *)memalloc(13))!=NULL)) {
                    sprintf(axisx,"axis:^%d",oidx);
                    putobj(dobj,"axis_x",idg2,axisx);
                  }
                  if ((idy2>=0) && (idr2>=0) 
                  && ((axisy=(char *)memalloc(13))!=NULL)) {
                    sprintf(axisy,"axis:^%d",oidy);
                    putobj(dobj,"axis_y",idg2,axisy);
                  }
                  AddList(dobj,inst2);
                  NgraphApp.Changed=TRUE;
                }
              }
            } else if ((type=='c') && findX && findY) {
              if ((idx2=newobj(obj))>=0) {
                ncopyobj(obj,idx2,idx);
                inst2=chkobjinst(obj,idx2);
                _getobj(obj,"oid",inst2,&oidx);
                AddList(obj,inst2);
                AddInvalidateRect(obj,inst2);
                NgraphApp.Changed=TRUE;
              } else {
                AddInvalidateRect(obj,inst);
              }
              if ((idy2=newobj(obj))>=0) {
                ncopyobj(obj,idy2,idy);
                inst2=chkobjinst(obj,idy2);
                _getobj(obj,"oid",inst2,&oidy);
                arrayinit(&agroup,sizeof(int));
                tp=3;
                arrayadd(&agroup,&tp);
                arrayadd(&agroup,&idx2);
                arrayadd(&agroup,&idy2);
                argv[0]=(char *)&agroup;
                argv[1]=NULL;
                exeobj(obj,"grouping",idy2,1,argv);
                arraydel(&agroup);
                focus->oid=oidy;
                AddList(obj,inst2);
                AddInvalidateRect(obj,inst2);
                NgraphApp.Changed=TRUE;
              } else {
                AddInvalidateRect(obj,inst);
              }
              if ((idx2>=0) && (idy2>=0)) {
                if ((axisy=(char *)memalloc(13))!=NULL) {
                  sprintf(axisy,"axis:^%d",oidy);
                  putobj(obj,"adjust_axis",idx2,axisy);
                }
                if ((axisx=(char *)memalloc(13))!=NULL) {
                  sprintf(axisx,"axis:^%d",oidx);
                  putobj(obj,"adjust_axis",idy2,axisx);
                }
              }
            }
          } else {
            if ((id2=newobj(obj))>=0) {
              ncopyobj(obj,id2,id);
              inst2=chkobjinst(obj,id2);
              _getobj(obj,"oid",inst2,&(focus->oid));
              AddList(obj,inst2);
              AddInvalidateRect(obj,inst2);
              NgraphApp.Changed=TRUE;
            } else {
              AddInvalidateRect(obj,inst);
            }
          }
        } else {
          if ((id2=newobj(obj))>=0) {
            ncopyobj(obj,id2,id);
            inst2=chkobjinst(obj,id2);
            _getobj(obj,"oid",inst2,&(focus->oid));
            AddList(obj,inst2);
            AddInvalidateRect(obj,inst2);
            NgraphApp.Changed=TRUE;
          } else {
            AddInvalidateRect(obj,inst);
          }
        }
      }
    }
    PaintLock=FALSE;
    if ((d->Mode==LegendB) || ((d->Mode==PointB) && (!axis)))
      d->allclear=FALSE;
    UpdateAll();
    ShowFocusFrame(dc);
    d->ShowFrame=TRUE;
    XFreeGC(Disp,dc);
  }
}

void ViewCross()
{
  GC dc;
  struct Viewer *d;

  d=&(NgraphApp.Viewer);
  dc=XCreateGC(Disp,d->win,0,0);
  if (d->ShowCross) {
    ShowCrossGauge(dc);
    d->ShowCross=FALSE;
  } else {
    ShowCrossGauge(dc);
    d->ShowCross=TRUE;
  }
  XFreeGC(Disp,dc);
}

void ViewerPopupMenu(Widget w,XtPointer client_data,XtPointer call_data)
{
  switch ((int )client_data) {
  case 0: 
    ViewUpdate();
    break;
  case 1: 
    ViewDelete();
    break;
  case 2: 
    ViewCopy();
    break;
  case 3: 
    ViewTop();
    break;
  case 4: 
    ViewLast();
    break;
  case 5: 
    ViewCross();
    break;
  }
}

void CmViewerButtonArm(Widget w,XtPointer client_data,XtPointer call_data)
{
  int i,Mode=PointB;
  struct Viewer *d;

  d=&(NgraphApp.Viewer);
  UnFocus();
  for(i=0;i<19;i++) {
    if (w==NgraphApp.viewb[i]) {
      Mode=NgraphApp.Viewer.Mode=i;
      XtVaSetValues(NgraphApp.viewb[i],XmNset,XmSET,NULL);
    } else XtVaSetValues(NgraphApp.viewb[i],XmNset,XmUNSET,NULL);
  }
  if ((Mode==PointB) || (Mode==LegendB) || (Mode==AxisB) || (Mode==TrimB)
   || (Mode==DataB) || (Mode==EvalB)) {
    SetCursor(XC_left_ptr);
  } else if (Mode==TextB) {
    SetCursor(XC_xterm);
  } else if (Mode==ZoomB) {
    SetCursor(XC_zoomin);
  } else {
    SetCursor(XC_crosshair);
  }
  NgraphApp.Viewer.Capture=FALSE;
  NgraphApp.Viewer.MouseMode=MOUSENONE;
}
